Class: Wavify::Codecs::Aiff
- Defined in:
- lib/wavify/codecs/aiff.rb,
sig/codecs.rbs
Overview
AIFF codec for PCM audio and uncompressed AIFF-C variants.
Constant Summary collapse
- EXTENSIONS =
Recognized filename extensions.
%w[.aiff .aif .aifc].freeze
- AIFF_MAX_SIZE =
:nodoc:
0xFFFF_FFFF
Class Method Summary collapse
- .can_read?(io_or_path) ⇒ Boolean
-
.metadata(io_or_path) ⇒ Hash
Reads AIFF metadata without decoding the full audio payload.
-
.read(io_or_path, format: nil, warning_io: nil) ⇒ Wavify::Core::SampleBuffer
Reads an AIFF file/IO into a sample buffer.
-
.stream_read(io_or_path, chunk_size: 4096, warning_io: nil) {|arg0| ... } ⇒ Enumerator
Streams AIFF decoding in frame chunks.
-
.stream_write(io_or_path, format:, form_type: nil, compression_type: nil, compression_name: nil, **codec_options) {|arg0| ... } ⇒ Enumerator, ...
Streams AIFF encoding through a yielded chunk writer.
-
.write(io_or_path, sample_buffer, format: nil, form_type: nil, compression_type: nil, compression_name: nil, **codec_options) ⇒ String, IO
Writes a sample buffer as AIFF (PCM only).
Methods inherited from Base
Class Method Details
.can_read?(io_or_path) ⇒ Boolean
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/wavify/codecs/aiff.rb', line 14 def can_read?(io_or_path) if io_or_path.is_a?(String) return true if EXTENSIONS.include?(File.extname(io_or_path).downcase) return false unless File.file?(io_or_path) end io, close_io = open_input(io_or_path) return false unless io header = probe_bytes(io, 12) header&.start_with?("FORM") && %w[AIFF AIFC].include?(header[8, 4]) rescue StreamError false ensure io.close if close_io && io end |
.metadata(io_or_path) ⇒ Hash
Reads AIFF metadata without decoding the full audio payload.
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/wavify/codecs/aiff.rb', line 171 def (io_or_path) io, close_io = open_input(io_or_path) ensure_seekable!(io) info = parse_chunks(io) format = info.fetch(:format) sample_frame_count = info.fetch(:sample_frame_count) { format: format, sample_frame_count: sample_frame_count, duration: Core::Duration.from_samples(sample_frame_count, format.sample_rate), form_type: info[:form_type], compression_type: info[:compression_type], compression_name: info[:compression_name], encoded_sample_rate: info[:encoded_sample_rate], container_bit_depth: info[:container_bit_depth], valid_bits_per_sample: info[:valid_bits_per_sample], warnings: info[:warnings].freeze, markers: info[:markers], instrument: info[:instrument] } ensure io.close if close_io && io end |
.read(io_or_path, format: nil, warning_io: nil) ⇒ Wavify::Core::SampleBuffer
Reads an AIFF file/IO into a sample buffer.
36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/wavify/codecs/aiff.rb', line 36 def read(io_or_path, format: nil, warning_io: nil) io, close_io = open_input(io_or_path) ensure_seekable!(io) info = parse_chunks(io) emit_warnings(info.fetch(:warnings), warning_io) source_format = info.fetch(:format) samples = read_sound_data(io, info, source_format) buffer = Core::SampleBuffer.new(samples, source_format) format ? buffer.convert(format) : buffer ensure io.close if close_io && io end |
.stream_read(io_or_path, chunk_size: 4096, warning_io: nil) {|arg0| ... } ⇒ Enumerator
Streams AIFF decoding in frame chunks.
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/wavify/codecs/aiff.rb', line 80 def stream_read(io_or_path, chunk_size: 4096, warning_io: nil) return enum_for(__method__, io_or_path, chunk_size: chunk_size, warning_io: warning_io) unless block_given? raise InvalidParameterError, "chunk_size must be a positive Integer" unless chunk_size.is_a?(Integer) && chunk_size.positive? io, close_io = open_input(io_or_path) ensure_seekable!(io) info = parse_chunks(io) emit_warnings(info.fetch(:warnings), warning_io) format = info.fetch(:format) bytes_per_frame = format.block_align remaining = info.fetch(:sound_data_size) io.seek(info.fetch(:sound_data_offset), IO::SEEK_SET) while remaining.positive? bytes = [remaining, bytes_per_frame * chunk_size].min chunk_data = read_exact(io, bytes, "truncated SSND data") yield Core::SampleBuffer.new(decode_samples(chunk_data, format, byte_order: info.fetch(:byte_order)), format) remaining -= bytes end ensure io.close if close_io && io end |
.stream_write(io_or_path, format:, form_type: nil, compression_type: nil, compression_name: nil, **codec_options) {|arg0| ... } ⇒ Enumerator, ...
Streams AIFF encoding through a yielded chunk writer.
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 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 |
# File 'lib/wavify/codecs/aiff.rb', line 109 def stream_write(io_or_path, format:, form_type: nil, compression_type: nil, compression_name: nil, **) (, operation: "AIFF stream_write") unless block_given? return enum_for( __method__, io_or_path, format: format, form_type: form_type, compression_type: compression_type, compression_name: compression_name, ** ) end raise InvalidParameterError, "format must be Core::Format" unless format.is_a?(Core::Format) raise UnsupportedFormatError, "AIFF stream writer supports PCM only" unless format.sample_format == :pcm = ( io_or_path, form_type: form_type, compression_type: compression_type, compression_name: compression_name ) io, close_io = open_output(io_or_path) ensure_seekable!(io) prepare_output!(io, owned: close_io) header = write_stream_header(io, format, ) total_data_bytes = 0 total_sample_frames = 0 writer = lambda do |buffer| raise InvalidParameterError, "stream chunk must be Core::SampleBuffer" unless buffer.is_a?(Core::SampleBuffer) converted = buffer.format == format ? buffer : buffer.convert(format) encoded_bytes = converted.sample_frame_count * format.block_align validate_aiff_sizes!( data_bytes: total_data_bytes + encoded_bytes, sample_frames: total_sample_frames + converted.sample_frame_count ) encoded = encode_samples(converted.samples, format, byte_order: .fetch(:byte_order)) unless encoded.bytesize == encoded_bytes raise StreamError, "AIFF encoder produced an unexpected byte count" end write_all(io, encoded) total_data_bytes += encoded_bytes total_sample_frames += converted.sample_frame_count end yield writer write_all(io, "\x00") if total_data_bytes.odd? finalize_stream_header(io, header, total_data_bytes, total_sample_frames) finalize_output!(io, owned: close_io) io_or_path ensure io.close if close_io && io end |
.write(io_or_path, sample_buffer, format: nil, form_type: nil, compression_type: nil, compression_name: nil, **codec_options) ⇒ String, IO
Writes a sample buffer as AIFF (PCM only).
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/wavify/codecs/aiff.rb', line 56 def write(io_or_path, sample_buffer, format: nil, form_type: nil, compression_type: nil, compression_name: nil, **) (, operation: "AIFF write") raise InvalidParameterError, "sample_buffer must be Core::SampleBuffer" unless sample_buffer.is_a?(Core::SampleBuffer) target_format = format || sample_buffer.format raise InvalidParameterError, "format must be Core::Format" unless target_format.is_a?(Core::Format) raise UnsupportedFormatError, "AIFF writer supports PCM only" unless target_format.sample_format == :pcm buffer = sample_buffer.format == target_format ? sample_buffer : sample_buffer.convert(target_format) stream_write( io_or_path, format: target_format, form_type: form_type, compression_type: compression_type, compression_name: compression_name ) { |writer| writer.call(buffer) } end |