Class: FormatParser::WAVParser
- Inherits:
-
Object
- Object
- FormatParser::WAVParser
- Includes:
- IOUtils
- Defined in:
- lib/parsers/wav_parser.rb
Instance Method Summary collapse
- #call(io) ⇒ Object
- #file_info(fmt_data, sample_frames) ⇒ Object
- #process_non_pcm(fmt_data, total_sample_frames) ⇒ Object
- #process_pcm(fmt_data, data_size) ⇒ Object
- #unpack_fmt_chunk(io, chunk_size) ⇒ Object
Methods included from IOUtils
Instance Method Details
#call(io) ⇒ Object
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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 |
# File 'lib/parsers/wav_parser.rb', line 4 def call(io) # Read the RIFF header. Chunk descriptor should be RIFF, the size should # contain the size of the entire file in bytes minus 8 bytes for the # two fields not included in this count: chunk_id and size. chunk_id, _size, riff_type = safe_read(io, 12).unpack('a4la4') # The chunk_id and riff_type should be RIFF and WAVE respectively return unless chunk_id == 'RIFF' && riff_type == 'WAVE' # There are no restrictions upon the order of the chunks within a WAVE file, # with the exception that the Format chunk must precede the Data chunk. # The specification does not require the Format chunk to be the first chunk # after the RIFF header. # http://soundfile.sapp.org/doc/WaveFormat/ # For WAVE files containing PCM audio format we parse the 'fmt ' and # 'data' chunks while for non PCM audio formats the 'fmt ' and 'fact' # chunks. In the latter case the order fo appearence of the chunks is # arbitrary. fmt_processed = false fact_processed = false fmt_data = {} total_sample_frames = 0 loop do chunk_type, chunk_size = safe_read(io, 8).unpack('a4l') case chunk_type when 'fmt ' # watch out: the chunk ID of the format chunk ends with a space fmt_data = unpack_fmt_chunk(io, chunk_size) if fmt_data[:audio_format] != 1 and fact_processed return process_non_pcm(fmt_data, total_sample_frames) end fmt_processed = true when 'data' return unless fmt_processed # the 'data' chunk cannot preceed the 'fmt ' chunk return process_pcm(fmt_data, chunk_size) if fmt_data[:audio_format] == 1 safe_skip(io, chunk_size) when 'fact' total_sample_frames = safe_read(io, 4).unpack('l').first safe_skip(io, chunk_size - 4) if fmt_processed and fmt_data[:audio_format] != 1 return process_non_pcm(fmt_data, total_sample_frames) end fact_processed = true else # Skip this chunk until a known chunk is encountered safe_skip(io, chunk_size) end end end |
#file_info(fmt_data, sample_frames) ⇒ Object
86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/parsers/wav_parser.rb', line 86 def file_info(fmt_data, sample_frames) return unless fmt_data[:sample_rate] > 0 duration_in_seconds = sample_frames / fmt_data[:sample_rate].to_f FormatParser::Audio.new( format: :wav, num_audio_channels: fmt_data[:channels], audio_sample_rate_hz: fmt_data[:sample_rate], media_duration_frames: sample_frames, media_duration_seconds: duration_in_seconds, ) end |
#process_non_pcm(fmt_data, total_sample_frames) ⇒ Object
82 83 84 |
# File 'lib/parsers/wav_parser.rb', line 82 def process_non_pcm(fmt_data, total_sample_frames) file_info(fmt_data, total_sample_frames) end |
#process_pcm(fmt_data, data_size) ⇒ Object
76 77 78 79 80 |
# File 'lib/parsers/wav_parser.rb', line 76 def process_pcm(fmt_data, data_size) return unless fmt_data[:channels] > 0 and fmt_data[:bits_per_sample] > 0 sample_frames = data_size / (fmt_data[:channels] * fmt_data[:bits_per_sample] / 8) file_info(fmt_data, sample_frames) end |
#unpack_fmt_chunk(io, chunk_size) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/parsers/wav_parser.rb', line 52 def unpack_fmt_chunk(io, chunk_size) # The size of the fmt chunk is at least 16 bytes. If the format tag's value is not # 1 compression might be in use for storing the data # and the fmt chunk might contain extra fields appended to it. # The last 4 fields of the fmt tag are always: # * unsigned short channels # * unsigned long samples per sec # * unsigned long average bytes per sec # * unsigned short block align # * unsigned short bits per sample fmt_info = safe_read(io, 16).unpack('S_2I2S_2') safe_skip(io, chunk_size - 16) # skip the extra fields { audio_format: fmt_info[0], channels: fmt_info[1], sample_rate: fmt_info[2], byte_rate: fmt_info[3], block_align: fmt_info[4], bits_per_sample: fmt_info[5], } end |