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.
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