audio_stream 3.3.3 → 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 +2 -0
- data/examples/bench_fx.rb +89 -0
- data/examples/canon.wav +0 -0
- data/examples/example_options.rb +2 -0
- data/examples/{chorus.rb → fx_chorus.rb} +2 -7
- data/examples/fx_compressor.rb +34 -0
- data/examples/fx_convolution_reverb.rb +37 -0
- data/examples/fx_delay.rb +33 -0
- data/examples/{distortion.rb → fx_distortion.rb} +4 -8
- data/examples/fx_noise_gate.rb +33 -0
- data/examples/fx_phaser.rb +36 -0
- data/examples/fx_schroeder_reverb.rb +34 -0
- data/examples/fx_vocoder.rb +44 -0
- data/examples/impulse_shaker.wav +0 -0
- data/examples/impulse_shaker_bigroom.wav +0 -0
- data/examples/impulse_shaker_smallhall.wav +0 -0
- data/examples/tuner.rb +4 -3
- data/jupyter/band_pass_filter.ipynb +117 -0
- data/jupyter/biquad.ipynb +372 -0
- data/jupyter/equalizer.ipynb +198 -0
- data/lib/audio_stream/audio_input_device.rb +20 -3
- data/lib/audio_stream/audio_observable.rb +5 -0
- data/lib/audio_stream/audio_output_device.rb +20 -3
- data/lib/audio_stream/buffer.rb +22 -0
- data/lib/audio_stream/decibel.rb +8 -8
- data/lib/audio_stream/fx/a_gain.rb +2 -6
- data/lib/audio_stream/fx/all_pass_filter.rb +4 -2
- data/lib/audio_stream/fx/band_pass_filter.rb +4 -2
- data/lib/audio_stream/fx/biquad_filter.rb +8 -3
- data/lib/audio_stream/fx/chorus.rb +6 -5
- data/lib/audio_stream/fx/comb_filter.rb +4 -2
- data/lib/audio_stream/fx/convolution_reverb.rb +8 -5
- data/lib/audio_stream/fx/delay.rb +6 -5
- data/lib/audio_stream/fx/distortion.rb +4 -4
- data/lib/audio_stream/fx/equalizer_2band.rb +5 -3
- data/lib/audio_stream/fx/equalizer_3band.rb +7 -0
- data/lib/audio_stream/fx/graphic_equalizer.rb +4 -0
- data/lib/audio_stream/fx/hanning_window.rb +0 -9
- data/lib/audio_stream/fx/high_pass_filter.rb +4 -2
- data/lib/audio_stream/fx/high_shelf_filter.rb +6 -7
- data/lib/audio_stream/fx/low_pass_filter.rb +4 -2
- data/lib/audio_stream/fx/low_shelf_filter.rb +6 -7
- data/lib/audio_stream/fx/noise_gate.rb +5 -4
- data/lib/audio_stream/fx/panning.rb +2 -1
- data/lib/audio_stream/fx/peaking_filter.rb +6 -7
- data/lib/audio_stream/fx/phaser.rb +7 -6
- data/lib/audio_stream/fx/schroeder_reverb.rb +9 -10
- data/lib/audio_stream/fx/tremolo.rb +7 -5
- data/lib/audio_stream/rate.rb +30 -5
- data/lib/audio_stream/version.rb +1 -1
- metadata +23 -10
- data/examples/biquad.ipynb +0 -428
- data/examples/equalizer.ipynb +0 -233
@@ -48,17 +48,34 @@ module AudioStream
|
|
48
48
|
|
49
49
|
def self.default_device(soundinfo:)
|
50
50
|
dev = CoreAudio.default_input_device
|
51
|
+
is_supported_sample_rate = dev.available_sample_rate.any? {|min,max|
|
52
|
+
min<=soundinfo.samplerate && soundinfo.samplerate<=max
|
53
|
+
}
|
54
|
+
if !is_supported_sample_rate
|
55
|
+
raise Error, "Unsupported sample rate: samplerate=#{soundinfo.samplerate}, device=#{dev.name}, available_sample_rate=#{dev.available_sample_rate}"
|
56
|
+
end
|
57
|
+
|
58
|
+
dev = CoreAudio.default_input_device({nominal_rate: soundinfo.samplerate})
|
51
59
|
new(dev, soundinfo: soundinfo)
|
52
60
|
end
|
53
61
|
|
54
62
|
def self.devices(soundinfo:)
|
55
|
-
|
56
|
-
.select{|dev|
|
57
|
-
|
63
|
+
self.core_devices
|
64
|
+
.select {|dev|
|
65
|
+
dev.available_sample_rate.any? {|min,max|
|
66
|
+
min<=soundinfo.samplerate && soundinfo.samplerate<=max
|
67
|
+
}
|
58
68
|
}
|
59
69
|
.map {|dev|
|
60
70
|
new(dev, soundinfo: soundinfo)
|
61
71
|
}
|
62
72
|
end
|
73
|
+
|
74
|
+
def self.core_devices
|
75
|
+
CoreAudio.devices
|
76
|
+
.select {|dev|
|
77
|
+
0<dev.input_stream.channels
|
78
|
+
}
|
79
|
+
end
|
63
80
|
end
|
64
81
|
end
|
@@ -15,6 +15,8 @@ module AudioStream
|
|
15
15
|
notify_observers(AudioNotification.new(AudioNotification::STAT_COMPLETE, nil, self))
|
16
16
|
end
|
17
17
|
|
18
|
+
# @param effector [AudioStream::Fx::*] Effector
|
19
|
+
# @param kwargs [Hash[String,AudioStream::AudioObservable]] Side chain bus
|
18
20
|
def fx(effector, **kwargs)
|
19
21
|
if Fx::MultiAudioInputtable===effector
|
20
22
|
bus = AudioObservableFxBus.new(effector)
|
@@ -46,6 +48,9 @@ module AudioStream
|
|
46
48
|
observer
|
47
49
|
end
|
48
50
|
|
51
|
+
# @param bus [AudioStream::AudioBus] Receive bus
|
52
|
+
# @param gain [AudioStream::Decibel | Float] Amplification level (~0.0)
|
53
|
+
# @param pan [Float] Panning (-1.0~1.0)
|
49
54
|
def send_to(bus, gain: nil, pan: nil)
|
50
55
|
bus.add(self, gain: gain, pan: pan)
|
51
56
|
self
|
@@ -41,18 +41,35 @@ module AudioStream
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def self.default_device(soundinfo:)
|
44
|
+
dev = CoreAudio.default_output_device
|
45
|
+
is_supported_sample_rate = dev.available_sample_rate.any? {|min,max|
|
46
|
+
min<=soundinfo.samplerate && soundinfo.samplerate<=max
|
47
|
+
}
|
48
|
+
if !is_supported_sample_rate
|
49
|
+
raise Error, "Unsupported sample rate: samplerate=#{soundinfo.samplerate}, device=#{dev.name}, available_sample_rate=#{dev.available_sample_rate}"
|
50
|
+
end
|
51
|
+
|
44
52
|
dev = CoreAudio.default_output_device({nominal_rate: soundinfo.samplerate})
|
45
53
|
new(dev, soundinfo: soundinfo)
|
46
54
|
end
|
47
55
|
|
48
56
|
def self.devices(soundinfo:)
|
49
|
-
|
50
|
-
.select{|dev|
|
51
|
-
|
57
|
+
self.core_devices
|
58
|
+
.select {|dev|
|
59
|
+
dev.available_sample_rate.any? {|min,max|
|
60
|
+
min<=soundinfo.samplerate && soundinfo.samplerate<=max
|
61
|
+
}
|
52
62
|
}
|
53
63
|
.map {|dev|
|
54
64
|
new(dev, soundinfo: soundinfo)
|
55
65
|
}
|
56
66
|
end
|
67
|
+
|
68
|
+
def self.core_devices
|
69
|
+
CoreAudio.devices
|
70
|
+
.select {|dev|
|
71
|
+
0<dev.output_stream.channels
|
72
|
+
}
|
73
|
+
end
|
57
74
|
end
|
58
75
|
end
|
data/lib/audio_stream/buffer.rb
CHANGED
@@ -84,6 +84,26 @@ module AudioStream
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
+
def -(other)
|
88
|
+
if self.window_size!=other.window_size
|
89
|
+
raise Error, "Buffer.window_size is not match: self.window_size=#{self.window_size} other.window_size=#{other.window_size}"
|
90
|
+
end
|
91
|
+
|
92
|
+
channels = [self.channels, other.channels].max
|
93
|
+
case channels
|
94
|
+
when 1
|
95
|
+
stream0 = self.streams[0] + other.streams[0]
|
96
|
+
self.class.new(stream0)
|
97
|
+
when 2
|
98
|
+
st_self = self.stereo
|
99
|
+
st_other = other.stereo
|
100
|
+
|
101
|
+
stream0 = st_self.streams[0] - st_other.streams[0]
|
102
|
+
stream1 = st_self.streams[1] - st_other.streams[1]
|
103
|
+
self.class.new(stream0, stream1)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
87
107
|
def self.merge(buffers, average: false)
|
88
108
|
buffers.each {|buf|
|
89
109
|
unless Buffer===buf
|
@@ -218,6 +238,8 @@ module AudioStream
|
|
218
238
|
max = 0x7FFF.to_f
|
219
239
|
when NArray::FLOAT
|
220
240
|
max = 1.0
|
241
|
+
when NArray::DCOMPLEX
|
242
|
+
max = 1.0
|
221
243
|
end
|
222
244
|
|
223
245
|
case channels
|
data/lib/audio_stream/decibel.rb
CHANGED
@@ -14,18 +14,18 @@ module AudioStream
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.db(db)
|
17
|
-
|
17
|
+
if self===db
|
18
|
+
db
|
19
|
+
else
|
20
|
+
new(db: db.to_f)
|
21
|
+
end
|
18
22
|
end
|
19
23
|
|
20
24
|
def self.mag(mag)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
def self.create(val)
|
25
|
-
if self===val
|
26
|
-
val
|
25
|
+
if self===mag
|
26
|
+
mag
|
27
27
|
else
|
28
|
-
new(
|
28
|
+
new(mag: mag.to_f)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -1,13 +1,9 @@
|
|
1
1
|
module AudioStream
|
2
2
|
module Fx
|
3
3
|
class AGain
|
4
|
-
# @param level [AudioStream::Decibel] Amplification level
|
4
|
+
# @param level [AudioStream::Decibel | Float] Amplification level (~0.0)
|
5
5
|
def initialize(level:)
|
6
|
-
|
7
|
-
@level = level.mag
|
8
|
-
else
|
9
|
-
@level = Decibel.db(level).mag
|
10
|
-
end
|
6
|
+
@level = Decibel.db(level).mag
|
11
7
|
end
|
12
8
|
|
13
9
|
def process(input)
|
@@ -3,7 +3,9 @@ module AudioStream
|
|
3
3
|
class AllPassFilter < BiquadFilter
|
4
4
|
|
5
5
|
def update_coef(freq:, q:)
|
6
|
-
|
6
|
+
freq = Rate.freq(freq)
|
7
|
+
|
8
|
+
omega = freq.sample_phase(@soundinfo)
|
7
9
|
alpha = Math.sin(omega) / (2.0 * q)
|
8
10
|
|
9
11
|
a0 = 1.0 + alpha
|
@@ -20,7 +22,7 @@ module AudioStream
|
|
20
22
|
end
|
21
23
|
|
22
24
|
# @param soundinfo [AudioStream::SoundInfo]
|
23
|
-
# @param freq [Float] Cutoff frequency
|
25
|
+
# @param freq [AudioStream::Rate | Float] Cutoff frequency
|
24
26
|
# @param q [Float] Quality factor
|
25
27
|
def self.create(soundinfo, freq:, q: DEFAULT_Q)
|
26
28
|
filter = new(soundinfo)
|
@@ -3,7 +3,9 @@ module AudioStream
|
|
3
3
|
class BandPassFilter < BiquadFilter
|
4
4
|
|
5
5
|
def update_coef(freq:, bandwidth:)
|
6
|
-
|
6
|
+
freq = Rate.freq(freq)
|
7
|
+
|
8
|
+
omega = freq.sample_phase(@soundinfo)
|
7
9
|
alpha = Math.sin(omega) * Math.sinh(Math.log(2.0) / 2.0 * bandwidth * omega / Math.sin(omega))
|
8
10
|
|
9
11
|
a0 = 1.0 + alpha
|
@@ -20,7 +22,7 @@ module AudioStream
|
|
20
22
|
end
|
21
23
|
|
22
24
|
# @param soundinfo [AudioStream::SoundInfo]
|
23
|
-
# @param freq [Float] Center frequency
|
25
|
+
# @param freq [AudioStream::Rate | Float] Center frequency
|
24
26
|
# @param bandwidth [Float] bandwidth (octave)
|
25
27
|
def self.create(soundinfo, freq:, bandwidth: 1.0)
|
26
28
|
filter = new(soundinfo)
|
@@ -4,7 +4,7 @@ module AudioStream
|
|
4
4
|
DEFAULT_Q = 1.0 / Math.sqrt(2.0)
|
5
5
|
|
6
6
|
def initialize(soundinfo)
|
7
|
-
@
|
7
|
+
@soundinfo = soundinfo
|
8
8
|
@biquads = [
|
9
9
|
Vdsp::DoubleBiquad.new(1),
|
10
10
|
Vdsp::DoubleBiquad.new(1),
|
@@ -37,7 +37,7 @@ module AudioStream
|
|
37
37
|
a2 = @coef.a2
|
38
38
|
|
39
39
|
noctaves = 10
|
40
|
-
nyquist = @samplerate * 0.5
|
40
|
+
nyquist = @soundinfo.samplerate * 0.5
|
41
41
|
|
42
42
|
freq = []
|
43
43
|
x = []
|
@@ -67,11 +67,16 @@ module AudioStream
|
|
67
67
|
def plot(width=500)
|
68
68
|
data = plot_data(width)
|
69
69
|
|
70
|
+
mag_range = nil
|
71
|
+
if -1.0<data[:magnitude].min && data[:magnitude].max<1.0
|
72
|
+
mag_range = [-1.0, 1.0]
|
73
|
+
end
|
74
|
+
|
70
75
|
Plotly::Plot.new(
|
71
76
|
data: [{x: data[:x], y: data[:magnitude], name: 'Magnitude', yaxis: 'y1'}, {x: data[:x], y: data[:phase], name: 'Phase', yaxis: 'y2'}],
|
72
77
|
layout: {
|
73
78
|
xaxis: {title: 'Frequency (Hz)', type: 'log'},
|
74
|
-
yaxis: {side: 'left', title: 'Magnitude (dB)', showgrid: false},
|
79
|
+
yaxis: {side: 'left', title: 'Magnitude (dB)', range: mag_range, showgrid: false},
|
75
80
|
yaxis2: {side: 'right', title: 'Phase (deg)', showgrid: false, overlaying: 'y'}
|
76
81
|
}
|
77
82
|
)
|
@@ -1,11 +1,12 @@
|
|
1
1
|
module AudioStream
|
2
2
|
module Fx
|
3
3
|
class Chorus
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
# @param soundinfo [AudioStream::SoundInfo]
|
5
|
+
# @param depth [Float] Chorus depth (0.0~)
|
6
|
+
# @param rate [AudioStream::Rate | Float] Chorus speed (0.0~) default unit: sec
|
7
|
+
def initialize(soundinfo, depth: 100, rate: 4)
|
7
8
|
@depth = depth
|
8
|
-
@rate = rate
|
9
|
+
@rate = Rate.sec(rate)
|
9
10
|
|
10
11
|
@delaybufs = [
|
11
12
|
RingBuffer.new(@depth * 3, 0.0),
|
@@ -13,7 +14,7 @@ module AudioStream
|
|
13
14
|
]
|
14
15
|
|
15
16
|
@phase = 0
|
16
|
-
@speed =
|
17
|
+
@speed = @rate.sample_phase(soundinfo)
|
17
18
|
end
|
18
19
|
|
19
20
|
def process(input)
|
@@ -3,12 +3,14 @@ module AudioStream
|
|
3
3
|
class CombFilter
|
4
4
|
|
5
5
|
# @param soundinfo [AudioStream::SoundInfo]
|
6
|
-
# @param freq [AudioStream::Rate] frequency
|
6
|
+
# @param freq [AudioStream::Rate | Float] frequency
|
7
7
|
# @param q [Float] Quality factor
|
8
8
|
def initialize(soundinfo, freq:, q:)
|
9
|
+
freq = Rate.freq(freq)
|
10
|
+
|
9
11
|
@window_size = soundinfo.window_size
|
10
12
|
@delaysample = freq.sample(soundinfo).round
|
11
|
-
@q = q
|
13
|
+
@q = q.to_f
|
12
14
|
|
13
15
|
@delaybufs = [
|
14
16
|
Vdsp::DoubleArray.new(soundinfo.window_size + @delaysample),
|
@@ -1,13 +1,16 @@
|
|
1
1
|
module AudioStream
|
2
2
|
module Fx
|
3
3
|
class ConvolutionReverb
|
4
|
-
|
4
|
+
# @param impulse [AudioStream::AudioInput] Impulse input
|
5
|
+
# @param dry [AudioStream::Decibel | Float] Dry gain
|
6
|
+
# @param wet [AudioStream::Decibel | Float] Wet gain
|
7
|
+
def initialize(impulse, dry: -6, wet: -6)
|
5
8
|
impulse_bufs = impulse.to_a
|
6
9
|
@impulse_size = impulse_bufs.size
|
7
10
|
@channels = impulse_bufs[0].channels
|
8
11
|
@window_size = impulse_bufs[0].window_size
|
9
|
-
@dry_gain = dry
|
10
|
-
@wet_gain = wet
|
12
|
+
@dry_gain = Decibel.db(dry).mag
|
13
|
+
@wet_gain = Decibel.db(wet).mag
|
11
14
|
|
12
15
|
zero_buf = Buffer.create(@window_size, @channels)
|
13
16
|
impulse_bufs = [zero_buf.clone] + impulse_bufs
|
@@ -16,7 +19,7 @@ module AudioStream
|
|
16
19
|
@impulse_size.times {|i|
|
17
20
|
na = NArray.float(@channels, @window_size*2)
|
18
21
|
impulse_bufs[i].to_float_na(na, 0)
|
19
|
-
impulse_bufs[i+1].to_float_na(na, @window_size)
|
22
|
+
impulse_bufs[i+1].to_float_na(na, @window_size*@channels)
|
20
23
|
@impulse_ffts << FFTW3.fft(na, FFTW3::FORWARD) / na.length
|
21
24
|
}
|
22
25
|
|
@@ -40,7 +43,7 @@ module AudioStream
|
|
40
43
|
# current dry to wet
|
41
44
|
na = NArray.float(@channels, @window_size*2)
|
42
45
|
@prev_input.to_float_na(na, 0)
|
43
|
-
input.to_float_na(na, @window_size)
|
46
|
+
input.to_float_na(na, @window_size*@channels)
|
44
47
|
|
45
48
|
input_fft = FFTW3.fft(na, FFTW3::FORWARD) / na.length
|
46
49
|
|
@@ -2,12 +2,13 @@ module AudioStream
|
|
2
2
|
module Fx
|
3
3
|
class Delay
|
4
4
|
# @param soundinfo [AudioStream::SoundInfo]
|
5
|
-
# @param time [AudioStream::Rate] delay time
|
6
|
-
# @param level [AudioStream::Decibel] wet gain
|
7
|
-
# @param feedback [AudioStream::Decibel] feedback level
|
5
|
+
# @param time [AudioStream::Rate | Float] delay time. default unit: sec
|
6
|
+
# @param level [AudioStream::Decibel | Float] wet gain
|
7
|
+
# @param feedback [AudioStream::Decibel | Float] feedback level
|
8
8
|
def initialize(soundinfo, time:, level:, feedback:)
|
9
|
-
|
10
|
-
@
|
9
|
+
time = Rate.sec(time)
|
10
|
+
@level = Decibel.db(level).mag
|
11
|
+
@feedback = Decibel.db(feedback).mag
|
11
12
|
|
12
13
|
@delaysample = time.sample(soundinfo).round
|
13
14
|
@delaybuf0 = Array.new(@delaysample, 0.0)
|
@@ -1,11 +1,11 @@
|
|
1
1
|
module AudioStream
|
2
2
|
module Fx
|
3
3
|
class Distortion
|
4
|
-
# @param gain [AudioStream::Decibel] input gain
|
5
|
-
# @param level [AudioStream::Decibel] output level
|
4
|
+
# @param gain [AudioStream::Decibel | Float] input gain
|
5
|
+
# @param level [AudioStream::Decibel | Float] output level
|
6
6
|
def initialize(gain: 40.0, level: -20.0)
|
7
|
-
@gain = Decibel.
|
8
|
-
@level = Decibel.
|
7
|
+
@gain = Decibel.db(gain).mag
|
8
|
+
@level = Decibel.db(level).mag
|
9
9
|
end
|
10
10
|
|
11
11
|
def process(input)
|
@@ -1,9 +1,11 @@
|
|
1
1
|
module AudioStream
|
2
2
|
module Fx
|
3
3
|
class Equalizer2band
|
4
|
-
# @param
|
5
|
-
# @param
|
6
|
-
# @param
|
4
|
+
# @param soundinfo [AudioStream::SoundInfo]
|
5
|
+
# @param lowfreq [AudioStream::Rate | Float] Low cutoff frequency
|
6
|
+
# @param lowgain [AudioStream::Decibel | Float] Amplification level at low cutoff frequency
|
7
|
+
# @param highfreq [AudioStream::Rate | Float] High cutoff frequency
|
8
|
+
# @param highgain [AudioStream::Decibel | Float] Amplification level at high cutoff frequency
|
7
9
|
def initialize(soundinfo, lowfreq: 400.0, lowgain:, highfreq: 4000.0, highgain:)
|
8
10
|
@low_filter = LowShelfFilter.create(soundinfo, freq: lowfreq, q: BiquadFilter::DEFAULT_Q, gain: lowgain)
|
9
11
|
@high_filter = HighShelfFilter.create(soundinfo, freq: highfreq, q: BiquadFilter::DEFAULT_Q, gain: highgain)
|
@@ -1,6 +1,13 @@
|
|
1
1
|
module AudioStream
|
2
2
|
module Fx
|
3
3
|
class Equalizer3band
|
4
|
+
# @param soundinfo [AudioStream::SoundInfo]
|
5
|
+
# @param lowfreq [AudioStream::Rate | Float] Low cutoff frequency
|
6
|
+
# @param lowgain [AudioStream::Decibel | Float] Amplification level at low cutoff frequency
|
7
|
+
# @param midfreq [AudioStream::Rate | Float] Middle cutoff frequency
|
8
|
+
# @param midgain [AudioStream::Decibel | Float] Amplification level at middle cutoff frequency
|
9
|
+
# @param highfreq [AudioStream::Rate | Float] High cutoff frequency
|
10
|
+
# @param highgain [AudioStream::Decibel | Float] Amplification level at high cutoff frequency
|
4
11
|
def initialize(soundinfo, lowfreq: 400.0, lowgain:, midfreq: 1000.0, midgain:, highfreq: 4000.0, highgain:)
|
5
12
|
@low_filter = LowShelfFilter.create(soundinfo, freq: lowfreq, q: BiquadFilter::DEFAULT_Q, gain: lowgain)
|
6
13
|
@mid_filter = PeakingFilter.create(soundinfo, freq: midfreq, bandwidth: 1.0, gain: midgain)
|
@@ -1,11 +1,15 @@
|
|
1
1
|
module AudioStream
|
2
2
|
module Fx
|
3
3
|
class GraphicEqualizer
|
4
|
+
# @param soundinfo [AudioStream::SoundInfo]
|
4
5
|
def initialize(soundinfo)
|
5
6
|
@soundinfo = soundinfo
|
6
7
|
@filters = []
|
7
8
|
end
|
8
9
|
|
10
|
+
# @param freq [AudioStream::Rate | Float] Cutoff frequency
|
11
|
+
# @param bandwidth [Float] bandwidth (octave)
|
12
|
+
# @param gain [AudioStream::Decibel | Float] Amplification level at cutoff frequency
|
9
13
|
def add(freq:, bandwidth: 1.0, gain:)
|
10
14
|
@filters << PeakingFilter.create(@soundinfo, freq: freq, bandwidth: bandwidth, gain: gain)
|
11
15
|
self
|
@@ -4,16 +4,7 @@ module AudioStream
|
|
4
4
|
include Singleton
|
5
5
|
|
6
6
|
def process(input)
|
7
|
-
#window_size = input.window_size
|
8
|
-
#window_max = input.window_size - 1
|
9
|
-
#channels = input.channels
|
10
|
-
|
11
|
-
#period = 2 * Math::PI / window_max
|
12
|
-
|
13
7
|
streams = input.streams.map {|stream|
|
14
|
-
#stream.map.with_index {|f, i|
|
15
|
-
# f * (0.5 - 0.5 * Math.cos(i * period))
|
16
|
-
#}
|
17
8
|
stream * self.window(input.window_size)
|
18
9
|
}
|
19
10
|
|
@@ -3,7 +3,9 @@ module AudioStream
|
|
3
3
|
class HighPassFilter < BiquadFilter
|
4
4
|
|
5
5
|
def update_coef(freq:, q:)
|
6
|
-
|
6
|
+
freq = Rate.freq(freq)
|
7
|
+
|
8
|
+
omega = freq.sample_phase(@soundinfo)
|
7
9
|
alpha = Math.sin(omega) / (2.0 * q)
|
8
10
|
|
9
11
|
a0 = 1.0 + alpha
|
@@ -20,7 +22,7 @@ module AudioStream
|
|
20
22
|
end
|
21
23
|
|
22
24
|
# @param soundinfo [AudioStream::SoundInfo]
|
23
|
-
# @param freq [Float] Cutoff frequency
|
25
|
+
# @param freq [AudioStream::Rate | Float] Cutoff frequency
|
24
26
|
# @param q [Float] Quality factor
|
25
27
|
def self.create(soundinfo, freq:, q: DEFAULT_Q)
|
26
28
|
filter = new(soundinfo)
|
@@ -3,13 +3,12 @@ module AudioStream
|
|
3
3
|
class HighShelfFilter < BiquadFilter
|
4
4
|
|
5
5
|
def update_coef(freq:, q:, gain:)
|
6
|
-
|
7
|
-
|
8
|
-
end
|
6
|
+
freq = Rate.freq(freq)
|
7
|
+
gain = Decibel.db(gain)
|
9
8
|
|
10
|
-
omega =
|
9
|
+
omega = freq.sample_phase(@soundinfo)
|
11
10
|
alpha = Math.sin(omega) / (2.0 * q)
|
12
|
-
a =
|
11
|
+
a = Decibel.db(gain.db / 2.0).mag
|
13
12
|
beta = Math.sqrt(a) / q
|
14
13
|
|
15
14
|
a0 = (a+1) - (a-1) * Math.cos(omega) + beta * Math.sin(omega)
|
@@ -26,9 +25,9 @@ module AudioStream
|
|
26
25
|
end
|
27
26
|
|
28
27
|
# @param soundinfo [AudioStream::SoundInfo]
|
29
|
-
# @param freq [Float] Cutoff frequency
|
28
|
+
# @param freq [AudioStream::Rate | Float] Cutoff frequency
|
30
29
|
# @param q [Float] Quality factor
|
31
|
-
# @param gain [AudioStream::Decibel] Amplification level at cutoff frequency
|
30
|
+
# @param gain [AudioStream::Decibel | Float] Amplification level at cutoff frequency
|
32
31
|
def self.create(soundinfo, freq:, q: DEFAULT_Q, gain: 1.0)
|
33
32
|
filter = new(soundinfo)
|
34
33
|
filter.update_coef(freq: freq, q: q, gain: gain)
|
@@ -3,7 +3,9 @@ module AudioStream
|
|
3
3
|
class LowPassFilter < BiquadFilter
|
4
4
|
|
5
5
|
def update_coef(freq:, q:)
|
6
|
-
|
6
|
+
freq = Rate.freq(freq)
|
7
|
+
|
8
|
+
omega = freq.sample_phase(@soundinfo)
|
7
9
|
alpha = Math.sin(omega) / (2.0 * q)
|
8
10
|
|
9
11
|
a0 = 1.0 + alpha
|
@@ -20,7 +22,7 @@ module AudioStream
|
|
20
22
|
end
|
21
23
|
|
22
24
|
# @param soundinfo [AudioStream::SoundInfo]
|
23
|
-
# @param freq [Float] Cutoff frequency
|
25
|
+
# @param freq [AudioStream::Rate | Float] Cutoff frequency
|
24
26
|
# @param q [Float] Quality factor
|
25
27
|
def self.create(soundinfo, freq:, q: DEFAULT_Q)
|
26
28
|
filter = new(soundinfo)
|
@@ -3,13 +3,12 @@ module AudioStream
|
|
3
3
|
class LowShelfFilter < BiquadFilter
|
4
4
|
|
5
5
|
def update_coef(freq:, q:, gain:)
|
6
|
-
|
7
|
-
|
8
|
-
end
|
6
|
+
freq = Rate.freq(freq)
|
7
|
+
gain = Decibel.db(gain)
|
9
8
|
|
10
|
-
omega =
|
9
|
+
omega = freq.sample_phase(@soundinfo)
|
11
10
|
alpha = Math.sin(omega) / (2.0 * q)
|
12
|
-
a =
|
11
|
+
a = Decibel.db(gain.db / 2.0).mag
|
13
12
|
beta = Math.sqrt(a) / q
|
14
13
|
|
15
14
|
a0 = (a+1) + (a-1) * Math.cos(omega) + beta * Math.sin(omega)
|
@@ -26,9 +25,9 @@ module AudioStream
|
|
26
25
|
end
|
27
26
|
|
28
27
|
# @param soundinfo [AudioStream::SoundInfo]
|
29
|
-
# @param freq [Float] Cutoff frequency
|
28
|
+
# @param freq [AudioStream::Rate | Float] Cutoff frequency
|
30
29
|
# @param q [Float] Quality factor
|
31
|
-
# @param gain [AudioStream::Decibel] Amplification level at cutoff frequency
|
30
|
+
# @param gain [AudioStream::Decibel | Float] Amplification level at cutoff frequency
|
32
31
|
def self.create(soundinfo, freq:, q: DEFAULT_Q, gain: 1.0)
|
33
32
|
filter = new(soundinfo)
|
34
33
|
filter.update_coef(freq: freq, q: q, gain: gain)
|
@@ -11,18 +11,19 @@ module AudioStream
|
|
11
11
|
channels = input.channels
|
12
12
|
|
13
13
|
# fft
|
14
|
-
|
15
|
-
na = input.to_float_na
|
14
|
+
na = @window.process(input).to_float_na
|
16
15
|
fft = FFTW3.fft(na, FFTW3::FORWARD) / na.length
|
17
16
|
|
17
|
+
# noise gate
|
18
18
|
fft.size.times {|i|
|
19
|
-
if fft[i].abs
|
19
|
+
if @threshold <= fft[i].abs
|
20
20
|
fft[i] = 0i
|
21
21
|
end
|
22
22
|
}
|
23
23
|
wet_na = FFTW3.fft(fft, FFTW3::BACKWARD)
|
24
|
+
noise = Buffer.from_na(wet_na)
|
24
25
|
|
25
|
-
|
26
|
+
input - noise
|
26
27
|
end
|
27
28
|
end
|
28
29
|
end
|
@@ -3,13 +3,12 @@ module AudioStream
|
|
3
3
|
class PeakingFilter < BiquadFilter
|
4
4
|
|
5
5
|
def update_coef(freq:, bandwidth:, gain:)
|
6
|
-
|
7
|
-
|
8
|
-
end
|
6
|
+
freq = Rate.freq(freq)
|
7
|
+
gain = Decibel.db(gain)
|
9
8
|
|
10
|
-
omega =
|
9
|
+
omega = freq.sample_phase(@soundinfo)
|
11
10
|
alpha = Math.sin(omega) * Math.sinh(Math.log(2.0) / 2.0 * bandwidth * omega / Math.sin(omega))
|
12
|
-
a =
|
11
|
+
a = Decibel.db(gain.db / 2.0).mag
|
13
12
|
|
14
13
|
a0 = 1.0 + alpha / a
|
15
14
|
a1 = -2.0 * Math.cos(omega)
|
@@ -25,9 +24,9 @@ module AudioStream
|
|
25
24
|
end
|
26
25
|
|
27
26
|
# @param soundinfo [AudioStream::SoundInfo]
|
28
|
-
# @param freq [Float] Cutoff frequency
|
27
|
+
# @param freq [AudioStream::Rate | Float] Cutoff frequency
|
29
28
|
# @param bandwidth [Float] bandwidth (octave)
|
30
|
-
# @param gain [AudioStream::Decibel] Amplification level at cutoff frequency
|
29
|
+
# @param gain [AudioStream::Decibel | Float] Amplification level at cutoff frequency
|
31
30
|
def self.create(soundinfo, freq:, bandwidth: 1.0, gain: 40.0)
|
32
31
|
filter = new(soundinfo)
|
33
32
|
filter.update_coef(freq: freq, bandwidth: bandwidth, gain: gain)
|
@@ -3,11 +3,11 @@ module AudioStream
|
|
3
3
|
class Phaser
|
4
4
|
|
5
5
|
# @param soundinfo [AudioStream::SoundInfo]
|
6
|
-
# @param rate [AudioStream::Rate] modulation speed
|
6
|
+
# @param rate [AudioStream::Rate | Float] modulation speed. default unit: sec
|
7
7
|
# @param depth [Float] frequency modulation depth
|
8
8
|
# @param freq [Float] Base cutoff frequency
|
9
|
-
# @param dry [AudioStream::Decibel] dry gain
|
10
|
-
# @param wet [AudioStream::Decibel] wet gain
|
9
|
+
# @param dry [AudioStream::Decibel | Float] dry gain
|
10
|
+
# @param wet [AudioStream::Decibel | Float] wet gain
|
11
11
|
def initialize(soundinfo, rate:, depth:, freq:, dry: -6.0, wet: -6.0)
|
12
12
|
@soundinfo = soundinfo
|
13
13
|
|
@@ -16,14 +16,15 @@ module AudioStream
|
|
16
16
|
AllPassFilter.new(soundinfo),
|
17
17
|
]
|
18
18
|
|
19
|
+
rate = Rate.sec(rate)
|
19
20
|
@speed = rate.frame_phase(soundinfo)
|
20
21
|
@phase = 0
|
21
22
|
|
22
23
|
@depth = depth
|
23
24
|
@freq = freq
|
24
25
|
|
25
|
-
@dry = Decibel.
|
26
|
-
@wet = Decibel.
|
26
|
+
@dry = Decibel.db(dry).mag
|
27
|
+
@wet = Decibel.db(wet).mag
|
27
28
|
end
|
28
29
|
|
29
30
|
def process(input)
|
@@ -31,7 +32,7 @@ module AudioStream
|
|
31
32
|
@phase = (@phase + @speed) % (2.0 * Math::PI)
|
32
33
|
|
33
34
|
a = Math.sin(@phase) * 0.5 + 0.5
|
34
|
-
apf_freq = @freq * (1.0 + a * @depth)
|
35
|
+
apf_freq = Rate.freq(@freq * (1.0 + a * @depth))
|
35
36
|
|
36
37
|
wet = input
|
37
38
|
@filters.each {|filter|
|