audio_stream 3.3.3 → 3.4.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 (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|