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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/examples/chorus.rb +1 -2
  3. data/examples/equalizer.ipynb +23 -23
  4. data/examples/example_options.rb +1 -1
  5. data/examples/lpf.rb +5 -5
  6. data/examples/tuner.rb +3 -3
  7. data/lib/audio_stream.rb +2 -1
  8. data/lib/audio_stream/audio_input_device.rb +1 -8
  9. data/lib/audio_stream/audio_input_file.rb +3 -4
  10. data/lib/audio_stream/audio_observable.rb +26 -0
  11. data/lib/audio_stream/audio_observable_fx.rb +1 -6
  12. data/lib/audio_stream/audio_observable_lambda.rb +20 -0
  13. data/lib/audio_stream/audio_observer_lambda.rb +19 -0
  14. data/lib/audio_stream/audio_output_device.rb +4 -7
  15. data/lib/audio_stream/audio_output_file.rb +1 -1
  16. data/lib/audio_stream/buffer.rb +191 -47
  17. data/lib/audio_stream/fx.rb +0 -2
  18. data/lib/audio_stream/fx/a_gain.rb +7 -13
  19. data/lib/audio_stream/fx/biquad_filter.rb +15 -25
  20. data/lib/audio_stream/fx/chorus.rb +25 -33
  21. data/lib/audio_stream/fx/compressor.rb +6 -22
  22. data/lib/audio_stream/fx/convolution_reverb.rb +30 -39
  23. data/lib/audio_stream/fx/delay.rb +25 -14
  24. data/lib/audio_stream/fx/distortion.rb +7 -24
  25. data/lib/audio_stream/fx/equalizer_2band.rb +5 -7
  26. data/lib/audio_stream/fx/equalizer_3band.rb +7 -9
  27. data/lib/audio_stream/fx/graphic_equalizer.rb +4 -6
  28. data/lib/audio_stream/fx/hanning_window.rb +8 -15
  29. data/lib/audio_stream/fx/high_pass_filter.rb +1 -3
  30. data/lib/audio_stream/fx/high_shelf_filter.rb +1 -3
  31. data/lib/audio_stream/fx/low_pass_filter.rb +1 -3
  32. data/lib/audio_stream/fx/low_shelf_filter.rb +1 -3
  33. data/lib/audio_stream/fx/noise_gate.rb +5 -23
  34. data/lib/audio_stream/fx/panning.rb +19 -13
  35. data/lib/audio_stream/fx/tremolo.rb +8 -16
  36. data/lib/audio_stream/fx/tuner.rb +12 -12
  37. data/lib/audio_stream/version.rb +1 -1
  38. metadata +4 -5
  39. data/lib/audio_stream/fx/mono_to_stereo.rb +0 -20
  40. data/lib/audio_stream/fx/stereo_to_mono.rb +0 -20
  41. 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: 1.0/Math.sqrt(2.0), gain: lowgain)
8
- @high_filter = HighShelfFilter.create(soundinfo, freq: highfreq, q: 1.0/Math.sqrt(2.0), gain: highgain)
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!(input)
12
- @low_filter.process!(input)
13
- @high_filter.process!(input)
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: 1.0/Math.sqrt(2.0), gain: lowgain)
8
- @mid_filter = PeakingFilter.create(soundinfo, freq: midfreq, bandwidth: 1.0/Math.sqrt(2.0), gain: midgain)
9
- @high_filter = HighShelfFilter.create(soundinfo, freq: highfreq, q: 1.0/Math.sqrt(2.0), gain: highgain)
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!(input)
13
- @low_filter.process!(input)
14
- @mid_filter.process!(input)
15
- @high_filter.process!(input)
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: nil, gain:)
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!(input)
14
+ def process(input)
18
15
  @filters.each {|filter|
19
- filter.process!(input)
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!(input)
8
- window_size = input.size
9
- window_max = input.size - 1
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
- case channels
15
- when 1
16
- input.each_with_index {|f, i|
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
- when 2
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
- input
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: nil)
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: nil, gain: 1.0)
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: nil)
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: nil, gain: 1.0)
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!(input)
12
- window_size = input.size
9
+ def process(input)
10
+ window_size = input.window_size
13
11
  channels = input.channels
14
12
 
15
13
  # fft
16
- @window.process!(input)
17
- na = NArray.float(channels, window_size)
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
- case channels
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!(input)
27
- return if @pan==0.0
24
+ def process(input)
25
+ return input if @pan==0.0
28
26
 
29
- case input.channels
30
- when 1
31
- when 2
32
- input.each_with_index {|fa, i|
33
- l = (fa[0] * @l_gain + fa[1] * @lr_gain) / @normalize
34
- r = (fa[1] * @r_gain + fa[0] * @rl_gain) / @normalize
35
- input[i] = [l, r]
36
- }
37
- end
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!(input)
14
- window_size = input.size
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
- case channels
20
- when 1
21
- input.each_with_index {|f, i|
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
- when 2
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.size
22
+ window_size = input.window_size
23
23
 
24
24
  # mono window
25
- input = StereoToMono.instance.process(input)
26
- @window.process!(input)
25
+ input = input.mono
26
+ input = @window.process(input)
27
+ stream = input.streams[0]
27
28
 
28
- gain = input.to_a.flatten.max
29
+ gain = stream.map(&:abs).max
29
30
  freq = nil
30
31
 
31
32
  if 0.01<gain
32
33
  # fft
33
- na = NArray.float(1, window_size)
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
- j = window_size - i
46
- if (amp[i]-amp[j]).abs<=0.0000001
47
- i = j
48
- end
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
@@ -1,3 +1,3 @@
1
1
  module AudioStream
2
- VERSION = "1.5.0"
2
+ VERSION = "2.0.0"
3
3
  end
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: 1.5.0
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-04 00:00:00.000000000 Z
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
@@ -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