Class: AudioStream::Buffer

Inherits:
Object
  • Object
show all
Defined in:
lib/audio_stream/buffer.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(stream0, stream1 = nil) ⇒ Buffer

Returns a new instance of Buffer.



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/audio_stream/buffer.rb', line 8

def initialize(stream0, stream1=nil)
  if Array===stream0
    stream0 = Vdsp::DoubleArray.create(stream0)
  end
  if Array===stream1
    stream1 = Vdsp::DoubleArray.create(stream1)
  end

  @stream0 = stream0
  @stream1 = stream1

  if !stream1
    @streams = [stream0]
    @channels = 1
    @window_size = stream0.size
  else
    @streams = [stream0, stream1]
    @channels = 2
    @window_size = stream0.size

    if stream0.size!=stream1.size
      raise Error, "stream size is not match: stream0.size=#{stream0.size}, stream1.size=#{stream1.size}"
    end
  end
end

Instance Attribute Details

#channelsObject (readonly)

Returns the value of attribute channels.



5
6
7
# File 'lib/audio_stream/buffer.rb', line 5

def channels
  @channels
end

#streamsObject (readonly)

Returns the value of attribute streams.



4
5
6
# File 'lib/audio_stream/buffer.rb', line 4

def streams
  @streams
end

#window_sizeObject (readonly)

Returns the value of attribute window_size.



6
7
8
# File 'lib/audio_stream/buffer.rb', line 6

def window_size
  @window_size
end

Class Method Details

.create(window_size, channels) ⇒ Object



192
193
194
195
196
197
198
199
# File 'lib/audio_stream/buffer.rb', line 192

def self.create(window_size, channels)
  case channels
  when 1
    create_mono(window_size)
  when 2
    create_stereo(window_size)
  end
end

.create_mono(window_size) ⇒ Object



201
202
203
204
# File 'lib/audio_stream/buffer.rb', line 201

def self.create_mono(window_size)
  stream0 = Vdsp::DoubleArray.new(window_size)
  new(stream0)
end

.create_stereo(window_size) ⇒ Object



206
207
208
209
210
# File 'lib/audio_stream/buffer.rb', line 206

def self.create_stereo(window_size)
  stream0 = Vdsp::DoubleArray.new(window_size)
  stream1 = Vdsp::DoubleArray.new(window_size)
  new(stream0, stream1)
end

.from_na(na) ⇒ Object



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/audio_stream/buffer.rb', line 212

def self.from_na(na)
  channels = na.shape[0]
  window_size = na.size / channels

  case na.typecode
  when NArray::SINT
    max = 0x7FFF.to_f
  when NArray::FLOAT
    max = 1.0
  end

  case channels
  when 1
    stream0 = []
    window_size.times {|i|
      stream0 << na[i].real / max
    }
    self.new(stream0)
  when 2
    stream0 = []
    stream1 = []
    window_size.times {|i|
      stream0 << na[i*2].real / max
      stream1 << na[(i*2)+1].real / max
    }
    self.new(stream0, stream1)
  end
end

.from_rabuffer(rabuf) ⇒ Object



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/audio_stream/buffer.rb', line 241

def self.from_rabuffer(rabuf)
  channels = rabuf.channels
  window_size = rabuf.size

  case channels
  when 1
    stream0 = rabuf.to_a
    while stream0.size<window_size
      stream0 << 0.0
    end
    self.new(stream0)
  when 2
    stream0 = Array.new(window_size, 0.0)
    stream1 = Array.new(window_size, 0.0)
    rabuf.each_with_index {|fa, i|
      stream0[i] = fa[0]
      stream1[i] = fa[1]
    }
    self.new(stream0, stream1)
  end
end

.merge(buffers, average: false) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/audio_stream/buffer.rb', line 87

def self.merge(buffers, average: false)
  buffers.each {|buf|
    unless Buffer===buf
      raise Error, "argument is not Buffer: #{buf}"
    end
  }

  if buffers.length==0
    raise Error, "argument is empty"
  elsif buffers.length==1
    return buffers[0]
  end

  dst = buffers.inject(:+)

  if average
    gain = AGain.new(level: Decibel.mag(1.0/buffers.length))
    dst = gain.process(dst)
  end

  dst
end

