audio_stream 3.3.0 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +12 -13
- data/lib/audio_stream/fx/tremolo.rb +7 -5
- data/lib/audio_stream/rate.rb +89 -38
- 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|
|