Class: Wavify::Codecs::Raw

Inherits:
Base
  • Object
show all
Defined in:
lib/wavify/codecs/raw.rb,
sig/codecs.rbs

Overview

Raw PCM/float sample codec.

Since raw audio has no container metadata, callers must provide a Wavify::Core::Format when reading/stream-reading/metadata.

Constant Summary collapse

EXTENSIONS =

Recognized filename extensions.

%w[.raw .pcm].freeze

Class Method Summary collapse

Methods inherited from Base

available?

Class Method Details

.can_read?(io_or_path) ⇒ Boolean

Parameters:

  • io_or_path (String, IO)

Returns:

  • (Boolean)


16
17
18
19
20
21
# File 'lib/wavify/codecs/raw.rb', line 16

def can_read?(io_or_path)
  return true if io_or_path.respond_to?(:read)
  return false unless io_or_path.is_a?(String)

  EXTENSIONS.include?(File.extname(io_or_path).downcase)
end

.metadata(io_or_path, format:, endianness: :little, signed: nil, float_domain: :normalized, **codec_options) ⇒ Hash

Reads raw audio metadata using byte size and explicit format.

Parameters:

  • io_or_path (String, IO)
  • format (Wavify::Core::Format)
  • format: (Core::Format)
  • endianness: (Symbol) (defaults to: :little)
  • signed: (Boolean, nil) (defaults to: nil)
  • float_domain: (Symbol) (defaults to: :normalized)
  • options (Object)

Returns:

  • (Hash)


176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/wavify/codecs/raw.rb', line 176

def (io_or_path, format:, endianness: :little, signed: nil, float_domain: :normalized, **codec_options)
  validate_no_codec_options!(codec_options, operation: "raw metadata")
  target_format = validate_format!(format)
  normalize_raw_encoding(target_format, endianness: endianness, signed: signed, float_domain: float_domain)
  io, close_io = open_input(io_or_path)
  byte_size = byte_size_without_consuming!(io)
  validate_frame_alignment!(byte_size, target_format)
  sample_frames = byte_size / target_format.block_align

  {
    format: target_format,
    sample_frame_count: sample_frames,
    duration: Core::Duration.from_samples(sample_frames, target_format.sample_rate)
  }
ensure
  io.close if close_io && io
end

.read(io_or_path, format: nil, endianness: :little, signed: nil, float_domain: :normalized, **codec_options) ⇒ Wavify::Core::SampleBuffer

Reads a raw audio file/IO into a sample buffer.

Parameters:

  • io_or_path (String, IO)
  • format (Wavify::Core::Format) (defaults to: nil)
  • format: (Core::Format, nil) (defaults to: nil)
  • endianness: (Symbol) (defaults to: :little)
  • signed: (Boolean, nil) (defaults to: nil)
  • float_domain: (Symbol) (defaults to: :normalized)
  • options (Object)

Returns:



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/wavify/codecs/raw.rb', line 28

def read(io_or_path, format: nil, endianness: :little, signed: nil, float_domain: :normalized, **codec_options)
  validate_no_codec_options!(codec_options, operation: "raw read")
  target_format = validate_format!(format)
  encoding = normalize_raw_encoding(
    target_format,
    endianness: endianness,
    signed: signed,
    float_domain: float_domain
  )
  io, close_io = open_input(io_or_path)
  data = read_to_end(io)
  validate_frame_alignment!(data.bytesize, target_format)
  samples = decode_samples(data, target_format, **encoding)
  Core::SampleBuffer.new(samples, target_format)
ensure
  io.close if close_io && io
end

.stream_read(io_or_path, format:, chunk_size: 4096, endianness: :little, signed: nil, float_domain: :normalized, **codec_options) {|arg0| ... } ⇒ Enumerator

Streams raw audio decoding in frame chunks.

Parameters:

  • io_or_path (String, IO)
  • format (Wavify::Core::Format)
  • chunk_size (Integer) (defaults to: 4096)
  • format: (Core::Format)
  • chunk_size: (Integer) (defaults to: 4096)
  • endianness: (Symbol) (defaults to: :little)
  • signed: (Boolean, nil) (defaults to: nil)
  • float_domain: (Symbol) (defaults to: :normalized)
  • options (Object)

Yields:

Yield Parameters:

Yield Returns:

  • (void)

Returns:

  • (Enumerator)


81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/wavify/codecs/raw.rb', line 81