Instance Method Details

#+(other) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/audio_stream/buffer.rb', line 67

def +(other)
  if self.window_size!=other.window_size
    raise Error, "Buffer.window_size is not match: self.window_size=#{self.window_size} other.window_size=#{other.window_size}"
  end

  channels = [self.channels, other.channels].max
  case channels
  when 1
    stream0 = self.streams[0] + other.streams[0]
    self.class.new(stream0)
  when 2
    st_self = self.stereo
    st_other = other.stereo

    stream0 = st_self.streams[0] + st_other.streams[0]
    stream1 = st_self.streams[1] + st_other.streams[1]
    self.class.new(stream0, stream1)
  end
end

#fft_plot(samplerate = 44100, window = nil) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/audio_stream/buffer.rb', line 119

def fft_plot(samplerate=44100, window=nil)
  window ||= HanningWindow.instance

  na = window.process(self).to_float_na
  fft = FFTW3.fft(na, FFTW3::FORWARD) / na.length
  buf = Buffer.from_na(fft)

  xs = window_size.times.map{|i| i.to_f * samplerate / window_size}
  traces = buf.streams.map {|stream|
    {x: xs, y: stream}
  }

  Plotly::Plot.new(
    data: traces,
    xaxis: {title: 'Frequency (Hz)', type: 'log'}
  )
end

#mono(deep_copy: false) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/audio_stream/buffer.rb', line 51

def mono(deep_copy: false)
  case self.channels
  when 1
    if deep_copy
      self.class.new(@stream0.clone)
    else
      self
    end
  when 2
    mono_stream = window_size.times.map {|i|
      (@stream0[i] + @stream1[i]) * 0.5
    }
    self.class.new(mono_stream)
  end
end

#plotObject



110
111
112
113
114
115
116
117
# File 'lib/audio_stream/buffer.rb', line 110

def plot
  xs = window_size.times.to_a
  traces = @streams.map {|stream|
    {x: xs, y: stream}
  }

  Plotly::Plot.new(data: traces)
end

#stereo(deep_copy: false) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/audio_stream/buffer.rb', line 34

def stereo(deep_copy: false)
  case self.channels
  when 1
    if deep_copy
      self.class.new(@stream0.clone, @stream0.clone)
    else
      self.class.new(@stream0, @stream0)
    end
  when 2
    if deep_copy
      self.class.new(@stream0.clone, @stream1.clone)
    else
      self
    end
  end
end

#to_float_na(dst = nil, offset = 0) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/audio_stream/buffer.rb', line 137

def to_float_na(dst=nil, offset=0)
  if dst
    if channels!=dst.shape[0]
      raise Error, "channels is not match: buffer.channels=#{channels} na.shape[0]=#{dst.shape[0]}"
    end
    if dst.typecode!=NArray::FLOAT
      raise Error, "typecode is not match: na.typecode=#{dst.typecode}"
    end
  end
  na = dst || NArray.float(channels, window_size)

  case channels
  when 1
    na[(0+offset)...(window_size+offset)] = @stream0.to_a
  when 2
    na[window_size.times.map{|i| i*2+offset}] = @stream0.to_a
    na[window_size.times.map{|i| i*2+1+offset}] = @stream1.to_a
  end

  na
end

#to_rabufferObject



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/audio_stream/buffer.rb', line 173

def to_rabuffer
  rabuf = RubyAudio::Buffer.float(window_size, channels)

  case channels
  when 1
    @stream0.each_with_index {|v, i|
      rabuf[i] = v
    }
  when 2
    stream0 = @stream0.to_a
    stream1 = @stream1.to_a
    window_size.times {|i|
      rabuf[i] = [stream0[i], stream1[i]]
    }
  end

  rabuf
end

#to_sint_naObject



159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/audio_stream/buffer.rb', line 159

def to_sint_na
  na = NArray.sint(channels, window_size)

  case channels
  when 1
    na[0...window_size] = @stream0.map {|f| (f * 0x7FFF).round}
  when 2
    na[window_size.times.map{|i| i*2}] = @stream0.map {|f| (f * 0x7FFF).round}
    na[window_size.times.map{|i| i*2+1}] = @stream1.map {|f| (f * 0x7FFF).round}
  end

  na
end