Class: Wavify::Codecs::OggVorbis
- Defined in:
- lib/wavify/codecs/ogg_vorbis.rb,
sig/codecs.rbs
Overview
OGG Vorbis codec backed by libogg (ogg-ruby) and libvorbis (vorbis-ruby).
Container demuxing uses Ogg::SyncState and Ogg::StreamState. Audio decode uses libvorbis synthesis functions via Vorbis::Native. Audio encode uses Vorbis::Encoder.
Sequential chained Vorbis logical streams are concatenated (and resampled to the first logical stream sample rate when rates differ). Interleaved multi-stream OGG logical streams are mixed with clipping.
Constant Summary collapse
- DEPENDENCY_LOAD_ERRORS =
begin errors = {} begin require "ogg" rescue LoadError => e errors["ogg-ruby"] = e end begin require "vorbis" rescue LoadError => e errors["vorbis"] = e end errors.freeze end
- EXTENSIONS =
Recognized filename extensions.
%w[.ogg .oga].freeze
- VORBIS_SIGNATURE =
:nodoc:
"vorbis"- IDENTIFICATION_HEADER_TYPE =
:nodoc:
0x01- COMMENT_HEADER_TYPE =
:nodoc:
0x03- SETUP_HEADER_TYPE =
:nodoc:
0x05- GRANULE_POSITION_UNKNOWN =
:nodoc:
0xFFFF_FFFF_FFFF_FFFF- VORBIS_ENCODE_DEFAULT_QUALITY =
:nodoc:
0.4- VORBIS_ENCODE_QUALITY_RANGE =
:nodoc:
(-0.1..1.0)
Class Method Summary collapse
- .available? ⇒ Boolean
- .can_read?(io_or_path) ⇒ Boolean
-
.metadata(io_or_path) ⇒ Hash
Parses OGG/Vorbis headers and returns metadata without audio decode.
-
.read(io_or_path, format: nil, mix_strategy: :clip) ⇒ Core::SampleBuffer
Reads OGG Vorbis audio.
- .runtime_diagnostics ⇒ Hash[Symbol, untyped]
-
.stream_read(io_or_path, chunk_size: 4096, mix_strategy: :clip) {|arg0| ... } ⇒ Core::Format
Streams OGG Vorbis audio decoding.
-
.stream_write(io_or_path, format:, quality: VORBIS_ENCODE_DEFAULT_QUALITY, **codec_options) {|arg0| ... } ⇒ Object
Streams OGG Vorbis audio encoding via Vorbis::Encoder.
-
.write(io_or_path, sample_buffer, format:, quality: VORBIS_ENCODE_DEFAULT_QUALITY, **codec_options) ⇒ Core::SampleBuffer
Writes OGG Vorbis audio.
Class Method Details
.available? ⇒ Boolean
47 48 49 |
# File 'lib/wavify/codecs/ogg_vorbis.rb', line 47 def available? DEPENDENCY_LOAD_ERRORS.empty? end |
.can_read?(io_or_path) ⇒ Boolean
73 74 75 76 77 78 79 80 81 |
# File 'lib/wavify/codecs/ogg_vorbis.rb', line 73 def can_read?(io_or_path) return true if io_or_path.is_a?(String) && EXTENSIONS.include?(File.extname(io_or_path).downcase) return false unless io_or_path.respond_to?(:read) magic = probe_bytes(io_or_path, 4) magic == "OggS" rescue StreamError false end |
.metadata(io_or_path) ⇒ Hash
Parses OGG/Vorbis headers and returns metadata without audio decode.
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/wavify/codecs/ogg_vorbis.rb', line 214 def (io_or_path) ensure_available! io, close_io = open_input(io_or_path) chained_streams, physical_ogg_info = read_ogg_logical_stream_chains(io, storage: :tempfile) if chained_streams.length > 1 = chained_streams.map do |stream| (logical_stream_io(stream)) end if physical_ogg_info[:interleaved_multistream] return (, chained_streams, physical_ogg_info) end return (, chained_streams) end (logical_stream_io(chained_streams.fetch(0))) ensure close_logical_streams(chained_streams) io.close if close_io && io end |
.read(io_or_path, format: nil, mix_strategy: :clip) ⇒ Core::SampleBuffer
Supports full Vorbis decode via libvorbis. Sequential chained OGG logical streams are concatenated and normalized to the first logical stream format (including resampling). Interleaved multi-stream OGG logical streams are mixed.
Reads OGG Vorbis audio.
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/wavify/codecs/ogg_vorbis.rb', line 89 def read(io_or_path, format: nil, mix_strategy: :clip) ensure_available! mix_strategy = validate_vorbis_mix_strategy!(mix_strategy) if (chained_decoded = decode_chained_vorbis_read_if_needed( io_or_path, target_format: format, mix_strategy: mix_strategy )) return chained_decoded end decode_context = build_vorbis_decode_context(io_or_path) decoded = run_vorbis_decode_pipeline(decode_context) return decoded unless format decoded.convert(format) end |
.runtime_diagnostics ⇒ Hash[Symbol, untyped]
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/wavify/codecs/ogg_vorbis.rb', line 51 def runtime_diagnostics return { available: false, compatible: false, missing: DEPENDENCY_LOAD_ERRORS.keys.freeze } unless available? required_native_methods = %i[ vorbis_info_init vorbis_info_clear vorbis_comment_init vorbis_comment_clear vorbis_synthesis_headerin vorbis_synthesis_init vorbis_block_init vorbis_synthesis vorbis_synthesis_blockin vorbis_synthesis_pcmout vorbis_synthesis_read ] missing_methods = required_native_methods.reject { |method| Vorbis::Native.respond_to?(method) } { available: true, compatible: missing_methods.empty?, missing_native_methods: missing_methods.freeze, versions: { "ogg-ruby" => Gem.loaded_specs["ogg-ruby"]&.version&.to_s, "vorbis" => Gem.loaded_specs["vorbis"]&.version&.to_s }.freeze }.freeze end |
.stream_read(io_or_path, chunk_size: 4096, mix_strategy: :clip) {|arg0| ... } ⇒ Core::Format
Supports full Vorbis decode via libvorbis. Sequential chained OGG logical streams are concatenated and normalized to the first logical stream format during streaming (including resampling). Interleaved multi-stream OGG logical streams are mixed.
Streams OGG Vorbis audio decoding.
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/wavify/codecs/ogg_vorbis.rb', line 125 def stream_read(io_or_path, chunk_size: 4096, mix_strategy: :clip, &block) unless block_given? return enum_for(__method__, io_or_path, chunk_size: chunk_size, mix_strategy: mix_strategy) end ensure_available! raise InvalidParameterError, "chunk_size must be a positive Integer" unless chunk_size.is_a?(Integer) && chunk_size.positive? mix_strategy = validate_vorbis_mix_strategy!(mix_strategy) if multistream_ogg_prefix?(io_or_path) return nil if stream_chained_vorbis_if_needed( io_or_path, chunk_size: chunk_size, mix_strategy: mix_strategy, &block ) end decode_context = build_vorbis_decode_context(io_or_path) run_vorbis_decode_pipeline(decode_context, streaming: true, chunk_size: chunk_size, &block) end |
.stream_write(io_or_path, format:, quality: VORBIS_ENCODE_DEFAULT_QUALITY, **codec_options) {|arg0| ... } ⇒ Object
Encodes using libvorbis at the default VBR quality level. Accepts any channel count and sample rate supported by libvorbis.
Streams OGG Vorbis audio encoding via Vorbis::Encoder.
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 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 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/wavify/codecs/ogg_vorbis.rb', line 150 def stream_write(io_or_path, format:, quality: VORBIS_ENCODE_DEFAULT_QUALITY, **) ensure_available! (, operation: "OGG Vorbis stream_write") encode_quality = normalize_vorbis_quality(quality) unless block_given? return enum_for(__method__, io_or_path, format: format, quality: quality, **) end raise InvalidParameterError, "format must be Core::Format" unless format.is_a?(Core::Format) raise InvalidParameterError, "Vorbis encode requires positive channel count" unless format.channels.to_i.positive? raise InvalidParameterError, "Vorbis encode requires positive sample_rate" unless format.sample_rate.to_i.positive? target_format = Core::Format.new( channels: format.channels, sample_rate: format.sample_rate, bit_depth: 32, sample_format: :float ) io, close_io = open_output(io_or_path) prepare_output!(io, owned: close_io) encoder = Vorbis::Encoder.new( channels: target_format.channels, rate: target_format.sample_rate, quality: encode_quality ) encoder.write_headers { |page_bytes| write_all(io, page_bytes) } channels_data = Array.new(target_format.channels) { [] } writer = lambda do |chunk| raise InvalidParameterError, "stream chunk must be Core::SampleBuffer" unless chunk.is_a?(Core::SampleBuffer) buffer = chunk.format == target_format ? chunk : chunk.convert(target_format) next nil if buffer.sample_frame_count.zero? channels_data.each(&:clear) buffer.samples.each_with_index do |sample, index| channels_data[index % target_format.channels] << sample.to_f end encoder.encode(channels_data) { |page_bytes| write_all(io, page_bytes) } end yield writer encoder.finish { |page_bytes| write_all(io, page_bytes) } encoder.close finalize_output!(io, owned: close_io) io_or_path rescue StandardError begin encoder&.close rescue StandardError nil end raise ensure io.close if close_io && io end |
.write(io_or_path, sample_buffer, format:, quality: VORBIS_ENCODE_DEFAULT_QUALITY, **codec_options) ⇒ Core::SampleBuffer
Writes OGG Vorbis audio.
109 110 111 112 113 114 115 116 117 |
# File 'lib/wavify/codecs/ogg_vorbis.rb', line 109 def write(io_or_path, sample_buffer, format:, quality: VORBIS_ENCODE_DEFAULT_QUALITY, **) ensure_available! (, operation: "OGG Vorbis write") raise InvalidParameterError, "sample_buffer must be Core::SampleBuffer" unless sample_buffer.is_a?(Core::SampleBuffer) stream_write(io_or_path, format: format, quality: quality) do |writer| writer.call(sample_buffer) end end |