Microsoft WAVE audio file: Ruby parsing library

The WAVE file format is a subset of Microsoft's RIFF specification for the storage of multimedia files. A RIFF file starts out with a file header followed by a sequence of data chunks. A WAVE file is often just a RIFF file with a single "WAVE" chunk which consists of two sub-chunks -- a "fmt " chunk specifying the data format and a "data" chunk containing the actual sample data, although other chunks exist and are used.

An extension of the file format is the Broadcast Wave Format (BWF) for radio broadcasts. Sample files can be found at:

https://www.bbc.co.uk/rd/publications/saqas

This Kaitai implementation was written by John Byrd of Gigantic Software (jbyrd@giganticsoftware.com), and it is likely to contain bugs.

This page hosts a formal specification of Microsoft WAVE audio file using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Runtime library

All parsing code for Ruby generated by Kaitai Struct depends on the Ruby runtime library. You have to install it before you can parse data.

The Ruby runtime library can be installed from RubyGems:

gem install kaitai-struct

Code

Parse a local file and get structure in memory:

data = Wav.from_file("path/to/local/file.wav")

Or parse structure from a string of bytes:

bytes = "\x00\x01\x02..."
data = Wav.new(Kaitai::Struct::Stream.new(bytes))

After that, one can get various attributes from the structure by invoking getter methods like:

data.chunk # => get chunk

Ruby source code to parse Microsoft WAVE audio file

wav.rb

# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

require 'kaitai/struct/struct'

unless Gem::Version.new(Kaitai::Struct::VERSION) >= Gem::Version.new('0.9')
  raise "Incompatible Kaitai Struct Ruby API: 0.9 or later is required, but you have #{Kaitai::Struct::VERSION}"
end


