Module: DSP::Wav
- Defined in:
- lib/dsprb/wav.rb,
sig/dsprb.rbs
Constant Summary collapse
- PCM =
1- IEEE_FLOAT =
3- EXTENSIBLE =
WAVE_FORMAT_EXTENSIBLE stores the effective format in the first two bytes of its SubFormat GUID rather than in the format field itself.
0xFFFE- HEADER_BYTES =
12- CHUNK_HEADER_BYTES =
8- EIGHT_BIT_MIDPOINT =
Unsigned 8-bit PCM is the one depth WAVE stores with a midpoint offset.
128.0
Class Method Summary collapse
- .assert_riff!(bytes) ⇒ void
- .decode(bytes) ⇒ Waveform
- .decode_packed_24(data) ⇒ ::Array[::Float]
- .decode_samples(data, format, bits) ⇒ ::Array[::Float]
- .downmix(samples, channels) ⇒ ::Array[::Float]
- .read(path) ⇒ Waveform
- .scale(integers, magnitude_bits) ⇒ ::Array[::Float]
Class Method Details
.assert_riff!(bytes) ⇒ void
This method returns an undefined value.
54 55 56 57 58 |
# File 'lib/dsprb/wav.rb', line 54 def assert_riff!(bytes) return if bytes.bytesize >= HEADER_BYTES && bytes[0, 4] == "RIFF" && bytes[8, 4] == "WAVE" raise FormatError, "not a RIFF/WAVE stream" end |
.decode(bytes) ⇒ Waveform
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/dsprb/wav.rb', line 22 def decode(bytes) assert_riff!(bytes) format = nil channels = nil sample_rate = nil bits = nil data = nil position = HEADER_BYTES while position + CHUNK_HEADER_BYTES <= bytes.bytesize identifier = bytes[position, 4] size = bytes[position + 4, 4].unpack1("V") body = bytes[position + CHUNK_HEADER_BYTES, size] case identifier when "fmt " format, channels, sample_rate, _byte_rate, _align, bits = body.unpack("vvVVvv") format = body[24, 2].unpack1("v") if format == EXTENSIBLE && body.bytesize >= 26 when "data" data = body end position += CHUNK_HEADER_BYTES + size + (size.odd? ? 1 : 0) end raise FormatError, "no fmt chunk" if sample_rate.nil? raise FormatError, "no data chunk" if data.nil? Waveform.new(samples: downmix(decode_samples(data, format, bits), channels), sample_rate: sample_rate) end |
.decode_packed_24(data) ⇒ ::Array[::Float]
84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/dsprb/wav.rb', line 84 def decode_packed_24(data) sign_bit = 1 << 23 full_scale = sign_bit.to_f data.unpack("C*").each_slice(3).filter_map do |low, middle, high| next if high.nil? value = (high << 16) | (middle << 8) | low value -= sign_bit << 1 if value >= sign_bit value / full_scale end end |
.decode_samples(data, format, bits) ⇒ ::Array[::Float]
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/dsprb/wav.rb', line 60 def decode_samples(data, format, bits) case [format, bits] in [PCM, 8] data.unpack("C*").map { |value| (value - EIGHT_BIT_MIDPOINT) / EIGHT_BIT_MIDPOINT } in [PCM, 16] scale(data.unpack("s<*"), 15) in [PCM, 24] decode_packed_24(data) in [PCM, 32] scale(data.unpack("l<*"), 31) in [IEEE_FLOAT, 32] data.unpack("e*") in [IEEE_FLOAT, 64] data.unpack("E*") else raise FormatError, "unsupported encoding: format #{format.inspect}, #{bits.inspect} bit" end end |
.downmix(samples, channels) ⇒ ::Array[::Float]
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/dsprb/wav.rb', line 97 def downmix(samples, channels) return samples if channels.nil? || channels <= 1 Array.new(samples.length / channels) do |index| offset = index * channels total = 0.0 channel = 0 while channel < channels total += samples[offset + channel] channel += 1 end total / channels end end |
.read(path) ⇒ Waveform
20 |
# File 'lib/dsprb/wav.rb', line 20 def read(path) = decode(File.binread(path)) |
.scale(integers, magnitude_bits) ⇒ ::Array[::Float]
79 80 81 82 |
# File 'lib/dsprb/wav.rb', line 79 def scale(integers, magnitude_bits) full_scale = (1 << magnitude_bits).to_f integers.map { |value| value / full_scale } end |