synthesizer 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,83 @@
1
+ module Synthesizer
2
+ class ModulationValue
3
+
4
+ attr_accessor :value
5
+ attr_reader :mods
6
+
7
+ def initialize(value, mods={})
8
+ @value = value
9
+ @mods = []
10
+
11
+ mods.each {|mod, depth|
12
+ add(mod, depth: depth)
13
+ }
14
+ end
15
+
16
+ # @param mod [Synth::Modulation]
17
+ # @param depth [Float] (-1.0~1.0)
18
+ def add(mod, depth: 1.0)
19
+ depth ||= 1.0
20
+ if depth<-1.0
21
+ depth = -1.0
22
+ elsif 1.0<depth
23
+ depth = 1.0
24
+ end
25
+
26
+ @mods << [mod, depth]
27
+ self
28
+ end
29
+
30
+ def self.create(value)
31
+ if ModulationValue===value
32
+ value
33
+ else
34
+ new(value)
35
+ end
36
+ end
37
+
38
+ def self.amp_generator(note_perform, samplerate, *modvals)
39
+ modvals = modvals.flatten.compact
40
+
41
+ # value
42
+ value = modvals.map(&:value).sum
43
+
44
+ # mods
45
+ mods = []
46
+ modvals.each {|modval|
47
+ modval.mods.each {|mod, depth|
48
+ mods << mod.amp_generator(note_perform, samplerate, depth)
49
+ }
50
+ }
51
+
52
+ Enumerator.new do |y|
53
+ loop {
54
+ depth = mods.map(&:next).inject(1.0, &:*)
55
+ y << value * depth
56
+ }
57
+ end
58
+ end
59
+
60
+ def self.balance_generator(note_perform, samplerate, *modvals, center: 0)
61
+ modvals = modvals.flatten.compact
62
+
63
+ # value
64
+ value = modvals.map(&:value).sum
65
+ value -= (modvals.length - 1) * center
66
+
67
+ # mods
68
+ mods = []
69
+ modvals.each {|modval|
70
+ modval.mods.each {|mod, depth|
71
+ mods << mod.balance_generator(note_perform, samplerate, depth)
72
+ }
73
+ }
74
+
75
+ Enumerator.new do |y|
76
+ loop {
77
+ depth = mods.map(&:next).sum
78
+ y << value + depth
79
+ }
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,76 @@
1
+ module Synthesizer
2
+ class Mono
3
+
4
+ attr_reader :oscillators
5
+ attr_reader :amplifier
6
+ attr_reader :processor
7
+
8
+ attr_reader :quality
9
+ attr_reader :soundinfo
10
+
11
+ attr_reader :glide
12
+ attr_accessor :pitch_bend
13
+
14
+ # @param oscillators [Oscillator] oscillator
15
+ # @param amplifier [Amplifier] amplifier
16
+ # @param soundinfo [SoundInfo]
17
+ def initialize(oscillators:, amplifier:, glide: 0.1, quality: Quality::LOW, soundinfo:)
18
+ @oscillators = [oscillators].flatten.compact
19
+ @amplifier = amplifier
20
+
21
+ @quality = quality
22
+ @soundinfo = soundinfo
23
+
24
+ @processor = Processor.create(quality)
25
+ @note_nums = []
26
+ @perform = nil
27
+ @glide = Modulation::Glide.new(time: glide)
28
+ @pitch_bend = 0.0
29
+ end
30
+
31
+ def next
32
+ if @perform
33
+ buf = @perform.next
34
+
35
+ # delete released note perform
36
+ if @perform.released?
37
+ @perform = nil
38
+ end
39
+
40
+ buf
41
+ else
42
+ AudioStream::Buffer.float(@soundinfo.window_size, @soundinfo.channels)
43
+ end
44
+ end
45
+
46
+ def note_on(note)
47
+ # Note Off
48
+ note_off(note)
49
+
50
+ if @perform && @perform.note_on?
51
+ # Glide
52
+ @glide.target = note.num
53
+ else
54
+ # Note On
55
+ @perform = NotePerform.new(self, note)
56
+ @glide.base = note.num
57
+ end
58
+ @note_nums << note.num
59
+ end
60
+
61
+ def note_off(note)
62
+ # Note Off
63
+ @note_nums.delete_if {|note_num| note_num==note.num}
64
+
65
+ if @perform
66
+ if @note_nums.length==0
67
+ # Note Off
68
+ @perform.note_off!
69
+ else
70
+ # Glide
71
+ @glide.target = @note_nums.last
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,40 @@
1
+ module Synthesizer
2
+ class Note
3
+ NOTE_TABLE = [:"C", :"C#/Db", :"D", :"D#/Eb", :"E", :"F", :"F#/Gb", :"G", :"G#/Ab", :"A", :"A#/Bb", :"B"].freeze
4
+
5
+ attr_reader :num
6
+
7
+ def initialize(num)
8
+ @num = num.to_i
9
+ end
10
+
11
+ def hz(semis: 0, cents: 0)
12
+ 6.875 * (2 ** ((@num + semis + (cents / 100.0) + 3) / 12.0))
13
+ end
14
+
15
+ def note_name
16
+ NOTE_TABLE[@num % 12]
17
+ end
18
+
19
+ def octave_num
20
+ (@num / 12) - 1
21
+ end
22
+
23
+ def self.create(name, octave)
24
+ name = name.to_s
25
+ octave = octave.to_i
26
+
27
+ note_index = NOTE_TABLE.index(name)
28
+ if !note_index
29
+ raise Error, "not found note name: #{name}"
30
+ end
31
+
32
+ num = (octave + 1) * 12 + note_index
33
+ if num<0
34
+ raise Error, "octave #{octave} outside of note"
35
+ end
36
+
37
+ new(num)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,39 @@
1
+ module Synthesizer
2
+ class NotePerform
3
+
4
+ attr_reader :synth
5
+ attr_reader :note
6
+
7
+ def initialize(synth, note)
8
+ @synth = synth
9
+ @processors = synth.oscillators.map {|osc|
10
+ synth.processor.generator(osc, self)
11
+ }
12
+
13
+ @note = note
14
+ @note_on = true
15
+ @released = false
16
+ end
17
+
18
+ def next
19
+ begin
20
+ @processors.map(&:next).inject(:+)
21
+ rescue StopIteration => e
22
+ @released = true
23
+ nil
24
+ end
25
+ end
26
+
27
+ def note_on?
28
+ @note_on
29
+ end
30
+
31
+ def note_off!
32
+ @note_on = false
33
+ end
34
+
35
+ def released?
36
+ @released
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,41 @@
1
+ module Synthesizer
2
+ class Oscillator
3
+
4
+ attr_reader :shape
5
+ attr_reader :volume
6
+ attr_reader :pan
7
+ attr_reader :tune_semis
8
+ attr_reader :tune_cents
9
+ attr_reader :sym
10
+ attr_reader :phase
11
+ attr_reader :sync
12
+ attr_reader :uni_num
13
+ attr_reader :uni_detune
14
+
15
+ # @param shape [Synth::Shape] oscillator waveform shape
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
20
+ # @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] TODO not implemented
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
+ def initialize(shape: Shape::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
+ @shape = shape
27
+
28
+ @volume = ModulationValue.create(volume)
29
+ @pan = ModulationValue.create(pan)
30
+ @tune_semis = ModulationValue.create(tune_semis)
31
+ @tune_cents = ModulationValue.create(tune_cents)
32
+
33
+ @sym = ModulationValue.create(sym)
34
+ @phase = ModulationValue.create(phase)
35
+ @sync = ModulationValue.create(sync)
36
+
37
+ @uni_num = ModulationValue.create(uni_num)
38
+ @uni_detune = ModulationValue.create(uni_detune)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,62 @@
1
+ module Synthesizer
2
+ class Poly
3
+
4
+ attr_reader :oscillators
5
+ attr_reader :amplifier
6
+ attr_reader :processor
7
+
8
+ attr_reader :quality
9
+ attr_reader :soundinfo
10
+
11
+ attr_reader :glide
12
+ attr_accessor :pitch_bend
13
+
14
+ # @param oscillators [Oscillator] Oscillator
15
+ # @param amplifier [Amplifier] amplifier
16
+ # @param soundinfo [SoundInfo]
17
+ def initialize(oscillators:, amplifier:, quality: Quality::LOW, soundinfo:)
18
+ @oscillators = [oscillators].flatten.compact
19
+ @amplifier = amplifier
20
+
21
+ @quality = quality
22
+ @soundinfo = soundinfo
23
+
24
+ @processor = Processor.create(quality)
25
+ @performs = {}
26
+ @pitch_bend = 0.0
27
+ end
28
+
29
+ def next
30
+ if 0<@performs.length
31
+ bufs = @performs.values.map(&:next)
32
+
33
+ # delete released note performs
34
+ @performs.delete_if {|note_num, perform| perform.released? }
35
+
36
+ bufs.compact.inject(:+)
37
+ else
38
+ AudioStream::Buffer.float(@soundinfo.window_size, @soundinfo.channels)
39
+ end
40
+ end
41
+
42
+ def note_on(note)
43
+ # Note Off
44
+ perform = @performs[note.num]
45
+ if perform
46
+ perform.note_off!
47
+ end
48
+
49
+ # Note On
50
+ perform = NotePerform.new(self, note)
51
+ @performs[note.num] = perform
52
+ end
53
+
54
+ def note_off(note)
55
+ # Note Off
56
+ perform = @performs[note.num]
57
+ if perform
58
+ perform.note_off!
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,10 @@
1
+ require 'synthesizer/processor/low'
2
+ require 'synthesizer/processor/high'
3
+
4
+ module Synthesizer
5
+ module Processor
6
+ def self.create(quality)
7
+ const_get(quality, false).new
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,63 @@
1
+ module Synthesizer
2
+ module Processor
3
+ class High
4
+ def generator(osc, note_perform, &block)
5
+ Enumerator.new do |y|
6
+ synth = note_perform.synth
7
+ amp = synth.amplifier
8
+ channels = synth.soundinfo.channels
9
+ window_size = synth.soundinfo.window_size
10
+ samplerate = synth.soundinfo.samplerate
11
+
12
+ volume_mod = ModulationValue.amp_generator(note_perform, samplerate, osc.volume, amp.volume)
13
+ pan_mod = ModulationValue.balance_generator(note_perform, samplerate, osc.pan, amp.pan)
14
+ tune_semis_mod = ModulationValue.balance_generator(note_perform, samplerate, osc.tune_semis, amp.tune_semis, synth.glide&.to_modval)
15
+ tune_cents_mod = ModulationValue.balance_generator(note_perform, samplerate, osc.tune_cents, amp.tune_cents)
16
+
17
+ uni_num_mod = ModulationValue.balance_generator(note_perform, samplerate, osc.uni_num, amp.uni_num, center: 1.0)
18
+ uni_detune_mod = ModulationValue.balance_generator(note_perform, samplerate, osc.uni_detune, amp.uni_detune)
19
+ unison = Unison.new(note_perform, osc.shape, osc.phase)
20
+
21
+ case channels
22
+ when 1
23
+ loop {
24
+ buf = AudioStream::Buffer.float(window_size, channels)
25
+
26
+ window_size.times.each {|i|
27
+ volume = volume_mod.next
28
+ tune_semis = tune_semis_mod.next + synth.pitch_bend
29
+ tune_cents = tune_cents_mod.next
30
+
31
+ uni_num = uni_num_mod.next
32
+ uni_detune = uni_detune_mod.next
33
+
34
+ val = unison.next(uni_num, uni_detune, volume, 0.0, tune_semis, tune_cents)
35
+ buf[i] = (val[0] + val[1]) / 2.0
36
+ }
37
+
38
+ y << buf
39
+ }
40
+ when 2
41
+ loop {
42
+ buf = AudioStream::Buffer.float(window_size, channels)
43
+
44
+ window_size.times.each {|i|
45
+ volume = volume_mod.next
46
+ pan = pan_mod.next
47
+ tune_semis = tune_semis_mod.next + synth.pitch_bend
48
+ tune_cents = tune_cents_mod.next
49
+
50
+ uni_num = uni_num_mod.next
51
+ uni_detune = uni_detune_mod.next
52
+
53
+ buf[i] = unison.next(uni_num, uni_detune, volume, pan, tune_semis, tune_cents)
54
+ }
55
+
56
+ y << buf
57
+ }
58
+ end
59
+ end.each(&block)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,63 @@
1
+ module Synthesizer
2
+ module Processor
3
+ class Low
4
+ def generator(osc, note_perform, &block)
5
+ Enumerator.new do |y|
6
+ synth = note_perform.synth
7
+ amp = synth.amplifier
8
+ channels = synth.soundinfo.channels
9
+ window_size = synth.soundinfo.window_size
10
+ samplerate = synth.soundinfo.samplerate.to_f / window_size
11
+
12
+ volume_mod = ModulationValue.amp_generator(note_perform, samplerate, osc.volume, amp.volume)
13
+ pan_mod = ModulationValue.balance_generator(note_perform, samplerate, osc.pan, amp.pan)
14
+ tune_semis_mod = ModulationValue.balance_generator(note_perform, samplerate, osc.tune_semis, amp.tune_semis, synth.glide&.to_modval)
15
+ tune_cents_mod = ModulationValue.balance_generator(note_perform, samplerate, osc.tune_cents, amp.tune_cents)
16
+
17
+ uni_num_mod = ModulationValue.balance_generator(note_perform, samplerate, osc.uni_num, amp.uni_num, center: 1.0)
18
+ uni_detune_mod = ModulationValue.balance_generator(note_perform, samplerate, osc.uni_detune, amp.uni_detune)
19
+ unison = Unison.new(note_perform, osc.shape, osc.phase)
20
+
21
+ case channels
22
+ when 1
23
+ loop {
24
+ buf = AudioStream::Buffer.float(window_size, channels)
25
+
26
+ volume = volume_mod.next
27
+ tune_semis = tune_semis_mod.next + synth.pitch_bend
28
+ tune_cents = tune_cents_mod.next
29
+
30
+ uni_num = uni_num_mod.next
31
+ uni_detune = uni_detune_mod.next
32
+
33
+ window_size.times.each {|i|
34
+ val = unison.next(uni_num, uni_detune, volume, 0.0, tune_semis, tune_cents)
35
+ buf[i] = (val[0] + val[1]) / 2.0
36
+ }
37
+
38
+ y << buf
39
+ }
40
+ when 2
41
+ loop {
42
+ buf = AudioStream::Buffer.float(window_size, channels)
43
+
44
+ volume = volume_mod.next
45
+ pan = pan_mod.next
46
+ tune_semis = tune_semis_mod.next + synth.pitch_bend
47
+ tune_cents = tune_cents_mod.next
48
+
49
+ uni_num = uni_num_mod.next
50
+ uni_detune = uni_detune_mod.next
51
+
52
+ window_size.times.each {|i|
53
+ buf[i] = unison.next(uni_num, uni_detune, volume, pan, tune_semis, tune_cents)
54
+ }
55
+
56
+ y << buf
57
+ }
58
+ end
59
+ end.each(&block)
60
+ end
61
+ end
62
+ end
63
+ end