##
# The WAVE file format is a subset of Microsoft's RIFF specification for the
# storage of multimedia files. A RIFF file starts out with a file header
# followed by a sequence of data chunks. A WAVE file is often just a RIFF
# file with a single "WAVE" chunk which consists of two sub-chunks --
# a "fmt " chunk specifying the data format and a "data" chunk containing
# the actual sample data, although other chunks exist and are used.
# 
# An extension of the file format is the Broadcast Wave Format (BWF) for radio
# broadcasts. Sample files can be found at:
# 
# <https://www.bbc.co.uk/rd/publications/saqas>
# 
# This Kaitai implementation was written by John Byrd of Gigantic Software
# (jbyrd@giganticsoftware.com), and it is likely to contain bugs.
# @see http://soundfile.sapp.org/doc/WaveFormat/ Source
# @see https://www.mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html Source
# @see https://web.archive.org/web/20101031101749/http://www.ebu.ch/fr/technical/publications/userguides/bwf_user_guide.php Source
class Wav < Kaitai::Struct::Struct

  W_FORMAT_TAG_TYPE = {
    0 => :w_format_tag_type_unknown,
    1 => :w_format_tag_type_pcm,
    2 => :w_format_tag_type_adpcm,
    3 => :w_format_tag_type_ieee_float,
    4 => :w_format_tag_type_vselp,
    5 => :w_format_tag_type_ibm_cvsd,
    6 => :w_format_tag_type_alaw,
    7 => :w_format_tag_type_mulaw,
    8 => :w_format_tag_type_dts,
    9 => :w_format_tag_type_drm,
    10 => :w_format_tag_type_wmavoice9,
    11 => :w_format_tag_type_wmavoice10,
    16 => :w_format_tag_type_oki_adpcm,
    17 => :w_format_tag_type_dvi_adpcm,
    18 => :w_format_tag_type_mediaspace_adpcm,
    19 => :w_format_tag_type_sierra_adpcm,
    20 => :w_format_tag_type_g723_adpcm,
    21 => :w_format_tag_type_digistd,
    22 => :w_format_tag_type_digifix,
    23 => :w_format_tag_type_dialogic_oki_adpcm,
    24 => :w_format_tag_type_mediavision_adpcm,
    25 => :w_format_tag_type_cu_codec,
    26 => :w_format_tag_type_hp_dyn_voice,
    32 => :w_format_tag_type_yamaha_adpcm,
    33 => :w_format_tag_type_sonarc,
    34 => :w_format_tag_type_dspgroup_truespeech,
    35 => :w_format_tag_type_echosc1,
    36 => :w_format_tag_type_audiofile_af36,
    37 => :w_format_tag_type_aptx,
    38 => :w_format_tag_type_audiofile_af10,
    39 => :w_format_tag_type_prosody_1612,
    40 => :w_format_tag_type_lrc,
    48 => :w_format_tag_type_dolby_ac2,
    49 => :w_format_tag_type_gsm610,
    50 => :w_format_tag_type_msnaudio,
    51 => :w_format_tag_type_antex_adpcme,
    52 => :w_format_tag_type_control_res_vqlpc,
    53 => :w_format_tag_type_digireal,
    54 => :w_format_tag_type_digiadpcm,
    55 => :w_format_tag_type_control_res_cr10,
    56 => :w_format_tag_type_nms_vbxadpcm,
    57 => :w_format_tag_type_cs_imaadpcm,
    58 => :w_format_tag_type_echosc3,
    59 => :w_format_tag_type_rockwell_adpcm,
    60 => :w_format_tag_type_rockwell_digitalk,
    61 => :w_format_tag_type_xebec,
    64 => :w_format_tag_type_g721_adpcm,
    65 => :w_format_tag_type_g728_celp,
    66 => :w_format_tag_type_msg723,
    67 => :w_format_tag_type_intel_g723_1,
    68 => :w_format_tag_type_intel_g729,
    69 => :w_format_tag_type_sharp_g726,
    80 => :w_format_tag_type_mpeg,
    82 => :w_format_tag_type_rt24,
    83 => :w_format_tag_type_pac,
    85 => :w_format_tag_type_mpeglayer3,
    89 => :w_format_tag_type_lucent_g723,
    96 => :w_format_tag_type_cirrus,
    97 => :w_format_tag_type_espcm,
    98 => :w_format_tag_type_voxware,
    99 => :w_format_tag_type_canopus_atrac,
    100 => :w_format_tag_type_g726_adpcm,
    101 => :w_format_tag_type_g722_adpcm,
    102 => :w_format_tag_type_dsat,
    103 => :w_format_tag_type_dsat_display,
    105 => :w_format_tag_type_voxware_byte_aligned,
    112 => :w_format_tag_type_voxware_ac8,
    113 => :w_format_tag_type_voxware_ac10,
    114 => :w_format_tag_type_voxware_ac16,
    115 => :w_format_tag_type_voxware_ac20,
    116 => :w_format_tag_type_voxware_rt24,
    117 => :w_format_tag_type_voxware_rt29,
    118 => :w_format_tag_type_voxware_rt29hw,
    119 => :w_format_tag_type_voxware_vr12,
    120 => :w_format_tag_type_voxware_vr18,
    121 => :w_format_tag_type_voxware_tq40,
    122 => :w_format_tag_type_voxware_sc3,
    123 => :w_format_tag_type_voxware_sc3_1,
    128 => :w_format_tag_type_softsound,
    129 => :w_format_tag_type_voxware_tq60,
    130 => :w_format_tag_type_msrt24,
    131 => :w_format_tag_type_g729a,
    132 => :w_format_tag_type_mvi_mvi2,
    133 => :w_format_tag_type_df_g726,
    134 => :w_format_tag_type_df_gsm610,
    136 => :w_format_tag_type_isiaudio,
    137 => :w_format_tag_type_onlive,
    138 => :w_format_tag_type_multitude_ft_sx20,
    139 => :w_format_tag_type_infocom_its_g721_adpcm,
    140 => :w_format_tag_type_convedia_g729,
    141 => :w_format_tag_type_congruency,
    145 => :w_format_tag_type_sbc24,
    146 => :w_format_tag_type_dolby_ac3_spdif,
    147 => :w_format_tag_type_mediasonic_g723,
    148 => :w_format_tag_type_prosody_8kbps,
    151 => :w_format_tag_type_zyxel_adpcm,
    152 => :w_format_tag_type_philips_lpcbb,
    153 => :w_format_tag_type_packed,
    160 => :w_format_tag_type_malden_phonytalk,
    161 => :w_format_tag_type_racal_recorder_gsm,
    162 => :w_format_tag_type_racal_recorder_g720_a,
    163 => :w_format_tag_type_racal_recorder_g723_1,
    164 => :w_format_tag_type_racal_recorder_tetra_acelp,
    176 => :w_format_tag_type_nec_aac,
    255 => :w_format_tag_type_raw_aac1,
    256 => :w_format_tag_type_rhetorex_adpcm,
    257 => :w_format_tag_type_irat,
    273 => :w_format_tag_type_vivo_g723,
    274 => :w_format_tag_type_vivo_siren,
    288 => :w_format_tag_type_philips_celp,
    289 => :w_format_tag_type_philips_grundig,
    291 => :w_format_tag_type_digital_g723,
    293 => :w_format_tag_type_sanyo_ld_adpcm,
    304 => :w_format_tag_type_siprolab_aceplnet,
    305 => :w_format_tag_type_siprolab_acelp4800,
    306 => :w_format_tag_type_siprolab_acelp8v3,
    307 => :w_format_tag_type_siprolab_g729,
    308 => :w_format_tag_type_siprolab_g729a,
    309 => :w_format_tag_type_siprolab_kelvin,
    310 => :w_format_tag_type_voiceage_amr,
    320 => :w_format_tag_type_g726adpcm,
    321 => :w_format_tag_type_dictaphone_celp68,
    322 => :w_format_tag_type_dictaphone_celp54,
    336 => :w_format_tag_type_qualcomm_purevoice,
    337 => :w_format_tag_type_qualcomm_halfrate,
    341 => :w_format_tag_type_tubgsm,
    352 => :w_format_tag_type_msaudio1,
    353 => :w_format_tag_type_wmaudio2,
    354 => :w_format_tag_type_wmaudio3,
    355 => :w_format_tag_type_wmaudio_lossless,
    356 => :w_format_tag_type_wmaspdif,
    368 => :w_format_tag_type_unisys_nap_adpcm,
    369 => :w_format_tag_type_unisys_nap_ulaw,
    370 => :w_format_tag_type_unisys_nap_alaw,
    371 => :w_format_tag_type_unisys_nap_16k,
    372 => :w_format_tag_type_sycom_acm_syc008,
    373 => :w_format_tag_type_sycom_acm_syc701_g726l,
    374 => :w_format_tag_type_sycom_acm_syc701_celp54,
    375 => :w_format_tag_type_sycom_acm_syc701_celp68,
    376 => :w_format_tag_type_knowledge_adventure_adpcm,
    384 => :w_format_tag_type_fraunhofer_iis_mpeg2_aac,
    400 => :w_format_tag_type_dts_ds,
    512 => :w_format_tag_type_creative_adpcm,
    514 => :w_format_tag_type_creative_fastspeech8,
    515 => :w_format_tag_type_creative_fastspeech10,
    528 => :w_format_tag_type_uher_adpcm,
    533 => :w_format_tag_type_ulead_dv_audio,
    534 => :w_format_tag_type_ulead_dv_audio_1,
    544 => :w_format_tag_type_quarterdeck,
    560 => :w_format_tag_type_ilink_vc,
    576 => :w_format_tag_type_raw_sport,
    577 => :w_format_tag_type_esst_ac3,
    585 => :w_format_tag_type_generic_passthru,
    592 => :w_format_tag_type_ipi_hsx,
    593 => :w_format_tag_type_ipi_rpelp,
    608 => :w_format_tag_type_cs2,
    624 => :w_format_tag_type_sony_scx,
    625 => :w_format_tag_type_sony_scy,
    626 => :w_format_tag_type_sony_atrac3,
    627 => :w_format_tag_type_sony_spc,
    640 => :w_format_tag_type_telum_audio,
    641 => :w_format_tag_type_telum_ia_audio,
    645 => :w_format_tag_type_norcom_voice_systems_adpcm,
    768 => :w_format_tag_type_fm_towns_snd,
    848 => :w_format_tag_type_micronas,
    849 => :w_format_tag_type_micronas_celp833,
    1024 => :w_format_tag_type_btv_digital,
    1025 => :w_format_tag_type_intel_music_coder,
    1026 => :w_format_tag_type_indeo_audio,
    1104 => :w_format_tag_type_qdesign_music,
    1280 => :w_format_tag_type_on2_vp7_audio,
    1281 => :w_format_tag_type_on2_vp6_audio,
    1664 => :w_format_tag_type_vme_vmpcm,
    1665 => :w_format_tag_type_tpc,
    2222 => :w_format_tag_type_lightwave_lossless,
    4096 => :w_format_tag_type_oligsm,
    4097 => :w_format_tag_type_oliadpcm,
    4098 => :w_format_tag_type_olicelp,
    4099 => :w_format_tag_type_olisbc,
    4100 => :w_format_tag_type_oliopr,
    4352 => :w_format_tag_type_lh_codec,
    4353 => :w_format_tag_type_lh_codec_celp,
    4354 => :w_format_tag_type_lh_codec_sbc8,
    4355 => :w_format_tag_type_lh_codec_sbc12,
    4356 => :w_format_tag_type_lh_codec_sbc16,
    5120 => :w_format_tag_type_norris,
    5121 => :w_format_tag_type_isiaudio_2,
    5376 => :w_format_tag_type_soundspace_musicompress,
    5632 => :w_format_tag_type_mpeg_adts_aac,
    5633 => :w_format_tag_type_mpeg_raw_aac,
    5634 => :w_format_tag_type_mpeg_loas,
    5640 => :w_format_tag_type_nokia_mpeg_adts_aac,
    5641 => :w_format_tag_type_nokia_mpeg_raw_aac,
    5642 => :w_format_tag_type_vodafone_mpeg_adts_aac,
    5643 => :w_format_tag_type_vodafone_mpeg_raw_aac,
    5648 => :w_format_tag_type_mpeg_heaac,
    6172 => :w_format_tag_type_voxware_rt24_speech,
    6513 => :w_format_tag_type_sonicfoundry_lossless,
    6521 => :w_format_tag_type_innings_telecom_adpcm,
    7175 => :w_format_tag_type_lucent_sx8300p,
    7180 => :w_format_tag_type_lucent_sx5363s,
    7939 => :w_format_tag_type_cuseeme,
    8132 => :w_format_tag_type_ntcsoft_alf2cm_acm,
    8192 => :w_format_tag_type_dvm,
    8193 => :w_format_tag_type_dts2,
    13075 => :w_format_tag_type_makeavis,
    16707 => :w_format_tag_type_divio_mpeg4_aac,
    16897 => :w_format_tag_type_nokia_adaptive_multirate,
    16963 => :w_format_tag_type_divio_g726,
    17228 => :w_format_tag_type_lead_speech,
    22092 => :w_format_tag_type_lead_vorbis,
    22358 => :w_format_tag_type_wavpack_audio,
    26447 => :w_format_tag_type_ogg_vorbis_mode_1,
    26448 => :w_format_tag_type_ogg_vorbis_mode_2,
    26449 => :w_format_tag_type_ogg_vorbis_mode_3,
    26479 => :w_format_tag_type_ogg_vorbis_mode_1_plus,
    26480 => :w_format_tag_type_ogg_vorbis_mode_2_plus,
    26481 => :w_format_tag_type_ogg_vorbis_mode_3_plus,
    28672 => :w_format_tag_type_threecom_nbx,
    28781 => :w_format_tag_type_faad_aac,
    29537 => :w_format_tag_type_amr_nb,
    29538 => :w_format_tag_type_amr_wb,
    29539 => :w_format_tag_type_amr_wp,
    31265 => :w_format_tag_type_gsm_amr_cbr,
    31266 => :w_format_tag_type_gsm_amr_vbr_sid,
    41216 => :w_format_tag_type_comverse_infosys_g723_1,
    41217 => :w_format_tag_type_comverse_infosys_avqsbc,
    41218 => :w_format_tag_type_comverse_infosys_sbc,
    41219 => :w_format_tag_type_symbol_g729_a,
    41220 => :w_format_tag_type_voiceage_amr_wb,
    41221 => :w_format_tag_type_ingenient_g726,
    41222 => :w_format_tag_type_mpeg4_aac,
    41223 => :w_format_tag_type_encore_g726,
    41224 => :w_format_tag_type_zoll_asao,
    41225 => :w_format_tag_type_speex_voice,
    41226 => :w_format_tag_type_vianix_masc,
    41227 => :w_format_tag_type_wm9_spectrum_analyzer,
    41228 => :w_format_tag_type_wmf_spectrum_anayzer,
    41229 => :w_format_tag_type_gsm_610,
    41230 => :w_format_tag_type_gsm_620,
    41231 => :w_format_tag_type_gsm_660,
    41232 => :w_format_tag_type_gsm_690,
    41233 => :w_format_tag_type_gsm_adaptive_multirate_wb,
    41234 => :w_format_tag_type_polycom_g722,
    41235 => :w_format_tag_type_polycom_g728,
    41236 => :w_format_tag_type_polycom_g729_a,
    41237 => :w_format_tag_type_polycom_siren,
    41238 => :w_format_tag_type_global_ip_ilbc,
    41239 => :w_format_tag_type_radiotime_time_shift_radio,
    41240 => :w_format_tag_type_nice_aca,
    41241 => :w_format_tag_type_nice_adpcm,
    41242 => :w_format_tag_type_vocord_g721,
    41243 => :w_format_tag_type_vocord_g726,
    41244 => :w_format_tag_type_vocord_g722_1,
    41245 => :w_format_tag_type_vocord_g728,
    41246 => :w_format_tag_type_vocord_g729,
    41247 => :w_format_tag_type_vocord_g729_a,
    41248 => :w_format_tag_type_vocord_g723_1,
    41249 => :w_format_tag_type_vocord_lbc,
    41250 => :w_format_tag_type_nice_g728,
    41251 => :w_format_tag_type_france_telecom_g729,
    41252 => :w_format_tag_type_codian,
    61868 => :w_format_tag_type_flac,
    65534 => :w_format_tag_type_extensible,
    65535 => :w_format_tag_type_development,
  }
  I__W_FORMAT_TAG_TYPE = W_FORMAT_TAG_TYPE.invert

  FOURCC = {
    540238953 => :fourcc_id3,
    543520099 => :fourcc_cue,
    544501094 => :fourcc_fmt,
    1163280727 => :fourcc_wave,
    1179011410 => :fourcc_riff,
    1262568784 => :fourcc_peak,
    1280137321 => :fourcc_ixml,
    1330007625 => :fourcc_info,
    1414744396 => :fourcc_list,
    1481461855 => :fourcc_pmx,
    1634625635 => :fourcc_chna,
    1635017060 => :fourcc_data,
    1684630901 => :fourcc_umid,
    1718511981 => :fourcc_minf,
    1819113569 => :fourcc_axml,
    1852269938 => :fourcc_regn,
    1886611041 => :fourcc_afsp,
    1952670054 => :fourcc_fact,
    1954047330 => :fourcc_bext,
  }
  I__FOURCC = FOURCC.invert
  def initialize(_io, _parent = nil, _root = self)
    super(_io, _parent, _root)
    _read
  end

  def _read
    @chunk = Riff::Chunk.new(@_io, self, @_root)
    self
  end
  class SampleType < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @sample = @_io.read_u2le
      self
    end
    attr_reader :sample
  end
  class FormatChunkType < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @w_format_tag = Kaitai::Struct::Stream::resolve_enum(Wav::W_FORMAT_TAG_TYPE, @_io.read_u2le)
      @n_channels = @_io.read_u2le
      @n_samples_per_sec = @_io.read_u4le
      @n_avg_bytes_per_sec = @_io.read_u4le
      @n_block_align = @_io.read_u2le
      @w_bits_per_sample = @_io.read_u2le
      if !(is_basic_pcm)
        @cb_size = @_io.read_u2le
      end
      if is_cb_size_meaningful
        @w_valid_bits_per_sample = @_io.read_u2le
      end
      if is_extensible
        @channel_mask_and_subformat = ChannelMaskAndSubformatType.new(@_io, self, @_root)
      end
      self
    end
    def is_extensible
      return @is_extensible unless @is_extensible.nil?
      @is_extensible = w_format_tag == :w_format_tag_type_extensible
      @is_extensible
    end
    def is_basic_pcm
      return @is_basic_pcm unless @is_basic_pcm.nil?
      @is_basic_pcm = w_format_tag == :w_format_tag_type_pcm
      @is_basic_pcm
    end
    def is_basic_float
      return @is_basic_float unless @is_basic_float.nil?
      @is_basic_float = w_format_tag == :w_format_tag_type_ieee_float
      @is_basic_float
    end
    def is_cb_size_meaningful
      return @is_cb_size_meaningful unless @is_cb_size_meaningful.nil?
      @is_cb_size_meaningful =  ((!(is_basic_pcm)) && (cb_size != 0)) 
      @is_cb_size_meaningful
    end
    attr_reader :w_format_tag
    attr_reader :n_channels
    attr_reader :n_samples_per_sec
    attr_reader :n_avg_bytes_per_sec
    attr_reader :n_block_align
    attr_reader :w_bits_per_sample
    attr_reader :cb_size
    attr_reader :w_valid_bits_per_sample
    attr_reader :channel_mask_and_subformat
  end
  class PmxChunkType < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @data = (@_io.read_bytes_full).force_encoding("UTF-8")
      self
    end

    ##
    # XMP data
    # @see https://github.com/adobe/XMP-Toolkit-SDK/blob/v2022.06/docs/XMPSpecificationPart3.pdf Source
    attr_reader :data
  end

  ##
  # required for all non-PCM formats
  # (`w_format_tag != w_format_tag_type::pcm` or `not is_basic_pcm` in
  # `format_chunk_type` context)
  class FactChunkType < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @num_samples_per_channel = @_io.read_u4le
      self
    end
    attr_reader :num_samples_per_channel
  end
  class GuidType < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @data1 = @_io.read_u4le
      @data2 = @_io.read_u2le
      @data3 = @_io.read_u2le
      @data4 = @_io.read_u4be
      @data4a = @_io.read_u4be
      self
    end
    attr_reader :data1
    attr_reader :data2
    attr_reader :data3
    attr_reader :data4
    attr_reader :data4a
  end

  ##
  # @see https://en.wikipedia.org/wiki/IXML Source
  class IxmlChunkType < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @data = (@_io.read_bytes_full).force_encoding("UTF-8")
      self
    end
    attr_reader :data
  end
  class InfoChunkType < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @chunk = Riff::Chunk.new(@_io, self, @_root)
      self
    end
    def chunk_data
      return @chunk_data unless @chunk_data.nil?
      io = chunk.data_slot._io
      _pos = io.pos
      io.seek(0)
      @chunk_data = (io.read_bytes_term(0, false, true, true)).force_encoding("ASCII")
      io.seek(_pos)
      @chunk_data
    end
    attr_reader :chunk
  end
  class CuePointType < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @dw_name = @_io.read_u4le
      @dw_position = @_io.read_u4le
      @fcc_chunk = @_io.read_u4le
      @dw_chunk_start = @_io.read_u4le
      @dw_block_start = @_io.read_u4le
      @dw_sample_offset = @_io.read_u4le
      self
    end
    attr_reader :dw_name
    attr_reader :dw_position
    attr_reader :fcc_chunk
    attr_reader :dw_chunk_start
    attr_reader :dw_block_start
    attr_reader :dw_sample_offset
  end
  class DataChunkType < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @data = @_io.read_bytes_full
      self
    end
    attr_reader :data
  end
  class SamplesType < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @samples = @_io.read_u4le
      self
    end
    attr_reader :samples
  end
  class ChannelMaskAndSubformatType < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @dw_channel_mask = ChannelMaskType.new(@_io, self, @_root)
      @subformat = GuidType.new(@_io, self, @_root)
      self
    end
    attr_reader :dw_channel_mask
    attr_reader :subformat
  end
  class CueChunkType < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @dw_cue_points = @_io.read_u4le
      @cue_points = []
      (dw_cue_points).times { |i|
        @cue_points << CuePointType.new(@_io, self, @_root)
      }
      self
    end
    attr_reader :dw_cue_points
    attr_reader :cue_points
  end
  class ListChunkType < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @parent_chunk_data = Riff::ParentChunkData.new(@_io, self, @_root)
      self
    end
    def form_type
      return @form_type unless @form_type.nil?
      @form_type = Kaitai::Struct::Stream::resolve_enum(Wav::FOURCC, parent_chunk_data.form_type)
      @form_type
    end
    def subchunks
      return @subchunks unless @subchunks.nil?
      io = parent_chunk_data.subchunks_slot._io
      _pos = io.pos
      io.seek(0)
      @subchunks = []
      i = 0
      while not io.eof?
        case form_type
        when :fourcc_info
          @subchunks << InfoChunkType.new(io, self, @_root)
        end
        i += 1
      end
      io.seek(_pos)
      @subchunks
    end
    attr_reader :parent_chunk_data
  end
  class ChannelMaskType < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @front_right_of_center = @_io.read_bits_int_be(1) != 0
      @front_left_of_center = @_io.read_bits_int_be(1) != 0
      @back_right = @_io.read_bits_int_be(1) != 0
      @back_left = @_io.read_bits_int_be(1) != 0
      @low_frequency = @_io.read_bits_int_be(1) != 0
      @front_center = @_io.read_bits_int_be(1) != 0
      @front_right = @_io.read_bits_int_be(1) != 0
      @front_left = @_io.read_bits_int_be(1) != 0
      @top_center = @_io.read_bits_int_be(1) != 0
      @side_right = @_io.read_bits_int_be(1) != 0
      @side_left = @_io.read_bits_int_be(1) != 0
      @back_center = @_io.read_bits_int_be(1) != 0
      @top_back_left = @_io.read_bits_int_be(1) != 0
      @top_front_right = @_io.read_bits_int_be(1) != 0
      @top_front_center = @_io.read_bits_int_be(1) != 0
      @top_front_left = @_io.read_bits_int_be(1) != 0
      @unused1 = @_io.read_bits_int_be(6)
      @top_back_right = @_io.read_bits_int_be(1) != 0
      @top_back_center = @_io.read_bits_int_be(1) != 0
      @unused2 = @_io.read_bits_int_be(8)
      self
    end
    attr_reader :front_right_of_center
    attr_reader :front_left_of_center
    attr_reader :back_right
    attr_reader :back_left
    attr_reader :low_frequency
    attr_reader :front_center
    attr_reader :front_right
    attr_reader :front_left
    attr_reader :top_center
    attr_reader :side_right
    attr_reader :side_left
    attr_reader :back_center
    attr_reader :top_back_left
    attr_reader :top_front_right
    attr_reader :top_front_center
    attr_reader :top_front_left
    attr_reader :unused1
    attr_reader :top_back_right
    attr_reader :top_back_center
    attr_reader :unused2
  end

  ##
  # @see https://www.mmsp.ece.mcgill.ca/Documents/Downloads/AFsp/ Source
  class AfspChunkType < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @magic = @_io.read_bytes(4)
      raise Kaitai::Struct::ValidationNotEqualError.new([65, 70, 115, 112].pack('C*'), magic, _io, "/types/afsp_chunk_type/seq/0") if not magic == [65, 70, 115, 112].pack('C*')
      @info_records = []
      i = 0
      while not @_io.eof?
        @info_records << (@_io.read_bytes_term(0, false, true, true)).force_encoding("ASCII")
        i += 1
      end
      self
    end
    attr_reader :magic

    ##
    # An array of AFsp information records, in the `<field_name>: <value>`
    # format (e.g. "`program: CopyAudio`"). The list of existing information
    # record types are available in the `doc-ref` links.
    # @see https://www.mmsp.ece.mcgill.ca/Documents/Software/Packages/AFsp/libtsp/AF/AFsetInfo.html Source
    # @see https://www.mmsp.ece.mcgill.ca/Documents/Software/Packages/AFsp/libtsp/AF/AFprintInfoRecs.html Source
    attr_reader :info_records
  end

  ##
  # @see https://tech.ebu.ch/docs/tech/tech3285s5.pdf Source
  class AxmlChunkType < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @data = (@_io.read_bytes_full).force_encoding("UTF-8")
      self
    end
    attr_reader :data
  end
  class ChunkType < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @chunk = Riff::Chunk.new(@_io, self, @_root)
      self
    end
    def chunk_id
      return @chunk_id unless @chunk_id.nil?
      @chunk_id = Kaitai::Struct::Stream::resolve_enum(Wav::FOURCC, chunk.id)
      @chunk_id
    end
    def chunk_data
      return @chunk_data unless @chunk_data.nil?
      io = chunk.data_slot._io
      _pos = io.pos
      io.seek(0)
      case chunk_id
      when :fourcc_fact
        @chunk_data = FactChunkType.new(io, self, @_root)
      when :fourcc_list
        @chunk_data = ListChunkType.new(io, self, @_root)
      when :fourcc_fmt
        @chunk_data = FormatChunkType.new(io, self, @_root)
      when :fourcc_afsp
        @chunk_data = AfspChunkType.new(io, self, @_root)
      when :fourcc_bext
        @chunk_data = BextChunkType.new(io, self, @_root)
      when :fourcc_cue
        @chunk_data = CueChunkType.new(io, self, @_root)
      when :fourcc_ixml
        @chunk_data = IxmlChunkType.new(io, self, @_root)
      when :fourcc_pmx
        @chunk_data = PmxChunkType.new(io, self, @_root)
      when :fourcc_axml
        @chunk_data = AxmlChunkType.new(io, self, @_root)
      when :fourcc_data
        @chunk_data = DataChunkType.new(io, self, @_root)
      end
      io.seek(_pos)
      @chunk_data
    end
    attr_reader :chunk
  end

  ##
  # @see https://en.wikipedia.org/wiki/Broadcast_Wave_Format Source
  class BextChunkType < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @description = (Kaitai::Struct::Stream::bytes_terminate(@_io.read_bytes(256), 0, false)).force_encoding("ASCII")
      @originator = (Kaitai::Struct::Stream::bytes_terminate(@_io.read_bytes(32), 0, false)).force_encoding("ASCII")
      @originator_reference = (Kaitai::Struct::Stream::bytes_terminate(@_io.read_bytes(32), 0, false)).force_encoding("ASCII")
      @origination_date = (@_io.read_bytes(10)).force_encoding("ASCII")
      @origination_time = (@_io.read_bytes(8)).force_encoding("ASCII")
      @time_reference_low = @_io.read_u4le
      @time_reference_high = @_io.read_u4le
      @version = @_io.read_u2le
      @umid = @_io.read_bytes(64)
      @loudness_value = @_io.read_u2le
      @loudness_range = @_io.read_u2le
      @max_true_peak_level = @_io.read_u2le
      @max_momentary_loudness = @_io.read_u2le
      @max_short_term_loudness = @_io.read_u2le
      self
    end
    attr_reader :description
    attr_reader :originator
    attr_reader :originator_reference
    attr_reader :origination_date
    attr_reader :origination_time
    attr_reader :time_reference_low
    attr_reader :time_reference_high
    attr_reader :version
    attr_reader :umid
    attr_reader :loudness_value
    attr_reader :loudness_range
    attr_reader :max_true_peak_level
    attr_reader :max_momentary_loudness
    attr_reader :max_short_term_loudness
  end
  def subchunks
    return @subchunks unless @subchunks.nil?
    if is_form_type_wave
      io = parent_chunk_data.subchunks_slot._io
      _pos = io.pos
      io.seek(0)
      @subchunks = []
      i = 0
      while not io.eof?
        @subchunks << ChunkType.new(io, self, @_root)
        i += 1
      end
      io.seek(_pos)
    end
    @subchunks
  end
  def parent_chunk_data
    return @parent_chunk_data unless @parent_chunk_data.nil?
    if is_riff_chunk
      io = chunk.data_slot._io
      _pos = io.pos
      io.seek(0)
      @parent_chunk_data = Riff::ParentChunkData.new(io, self, @_root)
      io.seek(_pos)
    end
    @parent_chunk_data
  end
  def is_form_type_wave
    return @is_form_type_wave unless @is_form_type_wave.nil?
    @is_form_type_wave =  ((is_riff_chunk) && (form_type == :fourcc_wave)) 
    @is_form_type_wave
  end
  def is_riff_chunk
    return @is_riff_chunk unless @is_riff_chunk.nil?
    @is_riff_chunk = chunk_id == :fourcc_riff
    @is_riff_chunk
  end
  def chunk_id
    return @chunk_id unless @chunk_id.nil?
    @chunk_id = Kaitai::Struct::Stream::resolve_enum(FOURCC, chunk.id)
    @chunk_id
  end
  def form_type
    return @form_type unless @form_type.nil?
    @form_type = Kaitai::Struct::Stream::resolve_enum(FOURCC, parent_chunk_data.form_type)
    @form_type
  end
  attr_reader :chunk
end