audio_stream 1.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 (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.travis.yml +7 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +39 -0
  7. data/Rakefile +10 -0
  8. data/audio_stream.gemspec +38 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +8 -0
  11. data/examples/adsr.ipynb +88 -0
  12. data/examples/buffer.ipynb +160 -0
  13. data/examples/chorus.rb +41 -0
  14. data/examples/curves.ipynb +105 -0
  15. data/examples/distortion.rb +43 -0
  16. data/examples/example_options.rb +56 -0
  17. data/examples/rec.rb +53 -0
  18. data/examples/shapes.ipynb +116 -0
  19. data/examples/sin.rb +62 -0
  20. data/examples/tremolo.rb +38 -0
  21. data/examples/tuner.rb +77 -0
  22. data/lib/audio_stream.rb +30 -0
  23. data/lib/audio_stream/audio_bus.rb +28 -0
  24. data/lib/audio_stream/audio_input.rb +48 -0
  25. data/lib/audio_stream/audio_input_buffer.rb +21 -0
  26. data/lib/audio_stream/audio_input_device.rb +50 -0
  27. data/lib/audio_stream/audio_input_file.rb +30 -0
  28. data/lib/audio_stream/audio_input_metronome.rb +49 -0
  29. data/lib/audio_stream/audio_input_sin.rb +41 -0
  30. data/lib/audio_stream/audio_output.rb +17 -0
  31. data/lib/audio_stream/audio_output_device.rb +65 -0
  32. data/lib/audio_stream/audio_output_file.rb +35 -0
  33. data/lib/audio_stream/buffer.rb +84 -0
  34. data/lib/audio_stream/conductor.rb +38 -0
  35. data/lib/audio_stream/core_ext.rb +13 -0
  36. data/lib/audio_stream/error.rb +4 -0
  37. data/lib/audio_stream/fx.rb +30 -0
  38. data/lib/audio_stream/fx/a_gain.rb +26 -0
  39. data/lib/audio_stream/fx/band_pass_filter.rb +29 -0
  40. data/lib/audio_stream/fx/bang_process.rb +11 -0
  41. data/lib/audio_stream/fx/biquad_filter.rb +58 -0
  42. data/lib/audio_stream/fx/chorus.rb +59 -0
  43. data/lib/audio_stream/fx/compressor.rb +41 -0
  44. data/lib/audio_stream/fx/convolution_reverb.rb +107 -0
  45. data/lib/audio_stream/fx/delay.rb +45 -0
  46. data/lib/audio_stream/fx/distortion.rb +42 -0
  47. data/lib/audio_stream/fx/equalizer_2band.rb +23 -0
  48. data/lib/audio_stream/fx/equalizer_3band.rb +28 -0
  49. data/lib/audio_stream/fx/hanning_window.rb +29 -0
  50. data/lib/audio_stream/fx/high_pass_filter.rb +29 -0
  51. data/lib/audio_stream/fx/high_shelf_filter.rb +32 -0
  52. data/lib/audio_stream/fx/low_pass_filter.rb +29 -0
  53. data/lib/audio_stream/fx/low_shelf_filter.rb +32 -0
  54. data/lib/audio_stream/fx/mono_to_stereo.rb +18 -0
  55. data/lib/audio_stream/fx/noise_gate.rb +47 -0
  56. data/lib/audio_stream/fx/panning.rb +41 -0
  57. data/lib/audio_stream/fx/peaking_filter.rb +31 -0
  58. data/lib/audio_stream/fx/stereo_to_mono.rb +18 -0
  59. data/lib/audio_stream/fx/tremolo.rb +34 -0
  60. data/lib/audio_stream/fx/tuner.rb +98 -0
  61. data/lib/audio_stream/plot.rb +53 -0
  62. data/lib/audio_stream/ring_buffer.rb +39 -0
  63. data/lib/audio_stream/sound_info.rb +9 -0
  64. data/lib/audio_stream/sync.rb +32 -0
  65. data/lib/audio_stream/utils.rb +45 -0
  66. data/lib/audio_stream/version.rb +3 -0
  67. metadata +223 -0
@@ -0,0 +1,35 @@
1
+ module AudioStream
2
+ class AudioOutputFile < AudioOutput
3
+ def initialize(fname, soundinfo:)
4
+ super()
5
+ @fname = fname
6
+ @soundinfo = soundinfo
7
+ end
8
+
9
+ def connect
10
+ @sound = RubyAudio::Sound.open(@fname, "w", @soundinfo)
11
+ end
12
+
13
+ def disconnect
14
+ end
15
+
16
+ def join
17
+ @sync.yield_wait
18
+ end
19
+
20
+ def on_next(input)
21
+ @sound.write(input)
22
+ end
23
+
24
+ def on_error(error)
25
+ puts error
26
+ puts error.backtrace.join("\n")
27
+ @sound.close
28
+ end
29
+
30
+ def on_completed
31
+ @sound.close
32
+ @sync.finish
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,84 @@
1
+ module AudioStream
2
+ class Buffer < RubyAudio::Buffer
3
+ def plot(soundinfo=nil)
4
+ Plot.new(self, soundinfo)
5
+ end
6
+
7
+ def +(other)
8
+ unless RubyAudio::Buffer===other
9
+ raise Error, "right operand is not Buffer: #{other}"
10
+ end
11
+ if self.size!=other.size
12
+ raise Error, "Buffer.size is not match: self.size=#{self.size} other.size=#{other.size}"
13
+ end
14
+
15
+ channels = [self.channels, other.channels].max
16
+ window_size = self.size
17
+
18
+ buf = Buffer.float(window_size, channels)
19
+
20
+ case channels
21
+ when 1
22
+ [self, other].each {|x|
23
+ x.size.times.each {|i|
24
+ buf[i] += x[i]
25
+ }
26
+ }
27
+ when 2
28
+ m2s = Fx::MonoToStereo.new
29
+ a = [
30
+ m2s.process(self),
31
+ m2s.process(other),
32
+ ]
33
+ a.each {|x|
34
+ x.size.times.each {|i|
35
+ buf[i] = buf[i].zip(x[i]).map {|a| a[0] + a[1]}
36
+ }
37
+ }
38
+ end
39
+
40
+ buf
41
+ end
42
+
43
+ def to_na
44
+ window_size = self.size
45
+ channels = self.channels
46
+
47
+ na = NArray.float(channels, window_size)
48
+ na[0...na.size] = self.to_a.flatten
49
+
50
+ na
51
+ end
52
+
53
+ def self.from_na(na)
54
+ channels = na.shape[0]
55
+ window_size = na.size / channels
56
+
57
+ buf = self.float(window_size, channels)
58
+
59
+ case channels
60
+ when 1
61
+ window_size.times {|i|
62
+ buf[i] = na[i].real
63
+ }
64
+ when 2
65
+ window_size.times {|i|
66
+ ch1 = na[i*2].real
67
+ ch2 = na[(i*2)+1].real
68
+
69
+ buf[i] = [ch1, ch2]
70
+ }
71
+ end
72
+
73
+ buf
74
+ end
75
+
76
+ [:short, :int, :float, :double].each do |type|
77
+ eval "def self.#{type}(frames, channels=1)
78
+ buf = self.new(:#{type}, frames, channels)
79
+ buf.real_size = buf.size
80
+ buf
81
+ end"
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,38 @@
1
+ module AudioStream
2
+ class Conductor
3
+ def initialize(input:, output:)
4
+ @inputs = Set[*[input].flatten.compact]
5
+ @outputs = Set[*[output].flatten.compact]
6
+ end
7
+
8
+ def connect
9
+ @outputs.map(&:connect)
10
+ @input_connections = @inputs.map(&:connect)
11
+
12
+ @sync_thread = Thread.start {
13
+ loop {
14
+ @inputs.each {|t|
15
+ t.sync.resume
16
+ }
17
+
18
+ @inputs.each {|t|
19
+ stat = t.sync.yield_wait
20
+ if stat==Sync::COMPLETED
21
+ @inputs.delete(t)
22
+ end
23
+ }
24
+
25
+ if @inputs.length==0
26
+ break
27
+ end
28
+ }
29
+ }
30
+ end
31
+
32
+ def join
33
+ @outputs.map(&:join)
34
+ @input_connections.map(&:join)
35
+ @sync_thread.join
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,13 @@
1
+ require 'audio_stream'
2
+
3
+ module Rx::Observable
4
+ def fx(effector)
5
+ #map(&effector.:process)
6
+ map(&effector.method(:process))
7
+ end
8
+
9
+ def send_to(bus, gain: nil, pan: nil)
10
+ bus.add(self, gain: gain, pan: pan)
11
+ self
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ module AudioStream
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -0,0 +1,30 @@
1
+ require 'audio_stream/fx/bang_process'
2
+
3
+ require 'audio_stream/fx/a_gain'
4
+ require 'audio_stream/fx/panning'
5
+ require 'audio_stream/fx/distortion'
6
+ require 'audio_stream/fx/noise_gate'
7
+ require 'audio_stream/fx/compressor'
8
+ require 'audio_stream/fx/biquad_filter'
9
+ require 'audio_stream/fx/low_pass_filter'
10
+ require 'audio_stream/fx/high_pass_filter'
11
+ require 'audio_stream/fx/band_pass_filter'
12
+ require 'audio_stream/fx/low_shelf_filter'
13
+ require 'audio_stream/fx/high_shelf_filter'
14
+ require 'audio_stream/fx/peaking_filter'
15
+ require 'audio_stream/fx/equalizer_2band'
16
+ require 'audio_stream/fx/equalizer_3band'
17
+ require 'audio_stream/fx/tremolo'
18
+ require 'audio_stream/fx/delay'
19
+ require 'audio_stream/fx/chorus'
20
+ require 'audio_stream/fx/convolution_reverb'
21
+ require 'audio_stream/fx/hanning_window'
22
+
23
+ require 'audio_stream/fx/stereo_to_mono'
24
+ require 'audio_stream/fx/mono_to_stereo'
25
+ require 'audio_stream/fx/tuner'
26
+
27
+ module AudioStream
28
+ module Fx
29
+ end
30
+ end
@@ -0,0 +1,26 @@
1
+ module AudioStream
2
+ module Fx
3
+ class AGain
4
+ include BangProcess
5
+
6
+ def initialize(level: 1.0)
7
+ @level = level
8
+ end
9
+
10
+ def process!(input)
11
+ return if @level==1.0
12
+
13
+ case input.channels
14
+ when 1
15
+ input.each_with_index {|f, i|
16
+ input[i] = f * @level
17
+ }
18
+ when 2
19
+ input.each_with_index {|fa, i|
20
+ input[i] = fa.map {|f| f * @level}
21
+ }
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ module AudioStream
2
+ module Fx
3
+ class BandPassFilter < BiquadFilter
4
+
5
+ def initialize(soundinfo, freq:, bandwidth: 1.0)
6
+ super()
7
+ @samplerate = soundinfo.samplerate.to_f
8
+ @freq = freq
9
+ @bandwidth = bandwidth
10
+
11
+ filter_coef
12
+ end
13
+
14
+ def filter_coef
15
+ omega = 2.0 * Math::PI * @freq / @samplerate
16
+ alpha = Math.sin(omega) * Math.sinh(Math.log(2.0) / 2.0 * @bandwidth * omega / Math.sin(omega))
17
+
18
+ a0 = 1.0 + alpha
19
+ a1 = -2.0 * Math.cos(omega)
20
+ a2 = 1.0 - alpha
21
+ b0 = alpha
22
+ b1 = 0.0
23
+ b2 = -alpha
24
+
25
+ @filter_coef = FilterCoef.new(a0, a1, a2, b0, b1, b2)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ module AudioStream
2
+ module Fx
3
+ module BangProcess
4
+ def process(input)
5
+ output = input.clone
6
+ process!(output)
7
+ output
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,58 @@
1
+ module AudioStream
2
+ module Fx
3
+ class BiquadFilter
4
+ include BangProcess
5
+
6
+ FilterBuffer = Struct.new("FilterBuffer", :in1, :in2, :out1, :out2) do
7
+ def self.create
8
+ new(0.0, 0.0, 0.0, 0.0)
9
+ end
10
+ end
11
+
12
+ FilterCoef = Struct.new("FilterCoef", :a0, :a1, :a2, :b0, :b1, :b2)
13
+
14
+ def initialize
15
+ @filter_bufs = [FilterBuffer.create, FilterBuffer.create]
16
+ end
17
+
18
+ def filter_coef
19
+ raise Error, "#{self.class.name}.filter_coef is not implemented"
20
+ end
21
+
22
+ def process!(input)
23
+ window_size = input.size
24
+ channels = input.channels
25
+
26
+ case channels
27
+ when 1
28
+ b = @filter_bufs[0]
29
+ window_size.times {|i|
30
+ input[i] = process_one(input[i], b)
31
+ }
32
+ when 2
33
+ window_size.times {|i|
34
+ input[i] = channels.times.map {|j|
35
+ b = @filter_bufs[j]
36
+ in0 = input[i][j]
37
+ process_one(in0, b)
38
+ }
39
+ }
40
+ end
41
+
42
+ input
43
+ end
44
+
45
+ def process_one(in0, b)
46
+ c = @filter_coef
47
+ out0 = c.b0/c.a0 * in0 + c.b1/c.a0 * b.in1 + c.b2/c.a0 * b.in2 - c.a1/c.a0 * b.out1 - c.a2/c.a0 * b.out2
48
+
49
+ b.in2 = b.in1
50
+ b.in1 = in0
51
+ b.out2 = b.out1
52
+ b.out1 = out0
53
+
54
+ out0
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,59 @@
1
+ module AudioStream
2
+ module Fx
3
+ class Chorus
4
+ include BangProcess
5
+
6
+ def initialize(soundinfo, depth: 100, rate: 0.25)
7
+ @soundinfo = soundinfo
8
+
9
+ @depth = depth
10
+ @rate = rate
11
+
12
+ @delaybuf0 = RingBuffer.new(@depth * 3, 0.0)
13
+ @delaybuf1 = RingBuffer.new(@depth * 3, 0.0)
14
+
15
+ @phase = 0
16
+ @speed = (2.0 * Math::PI * @rate) / @soundinfo.samplerate
17
+ end
18
+
19
+ def process!(input)
20
+ window_size = input.size
21
+ channels = input.channels
22
+
23
+ window_size.times {|i|
24
+ tau = @depth * (Math.sin(@speed * (@phase + i)) + 1)
25
+ t = i - tau
26
+
27
+ m = t.floor
28
+ delta = t - m
29
+
30
+ case channels
31
+ when 1
32
+ wet = delta * @delaybuf0[i-m+1] + (1.0 - delta) * @delaybuf0[i-m]
33
+ input[i] = (input[i] + wet) * 0.5
34
+ when 2
35
+ wet0 = delta * @delaybuf0[i-m+1] + (1.0 - delta) * @delaybuf0[i-m]
36
+ wet1 = delta * @delaybuf1[i-m+1] + (1.0 - delta) * @delaybuf1[i-m]
37
+ input[i] = [(input[i][0] + wet0) * 0.5, (input[i][1] + wet1) * 0.5]
38
+ end
39
+
40
+ case channels
41
+ when 1
42
+ @delaybuf0.current = input[i]
43
+ @delaybuf0.rotate
44
+ when 2
45
+ @delaybuf0.current = input[i][0]
46
+ @delaybuf1.current = input[i][1]
47
+ @delaybuf0.rotate
48
+ @delaybuf1.rotate
49
+ end
50
+ }
51
+ @phase = (@phase + window_size) % (window_size / @speed)
52
+ end
53
+
54
+ def lerp(start, stop, step)
55
+ (stop * step) + (start * (1.0 - step))
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,41 @@
1
+ module AudioStream
2
+ module Fx
3
+ class Compressor
4
+ include BangProcess
5
+
6
+ def initialize(threshold: 0.5, ratio: 0.5)
7
+ @threshold = threshold
8
+ @ratio = ratio
9
+ @zoom = 1.0 / (@ratio * (1.0 - @threshold) + @threshold)
10
+ end
11
+
12
+ def process!(input)
13
+ window_size = input.size
14
+ channels = input.channels
15
+
16
+ case channels
17
+ when 1
18
+ input.each_with_index {|f, i|
19
+ sign = f.negative? ? -1 : 1
20
+ f = f.abs
21
+ if @threshold<f
22
+ f = (f - @threshold) * @ratio + @threshold
23
+ end
24
+ input[i] = @zoom * f * sign
25
+ }
26
+ when 2
27
+ input.each_with_index {|fa, i|
28
+ input[i] = fa.map {|f|
29
+ sign = f.negative? ? -1 : 1
30
+ f = f.abs
31
+ if @threshold<f
32
+ f = (f - @threshold) * @ratio + @threshold
33
+ end
34
+ @zoom * f * sign
35
+ }
36
+ }
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end