Class: Deftones::Core::AudioBlock

Inherits:
Object
  • Object
show all
Defined in:
lib/deftones/core/audio_block.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(channel_data) ⇒ AudioBlock

Returns a new instance of AudioBlock.



39
40
41
# File 'lib/deftones/core/audio_block.rb', line 39

def initialize(channel_data)
  @channel_data = channel_data
end

Instance Attribute Details

#channel_dataObject (readonly)

Returns the value of attribute channel_data.



6
7
8
# File 'lib/deftones/core/audio_block.rb', line 6

def channel_data
  @channel_data
end

Class Method Details

.from_channel_data(channel_data) ⇒ Object



12
13
14
15
# File 'lib/deftones/core/audio_block.rb', line 12

def self.from_channel_data(channel_data)
  normalized = channel_data.map { |channel| channel.map(&:to_f) }
  new(normalized)
end

.from_interleaved(samples, channels:) ⇒ Object



22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/deftones/core/audio_block.rb', line 22

def self.from_interleaved(samples, channels:)
  channel_count = [channels.to_i, 1].max
  normalized = Array(samples).map(&:to_f)
  frame_count = (normalized.length.to_f / channel_count).ceil
  from_channel_data(
    Array.new(channel_count) do |channel_index|
      Array.new(frame_count) do |frame_index|
        normalized[(frame_index * channel_count) + channel_index] || 0.0
      end
    end
  )
end

.from_mono(samples, channels: 1) ⇒ Object



17
18
19
20
# File 'lib/deftones/core/audio_block.rb', line 17

def self.from_mono(samples, channels: 1)
  normalized = samples.map(&:to_f)
  from_channel_data(Array.new([channels.to_i, 1].max) { normalized.dup })
end

.from_packed_float32(payload, channels:) ⇒ Object



35
36
37
# File 'lib/deftones/core/audio_block.rb', line 35

def self.from_packed_float32(payload, channels:)
  from_interleaved(payload.unpack("e*"), channels: channels)
end

.silent(num_frames, channels = 1) ⇒ Object



8
9
10
# File 'lib/deftones/core/audio_block.rb', line 8

def self.silent(num_frames, channels = 1)
  from_channel_data(Array.new([channels.to_i, 1].max) { Array.new(num_frames.to_i, 0.0) })
end

Instance Method Details

#apply_headroom(sample, policy) ⇒ Object (private)



131
132
133
134
135
136
137
138
# File 'lib/deftones/core/audio_block.rb', line 131

def apply_headroom(sample, policy)
  case policy
  when :sum then sample
  when :clamp then sample.clamp(-1.0, 1.0)
  else
    raise ArgumentError, "Unsupported headroom policy: #{policy}"
  end
end

#channel(index) ⇒ Object



80
81
82
# File 'lib/deftones/core/audio_block.rb', line 80

def channel(index)
  @channel_data[index] || Array.new(num_frames, 0.0)
end

#channelsObject



43
44
45
# File 'lib/deftones/core/audio_block.rb', line 43

def channels
  @channel_data.length
end

#downmixed_channel(policy) ⇒ Object (private)



109
110
111
112
113
114
115
116
117
118
119
# File 'lib/deftones/core/audio_block.rb', line 109

def downmixed_channel(policy)
  case policy
  when :average then mono
  when :sum
    Array.new(num_frames) { |frame_index| @channel_data.sum { |channel| channel[frame_index] } }
  when :first
    channel(0).dup
  else
    raise ArgumentError, "Unsupported downmix policy: #{policy}"
  end
end

#dupObject



51
52
53
# File 'lib/deftones/core/audio_block.rb', line 51

def dup
  self.class.from_channel_data(@channel_data)
end

#fit_channels(target_channels, downmix: :average, upmix: :wrap) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
# File 'lib/deftones/core/audio_block.rb', line 84

def fit_channels(target_channels, downmix: :average, upmix: :wrap)
  target = [target_channels.to_i, 1].max
  return dup if target == channels
  return self.class.from_channel_data([downmixed_channel(downmix)]) if target == 1

  if channels == 1
    return self.class.from_channel_data(Array.new(target) { @channel_data.first.dup })
  end

  self.class.from_channel_data(Array.new(target) { |index| upmixed_channel(index, upmix) })
end

#interleavedObject



64
65
66
67
68
69
70
# File 'lib/deftones/core/audio_block.rb', line 64

def interleaved
  Array.new(num_frames * channels) do |index|
    frame_index = index / channels
    channel_index = index % channels
    @channel_data[channel_index][frame_index]
  end
end

#mix!(other, headroom: :sum, gain: 1.0) ⇒ Object



96
97
98
99
100
101
102
103
104
105
# File 'lib/deftones/core/audio_block.rb', line 96

def mix!(other, headroom: :sum, gain: 1.0)
  incoming = other.fit_channels(channels)
  channels.times do |channel_index|
    num_frames.times do |frame_index|
      mixed = @channel_data[channel_index][frame_index] + (incoming.channel_data[channel_index][frame_index] * gain)
      @channel_data[channel_index][frame_index] = apply_headroom(mixed, headroom)
    end
  end
  self
end

#monoObject



55
56
57
58
59
60
61
62
# File 'lib/deftones/core/audio_block.rb', line 55

def mono
  return [] if @channel_data.empty?
  return @channel_data.first.dup if channels == 1

  Array.new(num_frames) do |frame_index|
    @channel_data.sum { |channel| channel[frame_index] } / channels.to_f
  end
end

#num_framesObject



47
48
49
# File 'lib/deftones/core/audio_block.rb', line 47

def num_frames
  @channel_data.first&.length || 0
end

#packed_float32Object



72
73
74
# File 'lib/deftones/core/audio_block.rb', line 72

def packed_float32
  interleaved.pack("e*")
end

#packed_float64Object



76
77
78
# File 'lib/deftones/core/audio_block.rb', line 76

def packed_float64
  interleaved.pack("E*")
end

#upmixed_channel(index, policy) ⇒ Object (private)



121
122
123
124
125
126
127
128
129
# File 'lib/deftones/core/audio_block.rb', line 121

def upmixed_channel(index, policy)
  case policy
  when :wrap then channel(index % channels).dup
  when :silence then index < channels ? channel(index).dup : Array.new(num_frames, 0.0)
  when :duplicate then channel([index, channels - 1].min).dup
  else
    raise ArgumentError, "Unsupported upmix policy: #{policy}"
  end
end