synthesizer 3.0.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/examples/formant_vocoder.rb +86 -0
- data/examples/formant_vocoder_sweep.rb +76 -0
- data/examples/lpf.rb +1 -2
- data/examples/mono.rb +3 -4
- data/examples/poly.rb +3 -4
- data/examples/step_editor.rb +1 -2
- data/lib/audio_stream/audio_input_metronome.rb +1 -2
- data/lib/synthesizer.rb +1 -1
- data/lib/synthesizer/mono_synth.rb +2 -4
- data/lib/synthesizer/oscillator.rb +4 -4
- data/lib/synthesizer/oscillator_source.rb +5 -0
- data/lib/synthesizer/oscillator_source/base.rb +36 -0
- data/lib/synthesizer/oscillator_source/basic.rb +13 -0
- data/lib/synthesizer/oscillator_source/formant_vocoder.rb +107 -0
- data/lib/synthesizer/oscillator_source/pulse.rb +23 -0
- data/lib/synthesizer/poly_synth.rb +2 -4
- data/lib/synthesizer/processor.rb +44 -6
- data/lib/synthesizer/shape.rb +8 -3
- data/lib/synthesizer/unison.rb +11 -18
- data/lib/synthesizer/version.rb +1 -1
- data/synthesizer.gemspec +1 -1
- metadata +12 -8
- data/lib/synthesizer/processor/high.rb +0 -91
- data/lib/synthesizer/processor/low.rb +0 -88
- data/lib/synthesizer/quality.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de75bd1dce21cbe4d6fb77b3f7dac7b35a5db994a1e7ee3d4109d660cbb69d68
|
4
|
+
data.tar.gz: 6d4f22b0ee34003ce64ab908f39ab9bfdd5df448c738eed516d2cb8312f2dbbe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df6f09c4951a5925bc77f6821c53327307f9aeb16d4e273dc440c034c261fb06a35abd0f6c9b19735b3a581267afc30d98e3a05f722829bac9f2aa99e0cd5f34
|
7
|
+
data.tar.gz: 17da6f4b246047632d921e15d5b0b6975cc6c4ecda29ce9b1cdbe9231ea1832bef9b3601a91a98fe044711d5eef5a37e67aa74be6ef3691bf102380efc646b46
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'synthesizer'
|
2
|
+
require 'audio_stream'
|
3
|
+
|
4
|
+
include AudioStream
|
5
|
+
include Synthesizer
|
6
|
+
|
7
|
+
soundinfo = SoundInfo.new(
|
8
|
+
channels: 2,
|
9
|
+
samplerate: 44100,
|
10
|
+
window_size: 1024,
|
11
|
+
format: RubyAudio::FORMAT_WAV|RubyAudio::FORMAT_PCM_16
|
12
|
+
)
|
13
|
+
|
14
|
+
source = nil
|
15
|
+
|
16
|
+
synth = PolySynth.new(
|
17
|
+
oscillators: [
|
18
|
+
Oscillator.new(
|
19
|
+
source: source = OscillatorSource::FormantVocoder.new(
|
20
|
+
vowels: [:a],
|
21
|
+
),
|
22
|
+
phase: 0.0,
|
23
|
+
),
|
24
|
+
],
|
25
|
+
amplifier: Amplifier.new(
|
26
|
+
volume: ModulationValue.new(1.0)
|
27
|
+
.add(Modulation::Adsr.new(
|
28
|
+
attack: 0.05,
|
29
|
+
hold: 0.1,
|
30
|
+
decay: 0.4,
|
31
|
+
sustain: 0.8,
|
32
|
+
release: 0.2
|
33
|
+
), depth: 1.0),
|
34
|
+
),
|
35
|
+
soundinfo: soundinfo,
|
36
|
+
)
|
37
|
+
bufs = []
|
38
|
+
base = 36
|
39
|
+
|
40
|
+
source.vowels = [:a]
|
41
|
+
synth.note_on(Note.new(base))
|
42
|
+
bufs += 50.times.map {|_| synth.next}
|
43
|
+
synth.note_off(Note.new(base))
|
44
|
+
bufs += 20.times.map {|_| synth.next}
|
45
|
+
|
46
|
+
source.vowels = [:i]
|
47
|
+
synth.note_on(Note.new(base))
|
48
|
+
bufs += 50.times.map {|_| synth.next}
|
49
|
+
synth.note_off(Note.new(base))
|
50
|
+
bufs += 20.times.map {|_| synth.next}
|
51
|
+
|
52
|
+
source.vowels = [:u]
|
53
|
+
synth.note_on(Note.new(base))
|
54
|
+
bufs += 50.times.map {|_| synth.next}
|
55
|
+
synth.note_off(Note.new(base))
|
56
|
+
bufs += 20.times.map {|_| synth.next}
|
57
|
+
|
58
|
+
source.vowels = [:e]
|
59
|
+
synth.note_on(Note.new(base))
|
60
|
+
bufs += 50.times.map {|_| synth.next}
|
61
|
+
synth.note_off(Note.new(base))
|
62
|
+
bufs += 20.times.map {|_| synth.next}
|
63
|
+
|
64
|
+
source.vowels = [:o]
|
65
|
+
synth.note_on(Note.new(base))
|
66
|
+
bufs += 50.times.map {|_| synth.next}
|
67
|
+
synth.note_off(Note.new(base))
|
68
|
+
|
69
|
+
bufs += 50.times.map {|_| synth.next}
|
70
|
+
|
71
|
+
|
72
|
+
track1 = AudioInput.buffer(bufs)
|
73
|
+
|
74
|
+
stereo_out = AudioOutput.device(soundinfo: soundinfo)
|
75
|
+
#stereo_out = AudioOutput.file("formatvocoder.wav", soundinfo: soundinfo)
|
76
|
+
|
77
|
+
track1
|
78
|
+
.send_to(stereo_out, gain: 1.0)
|
79
|
+
|
80
|
+
|
81
|
+
conductor = Conductor.new(
|
82
|
+
input: [track1],
|
83
|
+
output: [stereo_out]
|
84
|
+
)
|
85
|
+
conductor.connect
|
86
|
+
conductor.join
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'synthesizer'
|
2
|
+
require 'audio_stream'
|
3
|
+
|
4
|
+
include AudioStream
|
5
|
+
include Synthesizer
|
6
|
+
|
7
|
+
soundinfo = SoundInfo.new(
|
8
|
+
channels: 2,
|
9
|
+
samplerate: 44100,
|
10
|
+
window_size: 1024,
|
11
|
+
format: RubyAudio::FORMAT_WAV|RubyAudio::FORMAT_PCM_16
|
12
|
+
)
|
13
|
+
|
14
|
+
synth = PolySynth.new(
|
15
|
+
oscillators: [
|
16
|
+
Oscillator.new(
|
17
|
+
source: OscillatorSource::FormantVocoder.new(
|
18
|
+
vowels: [:a, :i, :u, :e, :o],
|
19
|
+
#vowels: [:i, :e, :a, :o, :u],
|
20
|
+
pronunciation: ModulationValue.new(0)
|
21
|
+
.add(Modulation::Lfo.new(
|
22
|
+
shape: Shape::ForeverRampUp,
|
23
|
+
rate: 2.0,
|
24
|
+
)),
|
25
|
+
),
|
26
|
+
phase: 0.0,
|
27
|
+
),
|
28
|
+
],
|
29
|
+
amplifier: Amplifier.new(
|
30
|
+
volume: ModulationValue.new(1.0)
|
31
|
+
.add(Modulation::Adsr.new(
|
32
|
+
attack: 0.05,
|
33
|
+
hold: 0.1,
|
34
|
+
decay: 0.4,
|
35
|
+
sustain: 0.8,
|
36
|
+
release: 0.2
|
37
|
+
), depth: 1.0),
|
38
|
+
),
|
39
|
+
soundinfo: soundinfo,
|
40
|
+
)
|
41
|
+
bufs = []
|
42
|
+
|
43
|
+
base = 36
|
44
|
+
synth.note_on(Note.new(base))
|
45
|
+
bufs += 100.times.map {|_| synth.next}
|
46
|
+
synth.note_off(Note.new(base))
|
47
|
+
bufs += 20.times.map {|_| synth.next}
|
48
|
+
|
49
|
+
synth.note_on(Note.new(base+4))
|
50
|
+
bufs += 100.times.map {|_| synth.next}
|
51
|
+
synth.note_off(Note.new(base+4))
|
52
|
+
bufs += 20.times.map {|_| synth.next}
|
53
|
+
|
54
|
+
synth.note_on(Note.new(base+8))
|
55
|
+
bufs += 100.times.map {|_| synth.next}
|
56
|
+
synth.note_off(Note.new(base+8))
|
57
|
+
bufs += 20.times.map {|_| synth.next}
|
58
|
+
|
59
|
+
bufs += 50.times.map {|_| synth.next}
|
60
|
+
|
61
|
+
|
62
|
+
track1 = AudioInput.buffer(bufs)
|
63
|
+
|
64
|
+
stereo_out = AudioOutput.device(soundinfo: soundinfo)
|
65
|
+
#stereo_out = AudioOutput.file("formatvocoder_sweep.wav", soundinfo: soundinfo)
|
66
|
+
|
67
|
+
track1
|
68
|
+
.send_to(stereo_out, gain: 1.0)
|
69
|
+
|
70
|
+
|
71
|
+
conductor = Conductor.new(
|
72
|
+
input: [track1],
|
73
|
+
output: [stereo_out]
|
74
|
+
)
|
75
|
+
conductor.connect
|
76
|
+
conductor.join
|
data/examples/lpf.rb
CHANGED
@@ -14,7 +14,7 @@ soundinfo = SoundInfo.new(
|
|
14
14
|
synth = PolySynth.new(
|
15
15
|
oscillators: [
|
16
16
|
Oscillator.new(
|
17
|
-
|
17
|
+
source: OscillatorSource::WhiteNoise.instance
|
18
18
|
),
|
19
19
|
],
|
20
20
|
filter: Filter::Serial.new(
|
@@ -49,7 +49,6 @@ synth = PolySynth.new(
|
|
49
49
|
release: 0.2
|
50
50
|
), depth: 1.0),
|
51
51
|
),
|
52
|
-
quality: Quality::LOW,
|
53
52
|
soundinfo: soundinfo,
|
54
53
|
)
|
55
54
|
bufs = []
|
data/examples/mono.rb
CHANGED
@@ -14,20 +14,20 @@ soundinfo = SoundInfo.new(
|
|
14
14
|
synth = MonoSynth.new(
|
15
15
|
oscillators: [
|
16
16
|
Oscillator.new(
|
17
|
-
|
17
|
+
source: OscillatorSource::SquareSawtooth.instance,
|
18
18
|
uni_num: ModulationValue.new(4),
|
19
19
|
#.add(Modulation::Lfo.new(
|
20
20
|
#)),
|
21
21
|
uni_detune: 0.1,
|
22
22
|
),
|
23
23
|
#Oscillator.new(
|
24
|
-
#
|
24
|
+
# source: OscillatorSource::SquareSawtooth.instance,
|
25
25
|
# tune_cents: 0.1,
|
26
26
|
# uni_num: 4,
|
27
27
|
# uni_detune: 0.1,
|
28
28
|
#),
|
29
29
|
#Oscillator.new(
|
30
|
-
#
|
30
|
+
# source: OscillatorSource::SquareSawtooth.instance,
|
31
31
|
# tune_semis: -12,
|
32
32
|
# uni_num: 4,
|
33
33
|
# uni_detune: 0.1,
|
@@ -44,7 +44,6 @@ synth = MonoSynth.new(
|
|
44
44
|
), depth: 1.0),
|
45
45
|
),
|
46
46
|
glide: 0.2,
|
47
|
-
quality: Quality::LOW,
|
48
47
|
soundinfo: soundinfo,
|
49
48
|
)
|
50
49
|
bufs = []
|
data/examples/poly.rb
CHANGED
@@ -14,20 +14,20 @@ soundinfo = SoundInfo.new(
|
|
14
14
|
synth = PolySynth.new(
|
15
15
|
oscillators: [
|
16
16
|
Oscillator.new(
|
17
|
-
|
17
|
+
source: OscillatorSource::SquareSawtooth.instance,
|
18
18
|
uni_num: ModulationValue.new(4)
|
19
19
|
.add(Modulation::Lfo.new(
|
20
20
|
)),
|
21
21
|
uni_detune: 0.1,
|
22
22
|
),
|
23
23
|
#Oscillator.new(
|
24
|
-
#
|
24
|
+
# source: OscillatorSource::SquareSawtooth.instance,
|
25
25
|
# tune_cents: 0.1,
|
26
26
|
# uni_num: 4,
|
27
27
|
# uni_detune: 0.1,
|
28
28
|
#),
|
29
29
|
#Oscillator.new(
|
30
|
-
#
|
30
|
+
# source: OscillatorSource::SquareSawtooth.instance,
|
31
31
|
# tune_semis: -12,
|
32
32
|
# uni_num: 4,
|
33
33
|
# uni_detune: 0.1,
|
@@ -43,7 +43,6 @@ synth = PolySynth.new(
|
|
43
43
|
release: 0.2
|
44
44
|
), depth: 1.0),
|
45
45
|
),
|
46
|
-
quality: Quality::LOW,
|
47
46
|
soundinfo: soundinfo,
|
48
47
|
)
|
49
48
|
bufs = []
|
data/examples/step_editor.rb
CHANGED
@@ -14,7 +14,7 @@ soundinfo = SoundInfo.new(
|
|
14
14
|
synth = PolySynth.new(
|
15
15
|
oscillators: [
|
16
16
|
Oscillator.new(
|
17
|
-
|
17
|
+
source: OscillatorSource::SquareSawtooth.instance,
|
18
18
|
uni_num: 4,
|
19
19
|
uni_detune: 0.1,
|
20
20
|
),
|
@@ -29,7 +29,6 @@ synth = PolySynth.new(
|
|
29
29
|
release: 0.2
|
30
30
|
), depth: 1.0),
|
31
31
|
),
|
32
|
-
quality: Quality::LOW,
|
33
32
|
soundinfo: soundinfo,
|
34
33
|
)
|
35
34
|
|
@@ -11,7 +11,7 @@ module AudioStream
|
|
11
11
|
|
12
12
|
@synth = Synthesizer::PolySynth.new(
|
13
13
|
oscillators: Synthesizer::Oscillator.new(
|
14
|
-
|
14
|
+
source: Synthesizer::OscillatorSource::Sine,
|
15
15
|
phase: 0,
|
16
16
|
),
|
17
17
|
amplifier: Synthesizer::Amplifier.new(
|
@@ -24,7 +24,6 @@ module AudioStream
|
|
24
24
|
release: 0.0
|
25
25
|
), depth: 1.0),
|
26
26
|
),
|
27
|
-
quality: Synthesizer::Quality::LOW,
|
28
27
|
soundinfo: soundinfo,
|
29
28
|
)
|
30
29
|
|
data/lib/synthesizer.rb
CHANGED
@@ -9,6 +9,7 @@ require 'synthesizer/step_editor'
|
|
9
9
|
require 'synthesizer/poly_synth'
|
10
10
|
require 'synthesizer/mono_synth'
|
11
11
|
require 'synthesizer/oscillator'
|
12
|
+
require 'synthesizer/oscillator_source'
|
12
13
|
require 'synthesizer/filter'
|
13
14
|
require 'synthesizer/amplifier'
|
14
15
|
require 'synthesizer/shape'
|
@@ -19,7 +20,6 @@ require 'synthesizer/note'
|
|
19
20
|
require 'synthesizer/note_perform'
|
20
21
|
require 'synthesizer/unison'
|
21
22
|
require 'synthesizer/processor'
|
22
|
-
require 'synthesizer/quality'
|
23
23
|
require 'synthesizer/utils'
|
24
24
|
|
25
25
|
module Synthesizer
|
@@ -6,7 +6,6 @@ module Synthesizer
|
|
6
6
|
attr_reader :amplifier
|
7
7
|
attr_reader :processor
|
8
8
|
|
9
|
-
attr_reader :quality
|
10
9
|
attr_reader :soundinfo
|
11
10
|
|
12
11
|
attr_reader :glide
|
@@ -15,15 +14,14 @@ module Synthesizer
|
|
15
14
|
# @param oscillators [Synthesizer::Oscillator] oscillator
|
16
15
|
# @param amplifier [Synthesizer::Amplifier] amplifier
|
17
16
|
# @param soundinfo [AudioStream::SoundInfo]
|
18
|
-
def initialize(oscillators:, filter: nil, amplifier:, glide: 0.1,
|
17
|
+
def initialize(oscillators:, filter: nil, amplifier:, glide: 0.1, soundinfo:)
|
19
18
|
@oscillators = [oscillators].flatten.compact
|
20
19
|
@filter = filter
|
21
20
|
@amplifier = amplifier
|
22
21
|
|
23
|
-
@quality = quality
|
24
22
|
@soundinfo = soundinfo
|
25
23
|
|
26
|
-
@processor = Processor.
|
24
|
+
@processor = Processor.new
|
27
25
|
@note_nums = []
|
28
26
|
@perform = nil
|
29
27
|
@glide = Modulation::Glide.new(time: glide)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Synthesizer
|
2
2
|
class Oscillator
|
3
3
|
|
4
|
-
attr_reader :
|
4
|
+
attr_reader :source
|
5
5
|
attr_reader :volume
|
6
6
|
attr_reader :pan
|
7
7
|
attr_reader :tune_semis
|
@@ -12,7 +12,7 @@ module Synthesizer
|
|
12
12
|
attr_reader :uni_num
|
13
13
|
attr_reader :uni_detune
|
14
14
|
|
15
|
-
# @param
|
15
|
+
# @param source [Synthesizer::OscillatorSource] oscillator waveform source
|
16
16
|
# @param volume [Float] oscillator volume. mute=0.0 max=1.0
|
17
17
|
# @param pan [Float] oscillator pan. left=-1.0 center=0.0 right=1.0 (-1.0~1.0)
|
18
18
|
# @param tune_semis [Integer] oscillator pitch semitone
|
@@ -22,8 +22,8 @@ module Synthesizer
|
|
22
22
|
# @param sync [Integer] TODO not implemented
|
23
23
|
# @param uni_num [Float] oscillator voicing number (1.0~16.0)
|
24
24
|
# @param uni_detune [Float] oscillator voicing detune percent. 0.01=1cent 1.0=semitone (0.0~1.0)
|
25
|
-
def initialize(
|
26
|
-
@
|
25
|
+
def initialize(source: OscillatorSource::Sine, 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)
|
26
|
+
@source = source
|
27
27
|
|
28
28
|
@volume = ModulationValue.create(volume)
|
29
29
|
@pan = ModulationValue.create(pan)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Synthesizer
|
2
|
+
module OscillatorSource
|
3
|
+
class Base
|
4
|
+
def initialize
|
5
|
+
end
|
6
|
+
|
7
|
+
def next(context, delta, l_gain, r_gain)
|
8
|
+
channels = context.channels
|
9
|
+
window_size = context.window_size
|
10
|
+
pos = context.pos
|
11
|
+
|
12
|
+
dst = window_size.times.map {|i|
|
13
|
+
sample(context, pos.next(delta))
|
14
|
+
}
|
15
|
+
dst = Vdsp::DoubleArray.create(dst)
|
16
|
+
|
17
|
+
case channels
|
18
|
+
when 1
|
19
|
+
Buffer.new(dst * l_gain)
|
20
|
+
when 2
|
21
|
+
Buffer.new(dst * l_gain, dst * r_gain)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def sample(context, phase)
|
26
|
+
raise Error, "not implemented abstruct method: #{self.class.name}.sample(context, phase)"
|
27
|
+
end
|
28
|
+
|
29
|
+
def generate_context(soundinfo, note_perform, phase)
|
30
|
+
Context.new(soundinfo.window_size, soundinfo.channels, ShapePos.new(phase: phase))
|
31
|
+
end
|
32
|
+
|
33
|
+
Context = Struct.new("Context", :window_size, :channels, :pos)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Synthesizer
|
2
|
+
module OscillatorSource
|
3
|
+
class FormantVocoder
|
4
|
+
attr_reader :vowels
|
5
|
+
attr_reader :pronunciation
|
6
|
+
|
7
|
+
@@f = {
|
8
|
+
i: [110.0, 250.0, 2100.0, 2900.0, 3700.0],
|
9
|
+
e: [160.0, 450.0, 1900.0, 2650.0, 3800.0],
|
10
|
+
a: [110.0, 700.0, 1250.0, 2500.0, 3900.0],
|
11
|
+
o: [110.0, 500.0, 1050.0, 2700.0, 3700.0],
|
12
|
+
u: [110.0, 330.0, 1500.0, 2400.0, 3650.0],
|
13
|
+
x: [110.0, 335.0, 1550.0, 2450.0, 3800.0],
|
14
|
+
}
|
15
|
+
@@gain = [50.0, 70.0, 110.0, 200.0, 200.0].map {|x| Math::PI * x}
|
16
|
+
|
17
|
+
def initialize(vowels: [:i, :e, :a, :o, :u], pronunciation: 0)
|
18
|
+
self.vowels = vowels
|
19
|
+
self.pronunciation = pronunciation
|
20
|
+
@pulse = Pulse.instance
|
21
|
+
end
|
22
|
+
|
23
|
+
def vowels=(vowels)
|
24
|
+
vowels = vowels + [vowels[0]]
|
25
|
+
@vowels = vowels.map {|v| @@f[v]}
|
26
|
+
@vowels_len = @vowels.length
|
27
|
+
end
|
28
|
+
|
29
|
+
def pronunciation=(pronunciation)
|
30
|
+
@pronunciation = ModulationValue.create(pronunciation)
|
31
|
+
end
|
32
|
+
|
33
|
+
def next(context, delta, l_gain, r_gain)
|
34
|
+
channels = context.channels
|
35
|
+
window_size = context.window_size
|
36
|
+
samplerate = context.samplerate
|
37
|
+
tmpbufs = context.tmpbufs
|
38
|
+
pronunciation_mod = context.pronunciation_mod
|
39
|
+
|
40
|
+
@pulse_context ||= @pulse.generate_context(context.soundinfo, context.note_perform, context.phase)
|
41
|
+
pulse = @pulse.next(@pulse_context, delta, 0.5, 0.5).streams[0]
|
42
|
+
|
43
|
+
r_index = pronunciation_mod[]
|
44
|
+
index = r_index.to_i
|
45
|
+
|
46
|
+
dst = 5.times.map {|i|
|
47
|
+
#dst = (1...5).each.map {|i|
|
48
|
+
tmpbuf = tmpbufs[i]
|
49
|
+
freq = @vowels[index % @vowels_len][i]+(@vowels[(index+1) % @vowels_len][i]-@vowels[index % @vowels_len][i])*(r_index-index)
|
50
|
+
w = Math::PI * 2 * freq
|
51
|
+
resfil(pulse, tmpbufs[i], @@gain[i], w, 1.0/samplerate, window_size)
|
52
|
+
}.inject(:+)
|
53
|
+
|
54
|
+
case channels
|
55
|
+
when 1
|
56
|
+
Buffer.new(dst * l_gain)
|
57
|
+
when 2
|
58
|
+
Buffer.new(dst * l_gain, dst * r_gain)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def resfil(x, v, a, w, dt, len)
|
63
|
+
b = 2.0 * (Math::E ** (-a*dt)) * Math.cos(w*dt)
|
64
|
+
c = Math::E ** (-2.0 * a * dt)
|
65
|
+
d = ((a*a+w*w)/w) * (Math::E ** (-a*dt)) * Math.sin(w*dt)
|
66
|
+
|
67
|
+
#v[0] = v[len-2]
|
68
|
+
#v[1] = v[len-1]
|
69
|
+
#v[0] += 1.0
|
70
|
+
|
71
|
+
#Vdsp::UnsafeDouble.vsmsma(v, 1, 1, b, v, 0, 1, -c, v, 2, 1, len)
|
72
|
+
#v * (d / 25000.to_f)
|
73
|
+
|
74
|
+
v[0] = v[len-2]
|
75
|
+
v[1] = v[len-1]
|
76
|
+
|
77
|
+
y = len.times.map {|i|
|
78
|
+
v[i+2] = b * v[i+1] - c * v[i] + x[i]
|
79
|
+
v[i+1] * (d / 25000.to_f)
|
80
|
+
}
|
81
|
+
|
82
|
+
Vdsp::DoubleArray.create(y)
|
83
|
+
end
|
84
|
+
|
85
|
+
def generate_context(soundinfo, note_perform, phase)
|
86
|
+
pronunciation_mod = ModulationValue.balance_generator(note_perform, soundinfo.framerate, @pronunciation)
|
87
|
+
tmpbufs = Array.new(5) {|i| Vdsp::DoubleArray.new(soundinfo.window_size+2)}
|
88
|
+
|
89
|
+
FvContext.new(soundinfo, note_perform, phase, tmpbufs, pronunciation_mod)
|
90
|
+
end
|
91
|
+
|
92
|
+
FvContext = Struct.new("FvContext", :soundinfo, :note_perform, :phase, :tmpbufs, :pronunciation_mod) do
|
93
|
+
def window_size
|
94
|
+
soundinfo.window_size
|
95
|
+
end
|
96
|
+
|
97
|
+
def channels
|
98
|
+
soundinfo.channels
|
99
|
+
end
|
100
|
+
|
101
|
+
def samplerate
|
102
|
+
soundinfo.samplerate
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Synthesizer
|
2
|
+
module OscillatorSource
|
3
|
+
class Pulse < Square
|
4
|
+
include Singleton
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
end
|
8
|
+
|
9
|
+
def sample(context, phase)
|
10
|
+
val = super(context, phase)
|
11
|
+
result = context.prev<0.0 && 0.0<val ? 1.0 : 0.0
|
12
|
+
context.prev = val
|
13
|
+
result
|
14
|
+
end
|
15
|
+
|
16
|
+
def generate_context(soundinfo, note_perform, phase)
|
17
|
+
PulseContext.new(soundinfo.window_size, soundinfo.channels, phase, ShapePos.new(phase: phase), -1.0)
|
18
|
+
end
|
19
|
+
|
20
|
+
PulseContext = Struct.new("PulseContext", :window_size, :channels, :phase, :pos, :prev)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -6,7 +6,6 @@ module Synthesizer
|
|
6
6
|
attr_reader :amplifier
|
7
7
|
attr_reader :processor
|
8
8
|
|
9
|
-
attr_reader :quality
|
10
9
|
attr_reader :soundinfo
|
11
10
|
|
12
11
|
attr_reader :glide
|
@@ -16,15 +15,14 @@ module Synthesizer
|
|
16
15
|
# @param filter [Synthesizer::Filter] filter
|
17
16
|
# @param amplifier [Synthesizer::Amplifier] amplifier
|
18
17
|
# @param soundinfo [AudioStream::SoundInfo]
|
19
|
-
def initialize(oscillators:, filter: nil, amplifier:,
|
18
|
+
def initialize(oscillators:, filter: nil, amplifier:, soundinfo:)
|
20
19
|
@oscillators = [oscillators].flatten.compact
|
21
20
|
@filter = filter
|
22
21
|
@amplifier = amplifier
|
23
22
|
|
24
|
-
@quality = quality
|
25
23
|
@soundinfo = soundinfo
|
26
24
|
|
27
|
-
@processor = Processor.
|
25
|
+
@processor = Processor.new
|
28
26
|
@performs = {}
|
29
27
|
@pitch_bend = 0.0
|
30
28
|
end
|
@@ -1,10 +1,48 @@
|
|
1
|
-
require 'synthesizer/processor/low'
|
2
|
-
require 'synthesizer/processor/high'
|
3
|
-
|
4
1
|
module Synthesizer
|
5
|
-
|
6
|
-
def
|
7
|
-
|
2
|
+
class Processor
|
3
|
+
def generator(osc, note_perform)
|
4
|
+
synth = note_perform.synth
|
5
|
+
filter = synth.filter
|
6
|
+
amp = synth.amplifier
|
7
|
+
window_size = synth.soundinfo.window_size
|
8
|
+
framerate = synth.soundinfo.samplerate.to_f / window_size
|
9
|
+
|
10
|
+
# Oscillator, Amplifier
|
11
|
+
volume_mod = ModulationValue.amp_generator(note_perform, framerate, osc.volume, amp.volume)
|
12
|
+
pan_mod = ModulationValue.balance_generator(note_perform, framerate, osc.pan, amp.pan)
|
13
|
+
tune_semis_mod = ModulationValue.balance_generator(note_perform, framerate, osc.tune_semis, amp.tune_semis, synth.glide&.to_modval)
|
14
|
+
tune_cents_mod = ModulationValue.balance_generator(note_perform, framerate, osc.tune_cents, amp.tune_cents)
|
15
|
+
|
16
|
+
uni_num_mod = ModulationValue.balance_generator(note_perform, framerate, osc.uni_num, amp.uni_num, center: 1.0)
|
17
|
+
uni_detune_mod = ModulationValue.balance_generator(note_perform, framerate, osc.uni_detune, amp.uni_detune)
|
18
|
+
unison = Unison.new(note_perform, osc.source, osc.phase)
|
19
|
+
|
20
|
+
# Filter
|
21
|
+
filter_mod = nil
|
22
|
+
if filter
|
23
|
+
filter_mod = filter.generator(note_perform, framerate)
|
24
|
+
end
|
25
|
+
|
26
|
+
-> {
|
27
|
+
# Oscillator, Amplifier
|
28
|
+
volume = volume_mod[] * note_perform.velocity
|
29
|
+
pan = pan_mod[]
|
30
|
+
tune_semis = tune_semis_mod[] + synth.pitch_bend
|
31
|
+
tune_cents = tune_cents_mod[]
|
32
|
+
|
33
|
+
uni_num = uni_num_mod[]
|
34
|
+
uni_detune = uni_detune_mod[]
|
35
|
+
|
36
|
+
buf = unison.next(uni_num, uni_detune, volume, pan, tune_semis, tune_cents)
|
37
|
+
|
38
|
+
# Filter
|
39
|
+
if filter_mod
|
40
|
+
filter_fx = filter_mod[]
|
41
|
+
buf = filter_fx.process(buf)
|
42
|
+
end
|
43
|
+
|
44
|
+
buf
|
45
|
+
}
|
8
46
|
end
|
9
47
|
end
|
10
48
|
end
|
data/lib/synthesizer/shape.rb
CHANGED
@@ -15,8 +15,14 @@ module Synthesizer
|
|
15
15
|
WhiteNoise = ->(phase) { Random.rand(-1.0...1.0) }
|
16
16
|
|
17
17
|
# LFO
|
18
|
-
RampUp = ->(phase) { (phase % 1) * 2 - 1 }
|
19
|
-
RampDown = ->(phase) { (phase % 1) * -2 + 1 }
|
18
|
+
RampUp = ->(phase) { (phase % 1.0) * 2 - 1 }
|
19
|
+
RampDown = ->(phase) { (phase % 1.0) * -2 + 1 }
|
20
|
+
|
21
|
+
PositiveRampUp = ->(phase) { phase % 1.0}
|
22
|
+
PositiveRampDown = ->(phase) { (phase % 1.0) * -1 + 1}
|
23
|
+
|
24
|
+
ForeverRampUp = ->(phase) { phase}
|
25
|
+
ForeverRampDown = ->(phase) { -phase}
|
20
26
|
|
21
27
|
# Complex
|
22
28
|
self.constants.tap {|consts|
|
@@ -30,6 +36,5 @@ module Synthesizer
|
|
30
36
|
}
|
31
37
|
}
|
32
38
|
}
|
33
|
-
|
34
39
|
end
|
35
40
|
end
|
data/lib/synthesizer/unison.rb
CHANGED
@@ -2,15 +2,15 @@ module Synthesizer
|
|
2
2
|
class Unison
|
3
3
|
UNI_NUM_MAX = 16
|
4
4
|
|
5
|
-
def initialize(note_perform,
|
6
|
-
@note_perform = note_perform
|
7
|
-
@shape = shape
|
8
|
-
@poss = UNI_NUM_MAX.times.map {|i|
|
9
|
-
ShapePos.new(phase: phase.value)
|
10
|
-
}
|
11
|
-
|
5
|
+
def initialize(note_perform, source, phase)
|
12
6
|
synth = note_perform.synth
|
13
7
|
@samplerate = synth.soundinfo.samplerate
|
8
|
+
|
9
|
+
@note_perform = note_perform
|
10
|
+
@source = source
|
11
|
+
@source_contexts = UNI_NUM_MAX.times.map {|i|
|
12
|
+
source.generate_context(synth.soundinfo, note_perform, phase.value)
|
13
|
+
}
|
14
14
|
end
|
15
15
|
|
16
16
|
def next(uni_num, uni_detune, volume, pan, tune_semis, tune_cents)
|
@@ -20,11 +20,8 @@ module Synthesizer
|
|
20
20
|
uni_num = UNI_NUM_MAX
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
uni_num.ceil.times {|i|
|
27
|
-
pos = @poss[i]
|
23
|
+
uni_num.ceil.times.map {|i|
|
24
|
+
context = @source_contexts[i]
|
28
25
|
|
29
26
|
uni_volume = 1.0
|
30
27
|
if uni_num<i
|
@@ -40,12 +37,8 @@ module Synthesizer
|
|
40
37
|
hz = @note_perform.note.hz(semis: tune_semis, cents: tune_cents + detune_cents)
|
41
38
|
delta = hz / @samplerate
|
42
39
|
|
43
|
-
|
44
|
-
|
45
|
-
val_r += val * r_gain
|
46
|
-
}
|
47
|
-
|
48
|
-
[val_l * volume / uni_num, val_r * volume / uni_num]
|
40
|
+
@source.next(context, delta, l_gain * volume * uni_volume / uni_num, r_gain * volume * uni_volume / uni_num)
|
41
|
+
}.inject(:+)
|
49
42
|
end
|
50
43
|
end
|
51
44
|
end
|
data/lib/synthesizer/version.rb
CHANGED
data/synthesizer.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: synthesizer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yoshida Tetsuya
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-11-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 3.
|
61
|
+
version: 3.2.0
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 3.
|
68
|
+
version: 3.2.0
|
69
69
|
description: Synthesizer implemented in Ruby.
|
70
70
|
email:
|
71
71
|
- yoshida.eth0@gmail.com
|
@@ -83,6 +83,8 @@ files:
|
|
83
83
|
- bin/setup
|
84
84
|
- examples/adsr.ipynb
|
85
85
|
- examples/curves.ipynb
|
86
|
+
- examples/formant_vocoder.rb
|
87
|
+
- examples/formant_vocoder_sweep.rb
|
86
88
|
- examples/lpf.rb
|
87
89
|
- examples/metronome.rb
|
88
90
|
- examples/mono.rb
|
@@ -113,11 +115,13 @@ files:
|
|
113
115
|
- lib/synthesizer/note.rb
|
114
116
|
- lib/synthesizer/note_perform.rb
|
115
117
|
- lib/synthesizer/oscillator.rb
|
118
|
+
- lib/synthesizer/oscillator_source.rb
|
119
|
+
- lib/synthesizer/oscillator_source/base.rb
|
120
|
+
- lib/synthesizer/oscillator_source/basic.rb
|
121
|
+
- lib/synthesizer/oscillator_source/formant_vocoder.rb
|
122
|
+
- lib/synthesizer/oscillator_source/pulse.rb
|
116
123
|
- lib/synthesizer/poly_synth.rb
|
117
124
|
- lib/synthesizer/processor.rb
|
118
|
-
- lib/synthesizer/processor/high.rb
|
119
|
-
- lib/synthesizer/processor/low.rb
|
120
|
-
- lib/synthesizer/quality.rb
|
121
125
|
- lib/synthesizer/shape.rb
|
122
126
|
- lib/synthesizer/shape_pos.rb
|
123
127
|
- lib/synthesizer/step_editor.rb
|
@@ -148,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
148
152
|
- !ruby/object:Gem::Version
|
149
153
|
version: '0'
|
150
154
|
requirements: []
|
151
|
-
rubygems_version: 3.0.
|
155
|
+
rubygems_version: 3.0.3
|
152
156
|
signing_key:
|
153
157
|
specification_version: 4
|
154
158
|
summary: Synthesizer implemented in Ruby.
|
@@ -1,91 +0,0 @@
|
|
1
|
-
module Synthesizer
|
2
|
-
module Processor
|
3
|
-
class High
|
4
|
-
def generator(osc, note_perform)
|
5
|
-
synth = note_perform.synth
|
6
|
-
filter = synth.filter
|
7
|
-
amp = synth.amplifier
|
8
|
-
channels = synth.soundinfo.channels
|
9
|
-
window_size = synth.soundinfo.window_size
|
10
|
-
framerate = synth.soundinfo.samplerate.to_f
|
11
|
-
|
12
|
-
# Oscillator, Amplifier
|
13
|
-
volume_mod = ModulationValue.amp_generator(note_perform, framerate, osc.volume, amp.volume)
|
14
|
-
pan_mod = ModulationValue.balance_generator(note_perform, framerate, osc.pan, amp.pan)
|
15
|
-
tune_semis_mod = ModulationValue.balance_generator(note_perform, framerate, osc.tune_semis, amp.tune_semis, synth.glide&.to_modval)
|
16
|
-
tune_cents_mod = ModulationValue.balance_generator(note_perform, framerate, osc.tune_cents, amp.tune_cents)
|
17
|
-
|
18
|
-
uni_num_mod = ModulationValue.balance_generator(note_perform, framerate, osc.uni_num, amp.uni_num, center: 1.0)
|
19
|
-
uni_detune_mod = ModulationValue.balance_generator(note_perform, framerate, osc.uni_detune, amp.uni_detune)
|
20
|
-
unison = Unison.new(note_perform, osc.shape, osc.phase)
|
21
|
-
|
22
|
-
# Filter
|
23
|
-
filter_mod = nil
|
24
|
-
if filter
|
25
|
-
filter_mod = filter.generator(note_perform, framerate / window_size)
|
26
|
-
end
|
27
|
-
|
28
|
-
case channels
|
29
|
-
when 1
|
30
|
-
-> {
|
31
|
-
buf = AudioStream::Buffer.create_mono(window_size)
|
32
|
-
dst0 = buf.streams[0]
|
33
|
-
|
34
|
-
window_size.times.each {|i|
|
35
|
-
# Oscillator, Amplifier
|
36
|
-
volume = volume_mod[] * note_perform.velocity
|
37
|
-
tune_semis = tune_semis_mod[] + synth.pitch_bend
|
38
|
-
tune_cents = tune_cents_mod[]
|
39
|
-
|
40
|
-
uni_num = uni_num_mod[]
|
41
|
-
uni_detune = uni_detune_mod[]
|
42
|
-
|
43
|
-
sval = unison.next(uni_num, uni_detune, volume, 0.0, tune_semis, tune_cents)
|
44
|
-
mval = (sval[0] + sval[1]) / 2.0
|
45
|
-
|
46
|
-
dst0[i] = mval
|
47
|
-
}
|
48
|
-
|
49
|
-
# Filter
|
50
|
-
if filter_mod
|
51
|
-
filter_fx = filter_mod[]
|
52
|
-
buf = filter_fx.process(buf)
|
53
|
-
end
|
54
|
-
|
55
|
-
buf
|
56
|
-
}
|
57
|
-
when 2
|
58
|
-
-> {
|
59
|
-
buf = AudioStream::Buffer.create_stereo(window_size)
|
60
|
-
dst0 = buf.streams[0]
|
61
|
-
dst1 = buf.streams[1]
|
62
|
-
|
63
|
-
window_size.times.each {|i|
|
64
|
-
# Oscillator, Amplifier
|
65
|
-
volume = volume_mod[] * note_perform.velocity
|
66
|
-
pan = pan_mod[]
|
67
|
-
tune_semis = tune_semis_mod[] + synth.pitch_bend
|
68
|
-
tune_cents = tune_cents_mod[]
|
69
|
-
|
70
|
-
uni_num = uni_num_mod[]
|
71
|
-
uni_detune = uni_detune_mod[]
|
72
|
-
|
73
|
-
sval = unison.next(uni_num, uni_detune, volume, pan, tune_semis, tune_cents)
|
74
|
-
|
75
|
-
dst0[i] = sval[0]
|
76
|
-
dst1[i] = sval[1]
|
77
|
-
}
|
78
|
-
|
79
|
-
# Filter
|
80
|
-
if filter_mod
|
81
|
-
filter_fx = filter_mod[]
|
82
|
-
buf = filter_fx.process(buf)
|
83
|
-
end
|
84
|
-
|
85
|
-
buf
|
86
|
-
}
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
@@ -1,88 +0,0 @@
|
|
1
|
-
module Synthesizer
|
2
|
-
module Processor
|
3
|
-
class Low
|
4
|
-
def generator(osc, note_perform)
|
5
|
-
synth = note_perform.synth
|
6
|
-
filter = synth.filter
|
7
|
-
amp = synth.amplifier
|
8
|
-
channels = synth.soundinfo.channels
|
9
|
-
window_size = synth.soundinfo.window_size
|
10
|
-
framerate = synth.soundinfo.samplerate.to_f / window_size
|
11
|
-
|
12
|
-
# Oscillator, Amplifier
|
13
|
-
volume_mod = ModulationValue.amp_generator(note_perform, framerate, osc.volume, amp.volume)
|
14
|
-
pan_mod = ModulationValue.balance_generator(note_perform, framerate, osc.pan, amp.pan)
|
15
|
-
tune_semis_mod = ModulationValue.balance_generator(note_perform, framerate, osc.tune_semis, amp.tune_semis, synth.glide&.to_modval)
|
16
|
-
tune_cents_mod = ModulationValue.balance_generator(note_perform, framerate, osc.tune_cents, amp.tune_cents)
|
17
|
-
|
18
|
-
uni_num_mod = ModulationValue.balance_generator(note_perform, framerate, osc.uni_num, amp.uni_num, center: 1.0)
|
19
|
-
uni_detune_mod = ModulationValue.balance_generator(note_perform, framerate, osc.uni_detune, amp.uni_detune)
|
20
|
-
unison = Unison.new(note_perform, osc.shape, osc.phase)
|
21
|
-
|
22
|
-
# Filter
|
23
|
-
filter_mod = nil
|
24
|
-
if filter
|
25
|
-
filter_mod = filter.generator(note_perform, framerate)
|
26
|
-
end
|
27
|
-
|
28
|
-
case channels
|
29
|
-
when 1
|
30
|
-
-> {
|
31
|
-
buf = AudioStream::Buffer.create_mono(window_size)
|
32
|
-
dst0 = buf.streams[0]
|
33
|
-
|
34
|
-
# Oscillator, Amplifier
|
35
|
-
volume = volume_mod[] * note_perform.velocity
|
36
|
-
tune_semis = tune_semis_mod[] + synth.pitch_bend
|
37
|
-
tune_cents = tune_cents_mod[]
|
38
|
-
|
39
|
-
uni_num = uni_num_mod[]
|
40
|
-
uni_detune = uni_detune_mod[]
|
41
|
-
|
42
|
-
window_size.times.each {|i|
|
43
|
-
val = unison.next(uni_num, uni_detune, volume, 0.0, tune_semis, tune_cents)
|
44
|
-
dst0[i] = (val[0] + val[1]) / 2.0
|
45
|
-
}
|
46
|
-
|
47
|
-
# Filter
|
48
|
-
if filter_mod
|
49
|
-
filter_fx = filter_mod[]
|
50
|
-
buf = filter_fx.process(buf)
|
51
|
-
end
|
52
|
-
|
53
|
-
buf
|
54
|
-
}
|
55
|
-
when 2
|
56
|
-
-> {
|
57
|
-
buf = AudioStream::Buffer.create_stereo(window_size)
|
58
|
-
dst0 = buf.streams[0]
|
59
|
-
dst1 = buf.streams[1]
|
60
|
-
|
61
|
-
# Oscillator, Amplifier
|
62
|
-
volume = volume_mod[] * note_perform.velocity
|
63
|
-
pan = pan_mod[]
|
64
|
-
tune_semis = tune_semis_mod[] + synth.pitch_bend
|
65
|
-
tune_cents = tune_cents_mod[]
|
66
|
-
|
67
|
-
uni_num = uni_num_mod[]
|
68
|
-
uni_detune = uni_detune_mod[]
|
69
|
-
|
70
|
-
window_size.times.each {|i|
|
71
|
-
val = unison.next(uni_num, uni_detune, volume, pan, tune_semis, tune_cents)
|
72
|
-
dst0[i] = val[0]
|
73
|
-
dst1[i] = val[1]
|
74
|
-
}
|
75
|
-
|
76
|
-
# Filter
|
77
|
-
if filter_mod
|
78
|
-
filter_fx = filter_mod[]
|
79
|
-
buf = filter_fx.process(buf)
|
80
|
-
end
|
81
|
-
|
82
|
-
buf
|
83
|
-
}
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|