audio_stream 1.5.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/examples/chorus.rb +1 -2
- data/examples/equalizer.ipynb +23 -23
- data/examples/example_options.rb +1 -1
- data/examples/lpf.rb +5 -5
- data/examples/tuner.rb +3 -3
- data/lib/audio_stream.rb +2 -1
- data/lib/audio_stream/audio_input_device.rb +1 -8
- data/lib/audio_stream/audio_input_file.rb +3 -4
- data/lib/audio_stream/audio_observable.rb +26 -0
- data/lib/audio_stream/audio_observable_fx.rb +1 -6
- data/lib/audio_stream/audio_observable_lambda.rb +20 -0
- data/lib/audio_stream/audio_observer_lambda.rb +19 -0
- data/lib/audio_stream/audio_output_device.rb +4 -7
- data/lib/audio_stream/audio_output_file.rb +1 -1
- data/lib/audio_stream/buffer.rb +191 -47
- data/lib/audio_stream/fx.rb +0 -2
- data/lib/audio_stream/fx/a_gain.rb +7 -13
- data/lib/audio_stream/fx/biquad_filter.rb +15 -25
- data/lib/audio_stream/fx/chorus.rb +25 -33
- data/lib/audio_stream/fx/compressor.rb +6 -22
- data/lib/audio_stream/fx/convolution_reverb.rb +30 -39
- data/lib/audio_stream/fx/delay.rb +25 -14
- data/lib/audio_stream/fx/distortion.rb +7 -24
- data/lib/audio_stream/fx/equalizer_2band.rb +5 -7
- data/lib/audio_stream/fx/equalizer_3band.rb +7 -9
- data/lib/audio_stream/fx/graphic_equalizer.rb +4 -6
- data/lib/audio_stream/fx/hanning_window.rb +8 -15
- data/lib/audio_stream/fx/high_pass_filter.rb +1 -3
- data/lib/audio_stream/fx/high_shelf_filter.rb +1 -3
- data/lib/audio_stream/fx/low_pass_filter.rb +1 -3
- data/lib/audio_stream/fx/low_shelf_filter.rb +1 -3
- data/lib/audio_stream/fx/noise_gate.rb +5 -23
- data/lib/audio_stream/fx/panning.rb +19 -13
- data/lib/audio_stream/fx/tremolo.rb +8 -16
- data/lib/audio_stream/fx/tuner.rb +12 -12
- data/lib/audio_stream/version.rb +1 -1
- metadata +4 -5
- data/lib/audio_stream/fx/mono_to_stereo.rb +0 -20
- data/lib/audio_stream/fx/stereo_to_mono.rb +0 -20
- data/lib/audio_stream/plot.rb +0 -53
@@ -1,16 +1,14 @@
|
|
1
1
|
module AudioStream
|
2
2
|
module Fx
|
3
3
|
class Equalizer2band
|
4
|
-
include BangProcess
|
5
|
-
|
6
4
|
def initialize(soundinfo, lowfreq: 400.0, lowgain:, highfreq: 4000.0, highgain:)
|
7
|
-
@low_filter = LowShelfFilter.create(soundinfo, freq: lowfreq, q:
|
8
|
-
@high_filter = HighShelfFilter.create(soundinfo, freq: highfreq, q:
|
5
|
+
@low_filter = LowShelfFilter.create(soundinfo, freq: lowfreq, q: BiquadFilter::DEFAULT_Q, gain: lowgain)
|
6
|
+
@high_filter = HighShelfFilter.create(soundinfo, freq: highfreq, q: BiquadFilter::DEFAULT_Q, gain: highgain)
|
9
7
|
end
|
10
8
|
|
11
|
-
def process
|
12
|
-
@low_filter.process
|
13
|
-
@high_filter.process
|
9
|
+
def process(input)
|
10
|
+
input = @low_filter.process(input)
|
11
|
+
input = @high_filter.process(input)
|
14
12
|
end
|
15
13
|
|
16
14
|
def plot(width=500)
|
@@ -1,18 +1,16 @@
|
|
1
1
|
module AudioStream
|
2
2
|
module Fx
|
3
3
|
class Equalizer3band
|
4
|
-
include BangProcess
|
5
|
-
|
6
4
|
def initialize(soundinfo, lowfreq: 400.0, lowgain:, midfreq: 1000.0, midgain:, highfreq: 4000.0, highgain:)
|
7
|
-
@low_filter = LowShelfFilter.create(soundinfo, freq: lowfreq, q:
|
8
|
-
@mid_filter = PeakingFilter.create(soundinfo, freq: midfreq, bandwidth: 1.0
|
9
|
-
@high_filter = HighShelfFilter.create(soundinfo, freq: highfreq, q:
|
5
|
+
@low_filter = LowShelfFilter.create(soundinfo, freq: lowfreq, q: BiquadFilter::DEFAULT_Q, gain: lowgain)
|
6
|
+
@mid_filter = PeakingFilter.create(soundinfo, freq: midfreq, bandwidth: 1.0, gain: midgain)
|
7
|
+
@high_filter = HighShelfFilter.create(soundinfo, freq: highfreq, q: BiquadFilter::DEFAULT_Q, gain: highgain)
|
10
8
|
end
|
11
9
|
|
12
|
-
def process
|
13
|
-
@low_filter.process
|
14
|
-
@mid_filter.process
|
15
|
-
@high_filter.process
|
10
|
+
def process(input)
|
11
|
+
input = @low_filter.process(input)
|
12
|
+
input = @mid_filter.process(input)
|
13
|
+
input = @high_filter.process(input)
|
16
14
|
end
|
17
15
|
|
18
16
|
def plot(width=500)
|
@@ -1,23 +1,21 @@
|
|
1
1
|
module AudioStream
|
2
2
|
module Fx
|
3
3
|
class GraphicEqualizer
|
4
|
-
include BangProcess
|
5
|
-
|
6
4
|
def initialize(soundinfo)
|
7
5
|
@soundinfo = soundinfo
|
8
6
|
@filters = []
|
9
7
|
end
|
10
8
|
|
11
|
-
def add(freq:, bandwidth:
|
12
|
-
bandwidth ||= 1.0/Math.sqrt(2.0)
|
9
|
+
def add(freq:, bandwidth: 1.0, gain:)
|
13
10
|
@filters << PeakingFilter.create(@soundinfo, freq: freq, bandwidth: bandwidth, gain: gain)
|
14
11
|
self
|
15
12
|
end
|
16
13
|
|
17
|
-
def process
|
14
|
+
def process(input)
|
18
15
|
@filters.each {|filter|
|
19
|
-
filter.process
|
16
|
+
input = filter.process(input)
|
20
17
|
}
|
18
|
+
input
|
21
19
|
end
|
22
20
|
|
23
21
|
def plot(width=500)
|
@@ -2,28 +2,21 @@ module AudioStream
|
|
2
2
|
module Fx
|
3
3
|
class HanningWindow
|
4
4
|
include Singleton
|
5
|
-
include BangProcess
|
6
5
|
|
7
|
-
def process
|
8
|
-
window_size = input.
|
9
|
-
window_max = input.
|
6
|
+
def process(input)
|
7
|
+
window_size = input.window_size
|
8
|
+
window_max = input.window_size - 1
|
10
9
|
channels = input.channels
|
11
10
|
|
12
11
|
period = 2 * Math::PI / window_max
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
input[i] *= 0.5 - 0.5 * Math.cos(i * period)
|
13
|
+
streams = input.streams.map {|stream|
|
14
|
+
stream.map.with_index {|f, i|
|
15
|
+
f * (0.5 - 0.5 * Math.cos(i * period))
|
18
16
|
}
|
19
|
-
|
20
|
-
input.each_with_index {|fa, i|
|
21
|
-
gain = 0.5 - 0.5 * Math.cos(i * period)
|
22
|
-
input[i] = fa.map {|f| f * gain}
|
23
|
-
}
|
24
|
-
end
|
17
|
+
}
|
25
18
|
|
26
|
-
|
19
|
+
Buffer.new(*streams)
|
27
20
|
end
|
28
21
|
end
|
29
22
|
end
|
@@ -16,9 +16,7 @@ module AudioStream
|
|
16
16
|
@filter_coef = FilterCoef.new(a0, a1, a2, b0, b1, b2)
|
17
17
|
end
|
18
18
|
|
19
|
-
def self.create(soundinfo, freq:, q:
|
20
|
-
q ||= 1.0 / Math.sqrt(2)
|
21
|
-
|
19
|
+
def self.create(soundinfo, freq:, q: DEFAULT_Q)
|
22
20
|
filter = new(soundinfo)
|
23
21
|
filter.update_coef(freq: freq, q: q)
|
24
22
|
|
@@ -18,9 +18,7 @@ module AudioStream
|
|
18
18
|
@filter_coef = FilterCoef.new(a0, a1, a2, b0, b1, b2)
|
19
19
|
end
|
20
20
|
|
21
|
-
def self.create(soundinfo, freq:, q:
|
22
|
-
q ||= 1.0 / Math.sqrt(2)
|
23
|
-
|
21
|
+
def self.create(soundinfo, freq:, q: DEFAULT_Q, gain: 1.0)
|
24
22
|
filter = new(soundinfo)
|
25
23
|
filter.update_coef(freq: freq, q: q, gain: gain)
|
26
24
|
|
@@ -16,9 +16,7 @@ module AudioStream
|
|
16
16
|
@filter_coef = FilterCoef.new(a0, a1, a2, b0, b1, b2)
|
17
17
|
end
|
18
18
|
|
19
|
-
def self.create(soundinfo, freq:, q:
|
20
|
-
q ||= 1.0 / Math.sqrt(2)
|
21
|
-
|
19
|
+
def self.create(soundinfo, freq:, q: DEFAULT_Q)
|
22
20
|
filter = new(soundinfo)
|
23
21
|
filter.update_coef(freq: freq, q: q)
|
24
22
|
|
@@ -18,9 +18,7 @@ module AudioStream
|
|
18
18
|
@filter_coef = FilterCoef.new(a0, a1, a2, b0, b1, b2)
|
19
19
|
end
|
20
20
|
|
21
|
-
def self.create(soundinfo, freq:, q:
|
22
|
-
q ||= 1.0 / Math.sqrt(2)
|
23
|
-
|
21
|
+
def self.create(soundinfo, freq:, q: DEFAULT_Q, gain: 1.0)
|
24
22
|
filter = new(soundinfo)
|
25
23
|
filter.update_coef(freq: freq, q: q, gain: gain)
|
26
24
|
|
@@ -1,21 +1,18 @@
|
|
1
1
|
module AudioStream
|
2
2
|
module Fx
|
3
3
|
class NoiseGate
|
4
|
-
include BangProcess
|
5
|
-
|
6
4
|
def initialize(threshold: 0.01)
|
7
5
|
@threshold = threshold
|
8
6
|
@window = HanningWindow.instance
|
9
7
|
end
|
10
8
|
|
11
|
-
def process
|
12
|
-
window_size = input.
|
9
|
+
def process(input)
|
10
|
+
window_size = input.window_size
|
13
11
|
channels = input.channels
|
14
12
|
|
15
13
|
# fft
|
16
|
-
@window.process
|
17
|
-
na =
|
18
|
-
na[0...na.size] = input.to_a.flatten
|
14
|
+
input = @window.process(input)
|
15
|
+
na = input.to_float_na
|
19
16
|
fft = FFTW3.fft(na, FFTW3::FORWARD) / na.length
|
20
17
|
|
21
18
|
fft.size.times {|i|
|
@@ -23,25 +20,10 @@ module AudioStream
|
|
23
20
|
fft[i] = 0i
|
24
21
|
end
|
25
22
|
}
|
26
|
-
|
27
23
|
wet_na = FFTW3.fft(fft, FFTW3::BACKWARD)
|
28
24
|
|
29
|
-
|
30
|
-
when 1
|
31
|
-
window_size.times {|i|
|
32
|
-
input[i] = wet_na[i].real
|
33
|
-
}
|
34
|
-
when 2
|
35
|
-
window_size.times {|i|
|
36
|
-
wet1 = wet_na[i*2].real
|
37
|
-
wet2 = wet_na[(i*2)+1].real
|
38
|
-
|
39
|
-
input[i] = [wet1, wet2]
|
40
|
-
}
|
41
|
-
end
|
25
|
+
Buffer.from_na(wet_na)
|
42
26
|
end
|
43
27
|
end
|
44
28
|
end
|
45
|
-
|
46
|
-
NArray.include Enumerable
|
47
29
|
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
module AudioStream
|
2
2
|
module Fx
|
3
3
|
class Panning
|
4
|
-
include BangProcess
|
5
|
-
|
6
4
|
def initialize(pan: 0.0)
|
7
5
|
@pan = pan
|
8
6
|
|
@@ -23,18 +21,26 @@ module AudioStream
|
|
23
21
|
@normalize = [1.0 - pan, 1.0 + pan].max
|
24
22
|
end
|
25
23
|
|
26
|
-
def process
|
27
|
-
return if @pan==0.0
|
24
|
+
def process(input)
|
25
|
+
return input if @pan==0.0
|
28
26
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
27
|
+
input = input.stereo
|
28
|
+
src = input.streams
|
29
|
+
src0 = src[0]
|
30
|
+
src1 = src[1]
|
31
|
+
|
32
|
+
output = Buffer.create_stereo(input.window_size)
|
33
|
+
dst = output.streams
|
34
|
+
dst0 = dst[0]
|
35
|
+
dst1 = dst[1]
|
36
|
+
|
37
|
+
input.window_size.times {|i|
|
38
|
+
l = (src0[i] * @l_gain + src1[i] * @lr_gain) / @normalize
|
39
|
+
r = (src1[i] * @r_gain + src0[i] * @rl_gain) / @normalize
|
40
|
+
dst0[i] = l
|
41
|
+
dst1[i] = r
|
42
|
+
}
|
43
|
+
output
|
38
44
|
end
|
39
45
|
end
|
40
46
|
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
module AudioStream
|
2
2
|
module Fx
|
3
3
|
class Tremolo
|
4
|
-
include BangProcess
|
5
|
-
|
6
4
|
def initialize(soundinfo, freq:, depth:)
|
7
5
|
@samplerate = soundinfo.samplerate
|
8
6
|
@freq = freq.to_f
|
@@ -10,24 +8,18 @@ module AudioStream
|
|
10
8
|
@phase = 0
|
11
9
|
end
|
12
10
|
|
13
|
-
def process
|
14
|
-
window_size = input.
|
15
|
-
channels = input.channels
|
16
|
-
|
11
|
+
def process(input)
|
12
|
+
window_size = input.window_size
|
17
13
|
period = 2 * Math::PI * @freq / @samplerate
|
18
14
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
input[i] *= 1.0 + @depth * Math.sin((i + @phase) * period)
|
15
|
+
streams = input.streams.map {|stream|
|
16
|
+
stream.map.with_index {|f, i|
|
17
|
+
f * (1.0 + @depth * Math.sin((i + @phase) * period))
|
23
18
|
}
|
24
|
-
|
25
|
-
input.each_with_index {|fa, i|
|
26
|
-
gain = 1.0 + @depth * Math.sin((i + @phase) * period)
|
27
|
-
input[i] = fa.map {|f| f * gain}
|
28
|
-
}
|
29
|
-
end
|
19
|
+
}
|
30
20
|
@phase = (@phase + window_size) % (window_size / period)
|
21
|
+
|
22
|
+
Buffer.new(*streams)
|
31
23
|
end
|
32
24
|
end
|
33
25
|
end
|
@@ -19,19 +19,19 @@ module AudioStream
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def process(input)
|
22
|
-
window_size = input.
|
22
|
+
window_size = input.window_size
|
23
23
|
|
24
24
|
# mono window
|
25
|
-
input =
|
26
|
-
@window.process
|
25
|
+
input = input.mono
|
26
|
+
input = @window.process(input)
|
27
|
+
stream = input.streams[0]
|
27
28
|
|
28
|
-
gain =
|
29
|
+
gain = stream.map(&:abs).max
|
29
30
|
freq = nil
|
30
31
|
|
31
32
|
if 0.01<gain
|
32
33
|
# fft
|
33
|
-
na =
|
34
|
-
na[0...na.size] = input.to_a
|
34
|
+
na = input.to_float_na
|
35
35
|
fft = FFTW3.fft(na, FFTW3::FORWARD) / na.length
|
36
36
|
|
37
37
|
amp = fft.map {|c|
|
@@ -41,12 +41,12 @@ module AudioStream
|
|
41
41
|
# peak
|
42
42
|
i = amp.index(amp.max)
|
43
43
|
|
44
|
-
if window_size/2<i
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
end
|
44
|
+
#if window_size/2<i
|
45
|
+
# j = window_size - i
|
46
|
+
# if (amp[i]-amp[j]).abs<=0.0000001
|
47
|
+
# i = j
|
48
|
+
# end
|
49
|
+
#end
|
50
50
|
|
51
51
|
# freq
|
52
52
|
freq_rate = @samplerate / window_size
|
data/lib/audio_stream/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: audio_stream
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.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-08-
|
11
|
+
date: 2019-08-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -142,7 +142,9 @@ files:
|
|
142
142
|
- lib/audio_stream/audio_notification.rb
|
143
143
|
- lib/audio_stream/audio_observable.rb
|
144
144
|
- lib/audio_stream/audio_observable_fx.rb
|
145
|
+
- lib/audio_stream/audio_observable_lambda.rb
|
145
146
|
- lib/audio_stream/audio_observer.rb
|
147
|
+
- lib/audio_stream/audio_observer_lambda.rb
|
146
148
|
- lib/audio_stream/audio_output.rb
|
147
149
|
- lib/audio_stream/audio_output_device.rb
|
148
150
|
- lib/audio_stream/audio_output_file.rb
|
@@ -168,14 +170,11 @@ files:
|
|
168
170
|
- lib/audio_stream/fx/high_shelf_filter.rb
|
169
171
|
- lib/audio_stream/fx/low_pass_filter.rb
|
170
172
|
- lib/audio_stream/fx/low_shelf_filter.rb
|
171
|
-
- lib/audio_stream/fx/mono_to_stereo.rb
|
172
173
|
- lib/audio_stream/fx/noise_gate.rb
|
173
174
|
- lib/audio_stream/fx/panning.rb
|
174
175
|
- lib/audio_stream/fx/peaking_filter.rb
|
175
|
-
- lib/audio_stream/fx/stereo_to_mono.rb
|
176
176
|
- lib/audio_stream/fx/tremolo.rb
|
177
177
|
- lib/audio_stream/fx/tuner.rb
|
178
|
-
- lib/audio_stream/plot.rb
|
179
178
|
- lib/audio_stream/ring_buffer.rb
|
180
179
|
- lib/audio_stream/sound_info.rb
|
181
180
|
- lib/audio_stream/sync.rb
|
@@ -1,20 +0,0 @@
|
|
1
|
-
module AudioStream
|
2
|
-
module Fx
|
3
|
-
class MonoToStereo
|
4
|
-
include Singleton
|
5
|
-
|
6
|
-
def process(input)
|
7
|
-
case input.channels
|
8
|
-
when 1
|
9
|
-
output = Buffer.float(input.size, 2)
|
10
|
-
input.each_with_index {|f, i|
|
11
|
-
output[i] = [f, f]
|
12
|
-
}
|
13
|
-
output
|
14
|
-
when 2
|
15
|
-
input.clone
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
module AudioStream
|
2
|
-
module Fx
|
3
|
-
class StereoToMono
|
4
|
-
include Singleton
|
5
|
-
|
6
|
-
def process(input)
|
7
|
-
case input.channels
|
8
|
-
when 1
|
9
|
-
input.clone
|
10
|
-
when 2
|
11
|
-
output = Buffer.float(input.size, 1)
|
12
|
-
input.each_with_index {|fa, i|
|
13
|
-
output[i] = fa.sum / 2.0
|
14
|
-
}
|
15
|
-
output
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
data/lib/audio_stream/plot.rb
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
module AudioStream
|
2
|
-
class Plot
|
3
|
-
def initialize(input, soundinfo=nil)
|
4
|
-
@input = input
|
5
|
-
if soundinfo
|
6
|
-
@samplerate = soundinfo.samplerate.to_f
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
def wave
|
11
|
-
buf_to_plot(@input)
|
12
|
-
end
|
13
|
-
|
14
|
-
def fft(window=nil)
|
15
|
-
window ||= HanningWindow.instance
|
16
|
-
|
17
|
-
na = window.process(@input).to_na
|
18
|
-
fft = FFTW3.fft(na, FFTW3::FORWARD) / na.length
|
19
|
-
|
20
|
-
buf = Buffer.from_na(fft)
|
21
|
-
buf_to_plot(buf, true)
|
22
|
-
end
|
23
|
-
|
24
|
-
def buf_to_plot(input, x_hz=false)
|
25
|
-
window_size = input.size
|
26
|
-
channels = input.channels
|
27
|
-
outputs = []
|
28
|
-
|
29
|
-
case channels
|
30
|
-
when 1
|
31
|
-
outputs << input.to_a
|
32
|
-
when 2
|
33
|
-
outputs << output_l = []
|
34
|
-
outputs << output_r = []
|
35
|
-
window_size.times {|i|
|
36
|
-
output_l << input[i][0]
|
37
|
-
output_r << input[i][1]
|
38
|
-
}
|
39
|
-
end
|
40
|
-
|
41
|
-
window_arr = window_size.times.to_a
|
42
|
-
if x_hz && @samplerate
|
43
|
-
window_arr.map!{|i| i * @samplerate / window_size}
|
44
|
-
end
|
45
|
-
|
46
|
-
traces = outputs.map {|output|
|
47
|
-
{x: window_arr, y: output}
|
48
|
-
}
|
49
|
-
|
50
|
-
Plotly::Plot.new(data: traces)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|