audio_stream 1.5.0 → 2.0.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/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
|