def stream_read(io_or_path, format:, chunk_size: 4096, endianness: :little, signed: nil,
                float_domain: :normalized, **codec_options)
  validate_no_codec_options!(codec_options, operation: "raw stream_read")
  unless block_given?
    return enum_for(
      __method__,
      io_or_path,
      format: format,
      chunk_size: chunk_size,
      endianness: endianness,
      signed: signed,
      float_domain: float_domain,
      **codec_options
    )
  end

  target_format = validate_format!(format)
  encoding = normalize_raw_encoding(
    target_format,
    endianness: endianness,
    signed: signed,
    float_domain: float_domain
  )
  io, close_io = open_input(io_or_path)
  bytes_per_frame = target_format.block_align
  raw_chunk_size = chunk_size * bytes_per_frame
  pending = +"".b

  loop do
    chunk = io.read(raw_chunk_size)
    break if chunk.nil? || chunk.empty?

    pending << chunk
    usable_bytes = pending.bytesize - (pending.bytesize % bytes_per_frame)
    next if usable_bytes.zero?

    frame_data = pending.byteslice(0, usable_bytes)
    pending = pending.byteslice(usable_bytes, pending.bytesize - usable_bytes) || +"".b
    samples = decode_samples(frame_data, target_format, **encoding)
    yield Core::SampleBuffer.new(samples, target_format)
  end
  raise InvalidFormatError, "raw data ends with a partial sample frame" unless pending.empty?
ensure
  io.close if close_io && io
end

.stream_write(io_or_path, format:, endianness: :little, signed: nil, float_domain: :normalized, **codec_options) {|arg0| ... } ⇒ Enumerator, ...

Streams raw audio encoding via a yielded chunk writer.

Parameters:

  • io_or_path (String, IO)
  • format (Wavify::Core::Format)
  • format: (Core::Format)
  • endianness: (Symbol) (defaults to: :little)
  • signed: (Boolean, nil) (defaults to: nil)
  • float_domain: (Symbol) (defaults to: :normalized)
  • options (Object)

Yields:

Yield Parameters:

  • arg0 (Object)

Yield Returns:

  • (void)

Returns:

  • (Enumerator, String, IO)


132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/wavify/codecs/raw.rb', line 132

def stream_write(io_or_path, format:, endianness: :little, signed: nil, float_domain: :normalized,
                 **codec_options)
  validate_no_codec_options!(codec_options, operation: "raw stream_write")
  unless block_given?
    return enum_for(
      __method__,
      io_or_path,
      format: format,
      endianness: endianness,
      signed: signed,
      float_domain: float_domain,
      **codec_options
    )
  end

  target_format = validate_format!(format)
  encoding = normalize_raw_encoding(
    target_format,
    endianness: endianness,
    signed: signed,
    float_domain: float_domain
  )
  io, close_io = open_output(io_or_path)
  prepare_output!(io, owned: close_io)

  writer = lambda do |sample_buffer|
    raise InvalidParameterError, "stream chunk must be Core::SampleBuffer" unless sample_buffer.is_a?(Core::SampleBuffer)

    buffer = sample_buffer.format == target_format ? sample_buffer : sample_buffer.convert(target_format)
    write_all(io, encode_samples(buffer.samples, target_format, **encoding))
  end

  yield writer
  finalize_output!(io, owned: close_io)
  io_or_path
ensure
  io.close if close_io && io
end

.write(io_or_path, sample_buffer, format:, endianness: :little, signed: nil, float_domain: :normalized, **codec_options) ⇒ String, IO

Writes a raw audio buffer to a path/IO.

Parameters:

Returns:

  • (String, IO)


52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/wavify/codecs/raw.rb', line 52

def write(io_or_path, sample_buffer, format:, endianness: :little, signed: nil, float_domain: :normalized,
          **codec_options)
  validate_no_codec_options!(codec_options, operation: "raw write")
  raise InvalidParameterError, "sample_buffer must be Core::SampleBuffer" unless sample_buffer.is_a?(Core::SampleBuffer)

  target_format = validate_format!(format)
  encoding = normalize_raw_encoding(
    target_format,
    endianness: endianness,
    signed: signed,
    float_domain: float_domain
  )
  buffer = sample_buffer.format == target_format ? sample_buffer : sample_buffer.convert(target_format)

  io, close_io = open_output(io_or_path)
  prepare_output!(io, owned: close_io)
  write_all(io, encode_samples(buffer.samples, target_format, **encoding))
  finalize_output!(io, owned: close_io)
  io_or_path
ensure
  io.close if close_io && io
end