audio_stream 3.3.3 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/examples/bench_fx.rb +89 -0
  4. data/examples/canon.wav +0 -0
  5. data/examples/example_options.rb +2 -0
  6. data/examples/{chorus.rb → fx_chorus.rb} +2 -7
  7. data/examples/fx_compressor.rb +34 -0
  8. data/examples/fx_convolution_reverb.rb +37 -0
  9. data/examples/fx_delay.rb +33 -0
  10. data/examples/{distortion.rb → fx_distortion.rb} +4 -8
  11. data/examples/fx_noise_gate.rb +33 -0
  12. data/examples/fx_phaser.rb +36 -0
  13. data/examples/fx_schroeder_reverb.rb +34 -0
  14. data/examples/fx_vocoder.rb +44 -0
  15. data/examples/impulse_shaker.wav +0 -0
  16. data/examples/impulse_shaker_bigroom.wav +0 -0
  17. data/examples/impulse_shaker_smallhall.wav +0 -0
  18. data/examples/tuner.rb +4 -3
  19. data/jupyter/band_pass_filter.ipynb +117 -0
  20. data/jupyter/biquad.ipynb +372 -0
  21. data/jupyter/equalizer.ipynb +198 -0
  22. data/lib/audio_stream/audio_input_device.rb +20 -3
  23. data/lib/audio_stream/audio_observable.rb +5 -0
  24. data/lib/audio_stream/audio_output_device.rb +20 -3
  25. data/lib/audio_stream/buffer.rb +22 -0
  26. data/lib/audio_stream/decibel.rb +8 -8
  27. data/lib/audio_stream/fx/a_gain.rb +2 -6
  28. data/lib/audio_stream/fx/all_pass_filter.rb +4 -2
  29. data/lib/audio_stream/fx/band_pass_filter.rb +4 -2
  30. data/lib/audio_stream/fx/biquad_filter.rb +8 -3
  31. data/lib/audio_stream/fx/chorus.rb +6 -5
  32. data/lib/audio_stream/fx/comb_filter.rb +4 -2
  33. data/lib/audio_stream/fx/convolution_reverb.rb +8 -5
  34. data/lib/audio_stream/fx/delay.rb +6 -5
  35. data/lib/audio_stream/fx/distortion.rb +4 -4
  36. data/lib/audio_stream/fx/equalizer_2band.rb +5 -3
  37. data/lib/audio_stream/fx/equalizer_3band.rb +7 -0
  38. data/lib/audio_stream/fx/graphic_equalizer.rb +4 -0
  39. data/lib/audio_stream/fx/hanning_window.rb +0 -9
  40. data/lib/audio_stream/fx/high_pass_filter.rb +4 -2
  41. data/lib/audio_stream/fx/high_shelf_filter.rb +6 -7
  42. data/lib/audio_stream/fx/low_pass_filter.rb +4 -2
  43. data/lib/audio_stream/fx/low_shelf_filter.rb +6 -7
  44. data/lib/audio_stream/fx/noise_gate.rb +5 -4
  45. data/lib/audio_stream/fx/panning.rb +2 -1
  46. data/lib/audio_stream/fx/peaking_filter.rb +6 -7
  47. data/lib/audio_stream/fx/phaser.rb +7 -6
  48. data/lib/audio_stream/fx/schroeder_reverb.rb +9 -10
  49. data/lib/audio_stream/fx/tremolo.rb +7 -5
  50. data/lib/audio_stream/rate.rb +30 -5
  51. data/lib/audio_stream/version.rb +1 -1
  52. metadata +23 -10
  53. data/examples/biquad.ipynb +0 -428
  54. 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
- CoreAudio.devices
56
- .select{|dev|
57
- 0<dev.input_stream.channels
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
- CoreAudio.devices({nominal_rate: soundinfo.samplerate})
50
- .select{|dev|
51
- 0<dev.output_stream.channels
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
@@ -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
@@ -14,18 +14,18 @@ module AudioStream
14
14
  end
15
15
 
16
16
  def self.db(db)
17
- new(db: db)
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
- new(mag: mag)
22
- end
23
-
24
- def self.create(val)
25
- if self===val
26
- val
25
+ if self===mag
26
+ mag
27
27
  else
28
- new(db: val.to_f)
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
- if Decibel===level
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
- omega = 2.0 * Math::PI * freq / @samplerate
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
- omega = 2.0 * Math::PI * freq / @samplerate
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
- @samplerate = soundinfo.samplerate.to_f
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
- def initialize(soundinfo, depth: 100, rate: 0.25)
5
- @soundinfo = soundinfo
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 = (2.0 * Math::PI * @rate) / @soundinfo.samplerate
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
- def initialize(impulse, dry: 0.5, wet: 0.5)
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
- @level = Decibel.create(level).mag
10
- @feedback = Decibel.create(feedback).mag
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.create(gain).mag
8
- @level = Decibel.create(level).mag
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 freq [Float] Cutoff frequency
5
- # @param q [Float] Quality factor
6
- # @param gain [AudioStream::Decibel] Amplification level at cutoff frequency
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
- omega = 2.0 * Math::PI * freq / @samplerate
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
- if Decibel===gain
7
- gain = gain.db
8
- end
6
+ freq = Rate.freq(freq)
7
+ gain = Decibel.db(gain)
9
8
 
10
- omega = 2.0 * Math::PI * freq / @samplerate
9
+ omega = freq.sample_phase(@soundinfo)
11
10
  alpha = Math.sin(omega) / (2.0 * q)
12
- a = 10.0 ** (gain / 40.0)
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
- omega = 2.0 * Math::PI * freq / @samplerate
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
- if Decibel===gain
7
- gain = gain.db
8
- end
6
+ freq = Rate.freq(freq)
7
+ gain = Decibel.db(gain)
9
8
 
10
- omega = 2.0 * Math::PI * freq / @samplerate
9
+ omega = freq.sample_phase(@soundinfo)
11
10
  alpha = Math.sin(omega) / (2.0 * q)
12
- a = 10.0 ** (gain / 40.0)
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
- input = @window.process(input)
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 < @threshold
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
- Buffer.from_na(wet_na)
26
+ input - noise
26
27
  end
27
28
  end
28
29
  end
@@ -1,8 +1,9 @@
1
1
  module AudioStream
2
2
  module Fx
3
3
  class Panning
4
+ # @param pan [Float] panning (-1.0~1.0) -1.0=left, 0.0=center, 1.0=right
4
5
  def initialize(pan: 0.0)
5
- @pan = pan
6
+ @pan = pan.to_f
6
7
 
7
8
  l_gain = 1.0 - pan
8
9
  lr_gain = 0.0
@@ -3,13 +3,12 @@ module AudioStream
3
3
  class PeakingFilter < BiquadFilter
4
4
 
5
5
  def update_coef(freq:, bandwidth:, gain:)
6
- if Decibel===gain
7
- gain = gain.db
8
- end
6
+ freq = Rate.freq(freq)
7
+ gain = Decibel.db(gain)
9
8
 
10
- omega = 2.0 * Math::PI * freq / @samplerate
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 = 10.0 ** (gain / 40.0)
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.create(dry).mag
26
- @wet = Decibel.create(wet).mag
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|