Microsoft WAVE audio file: Nim 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.

Nim source code to parse Microsoft WAVE audio file

wav.nim

import kaitai_struct_nim_runtime
import options
import /common/riff

type
  Wav* = ref object of KaitaiStruct
    `chunk`*: Riff_Chunk
    `parent`*: KaitaiStruct
    `subchunksInst`: seq[Wav_ChunkType]
    `subchunksInstFlag`: bool
    `parentChunkDataInst`: Riff_ParentChunkData
    `parentChunkDataInstFlag`: bool
    `isFormTypeWaveInst`: bool
    `isFormTypeWaveInstFlag`: bool
    `isRiffChunkInst`: bool
    `isRiffChunkInstFlag`: bool
    `chunkIdInst`: Wav_Fourcc
    `chunkIdInstFlag`: bool
    `formTypeInst`: Wav_Fourcc
    `formTypeInstFlag`: bool
  Wav_WFormatTagType* = enum
    unknown = 0
    pcm = 1
    adpcm = 2
    ieee_float = 3
    vselp = 4
    ibm_cvsd = 5
    alaw = 6
    mulaw = 7
    dts = 8
    drm = 9
    wmavoice9 = 10
    wmavoice10 = 11
    oki_adpcm = 16
    dvi_adpcm = 17
    mediaspace_adpcm = 18
    sierra_adpcm = 19
    g723_adpcm = 20
    digistd = 21
    digifix = 22
    dialogic_oki_adpcm = 23
    mediavision_adpcm = 24
    cu_codec = 25
    hp_dyn_voice = 26
    yamaha_adpcm = 32
    sonarc = 33
    dspgroup_truespeech = 34
    echosc1 = 35
    audiofile_af36 = 36
    aptx = 37
    audiofile_af10 = 38
    prosody_1612 = 39
    lrc = 40
    dolby_ac2 = 48
    gsm610 = 49
    msnaudio = 50
    antex_adpcme = 51
    control_res_vqlpc = 52
    digireal = 53
    digiadpcm = 54
    control_res_cr10 = 55
    nms_vbxadpcm = 56
    cs_imaadpcm = 57
    echosc3 = 58
    rockwell_adpcm = 59
    rockwell_digitalk = 60
    xebec = 61
    g721_adpcm = 64
    g728_celp = 65
    msg723 = 66
    intel_g723_1 = 67
    intel_g729 = 68
    sharp_g726 = 69
    mpeg = 80
    rt24 = 82
    pac = 83
    mpeglayer3 = 85
    lucent_g723 = 89
    cirrus = 96
    espcm = 97
    voxware = 98
    canopus_atrac = 99
    g726_adpcm = 100
    g722_adpcm = 101
    dsat = 102
    dsat_display = 103
    voxware_byte_aligned = 105
    voxware_ac8 = 112
    voxware_ac10 = 113
    voxware_ac16 = 114
    voxware_ac20 = 115
    voxware_rt24 = 116
    voxware_rt29 = 117
    voxware_rt29hw = 118
    voxware_vr12 = 119
    voxware_vr18 = 120
    voxware_tq40 = 121
    voxware_sc3 = 122
    voxware_sc3_1 = 123
    softsound = 128
    voxware_tq60 = 129
    msrt24 = 130
    g729a = 131
    mvi_mvi2 = 132
    df_g726 = 133
    df_gsm610 = 134
    isiaudio = 136
    onlive = 137
    multitude_ft_sx20 = 138
    infocom_its_g721_adpcm = 139
    convedia_g729 = 140
    congruency = 141
    sbc24 = 145
    dolby_ac3_spdif = 146
    mediasonic_g723 = 147
    prosody_8kbps = 148
    zyxel_adpcm = 151
    philips_lpcbb = 152
    packed = 153
    malden_phonytalk = 160
    racal_recorder_gsm = 161
    racal_recorder_g720_a = 162
    racal_recorder_g723_1 = 163
    racal_recorder_tetra_acelp = 164
    nec_aac = 176
    raw_aac1 = 255
    rhetorex_adpcm = 256
    irat = 257
    vivo_g723 = 273
    vivo_siren = 274
    philips_celp = 288
    philips_grundig = 289
    digital_g723 = 291
    sanyo_ld_adpcm = 293
    siprolab_aceplnet = 304
    siprolab_acelp4800 = 305
    siprolab_acelp8v3 = 306
    siprolab_g729 = 307
    siprolab_g729a = 308
    siprolab_kelvin = 309
    voiceage_amr = 310
    g726adpcm = 320
    dictaphone_celp68 = 321
    dictaphone_celp54 = 322
    qualcomm_purevoice = 336
    qualcomm_halfrate = 337
    tubgsm = 341
    msaudio1 = 352
    wmaudio2 = 353
    wmaudio3 = 354
    wmaudio_lossless = 355
    wmaspdif = 356
    unisys_nap_adpcm = 368
    unisys_nap_ulaw = 369
    unisys_nap_alaw = 370
    unisys_nap_16k = 371
    sycom_acm_syc008 = 372
    sycom_acm_syc701_g726l = 373
    sycom_acm_syc701_celp54 = 374
    sycom_acm_syc701_celp68 = 375
    knowledge_adventure_adpcm = 376
    fraunhofer_iis_mpeg2_aac = 384
    dts_ds = 400
    creative_adpcm = 512
    creative_fastspeech8 = 514
    creative_fastspeech10 = 515
    uher_adpcm = 528
    ulead_dv_audio = 533
    ulead_dv_audio_1 = 534
    quarterdeck = 544
    ilink_vc = 560
    raw_sport = 576
    esst_ac3 = 577
    generic_passthru = 585
    ipi_hsx = 592
    ipi_rpelp = 593
    cs2 = 608
    sony_scx = 624
    sony_scy = 625
    sony_atrac3 = 626
    sony_spc = 627
    telum_audio = 640
    telum_ia_audio = 641
    norcom_voice_systems_adpcm = 645
    fm_towns_snd = 768
    micronas = 848
    micronas_celp833 = 849
    btv_digital = 1024
    intel_music_coder = 1025
    indeo_audio = 1026
    qdesign_music = 1104
    on2_vp7_audio = 1280
    on2_vp6_audio = 1281
    vme_vmpcm = 1664
    tpc = 1665
    lightwave_lossless = 2222
    oligsm = 4096
    oliadpcm = 4097
    olicelp = 4098
    olisbc = 4099
    oliopr = 4100
    lh_codec = 4352
    lh_codec_celp = 4353
    lh_codec_sbc8 = 4354
    lh_codec_sbc12 = 4355
    lh_codec_sbc16 = 4356
    norris = 5120
    isiaudio_2 = 5121
    soundspace_musicompress = 5376
    mpeg_adts_aac = 5632
    mpeg_raw_aac = 5633
    mpeg_loas = 5634
    nokia_mpeg_adts_aac = 5640
    nokia_mpeg_raw_aac = 5641
    vodafone_mpeg_adts_aac = 5642
    vodafone_mpeg_raw_aac = 5643
    mpeg_heaac = 5648
    voxware_rt24_speech = 6172
    sonicfoundry_lossless = 6513
    innings_telecom_adpcm = 6521
    lucent_sx8300p = 7175
    lucent_sx5363s = 7180
    cuseeme = 7939
    ntcsoft_alf2cm_acm = 8132
    dvm = 8192
    dts2 = 8193
    makeavis = 13075
    divio_mpeg4_aac = 16707
    nokia_adaptive_multirate = 16897
    divio_g726 = 16963
    lead_speech = 17228
    lead_vorbis = 22092
    wavpack_audio = 22358
    ogg_vorbis_mode_1 = 26447
    ogg_vorbis_mode_2 = 26448
    ogg_vorbis_mode_3 = 26449
    ogg_vorbis_mode_1_plus = 26479
    ogg_vorbis_mode_2_plus = 26480
    ogg_vorbis_mode_3_plus = 26481
    threecom_nbx = 28672
    faad_aac = 28781
    amr_nb = 29537
    amr_wb = 29538
    amr_wp = 29539
    gsm_amr_cbr = 31265
    gsm_amr_vbr_sid = 31266
    comverse_infosys_g723_1 = 41216
    comverse_infosys_avqsbc = 41217
    comverse_infosys_sbc = 41218
    symbol_g729_a = 41219
    voiceage_amr_wb = 41220
    ingenient_g726 = 41221
    mpeg4_aac = 41222
    encore_g726 = 41223
    zoll_asao = 41224
    speex_voice = 41225
    vianix_masc = 41226
    wm9_spectrum_analyzer = 41227
    wmf_spectrum_anayzer = 41228
    gsm_610 = 41229
    gsm_620 = 41230
    gsm_660 = 41231
    gsm_690 = 41232
    gsm_adaptive_multirate_wb = 41233
    polycom_g722 = 41234
    polycom_g728 = 41235
    polycom_g729_a = 41236
    polycom_siren = 41237
    global_ip_ilbc = 41238
    radiotime_time_shift_radio = 41239
    nice_aca = 41240
    nice_adpcm = 41241
    vocord_g721 = 41242
    vocord_g726 = 41243
    vocord_g722_1 = 41244
    vocord_g728 = 41245
    vocord_g729 = 41246
    vocord_g729_a = 41247
    vocord_g723_1 = 41248
    vocord_lbc = 41249
    nice_g728 = 41250
    france_telecom_g729 = 41251
    codian = 41252
    flac = 61868
    extensible = 65534
    development = 65535
  Wav_Fourcc* = enum
    id3 = 540238953
    cue = 543520099
    fmt = 544501094
    wave = 1163280727
    riff = 1179011410
    peak = 1262568784
    ixml = 1280137321
    info = 1330007625
    list = 1414744396
    pmx = 1481461855
    chna = 1634625635
    data = 1635017060
    umid = 1684630901
    minf = 1718511981
    axml = 1819113569
    regn = 1852269938
    afsp = 1886611041
    fact = 1952670054
    bext = 1954047330
  Wav_SampleType* = ref object of KaitaiStruct
    `sample`*: uint16
    `parent`*: KaitaiStruct
  Wav_FormatChunkType* = ref object of KaitaiStruct
    `wFormatTag`*: Wav_WFormatTagType
    `nChannels`*: uint16
    `nSamplesPerSec`*: uint32
    `nAvgBytesPerSec`*: uint32
    `nBlockAlign`*: uint16
    `wBitsPerSample`*: uint16
    `cbSize`*: uint16
    `wValidBitsPerSample`*: uint16
    `channelMaskAndSubformat`*: Wav_ChannelMaskAndSubformatType
    `parent`*: Wav_ChunkType
    `isExtensibleInst`: bool
    `isExtensibleInstFlag`: bool
    `isBasicPcmInst`: bool
    `isBasicPcmInstFlag`: bool
    `isBasicFloatInst`: bool
    `isBasicFloatInstFlag`: bool
    `isCbSizeMeaningfulInst`: bool
    `isCbSizeMeaningfulInstFlag`: bool
  Wav_PmxChunkType* = ref object of KaitaiStruct
    `data`*: string
    `parent`*: Wav_ChunkType
  Wav_FactChunkType* = ref object of KaitaiStruct
    `numSamplesPerChannel`*: uint32
    `parent`*: Wav_ChunkType
  Wav_GuidType* = ref object of KaitaiStruct
    `data1`*: uint32
    `data2`*: uint16
    `data3`*: uint16
    `data4`*: uint32
    `data4a`*: uint32
    `parent`*: Wav_ChannelMaskAndSubformatType
  Wav_IxmlChunkType* = ref object of KaitaiStruct
    `data`*: string
    `parent`*: Wav_ChunkType
  Wav_InfoChunkType* = ref object of KaitaiStruct
    `chunk`*: Riff_Chunk
    `parent`*: Wav_ListChunkType
    `chunkDataInst`: string
    `chunkDataInstFlag`: bool
  Wav_CuePointType* = ref object of KaitaiStruct
    `dwName`*: uint32
    `dwPosition`*: uint32
    `fccChunk`*: uint32
    `dwChunkStart`*: uint32
    `dwBlockStart`*: uint32
    `dwSampleOffset`*: uint32
    `parent`*: Wav_CueChunkType
  Wav_DataChunkType* = ref object of KaitaiStruct
    `data`*: seq[byte]
    `parent`*: Wav_ChunkType
  Wav_SamplesType* = ref object of KaitaiStruct
    `samples`*: uint32
    `parent`*: KaitaiStruct
  Wav_ChannelMaskAndSubformatType* = ref object of KaitaiStruct
    `dwChannelMask`*: Wav_ChannelMaskType
    `subformat`*: Wav_GuidType
    `parent`*: Wav_FormatChunkType
  Wav_CueChunkType* = ref object of KaitaiStruct
    `dwCuePoints`*: uint32
    `cuePoints`*: seq[Wav_CuePointType]
    `parent`*: Wav_ChunkType
  Wav_ListChunkType* = ref object of KaitaiStruct
    `parentChunkData`*: Riff_ParentChunkData
    `parent`*: Wav_ChunkType
    `formTypeInst`: Wav_Fourcc
    `formTypeInstFlag`: bool
    `subchunksInst`: seq[Wav_InfoChunkType]
    `subchunksInstFlag`: bool
  Wav_ChannelMaskType* = ref object of KaitaiStruct
    `frontRightOfCenter`*: bool
    `frontLeftOfCenter`*: bool
    `backRight`*: bool
    `backLeft`*: bool
    `lowFrequency`*: bool
    `frontCenter`*: bool
    `frontRight`*: bool
    `frontLeft`*: bool
    `topCenter`*: bool
    `sideRight`*: bool
    `sideLeft`*: bool
    `backCenter`*: bool
    `topBackLeft`*: bool
    `topFrontRight`*: bool
    `topFrontCenter`*: bool
    `topFrontLeft`*: bool
    `unused1`*: uint64
    `topBackRight`*: bool
    `topBackCenter`*: bool
    `unused2`*: uint64
    `parent`*: Wav_ChannelMaskAndSubformatType
  Wav_AfspChunkType* = ref object of KaitaiStruct
    `magic`*: seq[byte]
    `infoRecords`*: seq[string]
    `parent`*: Wav_ChunkType
  Wav_AxmlChunkType* = ref object of KaitaiStruct
    `data`*: string
    `parent`*: Wav_ChunkType
  Wav_ChunkType* = ref object of KaitaiStruct
    `chunk`*: Riff_Chunk
    `parent`*: Wav
    `chunkIdInst`: Wav_Fourcc
    `chunkIdInstFlag`: bool
    `chunkDataInst`: KaitaiStruct
    `chunkDataInstFlag`: bool
  Wav_BextChunkType* = ref object of KaitaiStruct
    `description`*: string
    `originator`*: string
    `originatorReference`*: string
    `originationDate`*: string
    `originationTime`*: string
    `timeReferenceLow`*: uint32
    `timeReferenceHigh`*: uint32
    `version`*: uint16
    `umid`*: seq[byte]
    `loudnessValue`*: uint16
    `loudnessRange`*: uint16
    `maxTruePeakLevel`*: uint16
    `maxMomentaryLoudness`*: uint16
    `maxShortTermLoudness`*: uint16
    `parent`*: Wav_ChunkType

proc read*(_: typedesc[Wav], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct): Wav
proc read*(_: typedesc[Wav_SampleType], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct): Wav_SampleType
proc read*(_: typedesc[Wav_FormatChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_FormatChunkType
proc read*(_: typedesc[Wav_PmxChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_PmxChunkType
proc read*(_: typedesc[Wav_FactChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_FactChunkType
proc read*(_: typedesc[Wav_GuidType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChannelMaskAndSubformatType): Wav_GuidType
proc read*(_: typedesc[Wav_IxmlChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_IxmlChunkType
proc read*(_: typedesc[Wav_InfoChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ListChunkType): Wav_InfoChunkType
proc read*(_: typedesc[Wav_CuePointType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_CueChunkType): Wav_CuePointType
proc read*(_: typedesc[Wav_DataChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_DataChunkType
proc read*(_: typedesc[Wav_SamplesType], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct): Wav_SamplesType
proc read*(_: typedesc[Wav_ChannelMaskAndSubformatType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_FormatChunkType): Wav_ChannelMaskAndSubformatType
proc read*(_: typedesc[Wav_CueChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_CueChunkType
proc read*(_: typedesc[Wav_ListChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_ListChunkType
proc read*(_: typedesc[Wav_ChannelMaskType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChannelMaskAndSubformatType): Wav_ChannelMaskType
proc read*(_: typedesc[Wav_AfspChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_AfspChunkType
proc read*(_: typedesc[Wav_AxmlChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_AxmlChunkType
proc read*(_: typedesc[Wav_ChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav): Wav_ChunkType
proc read*(_: typedesc[Wav_BextChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_BextChunkType

proc subchunks*(this: Wav): seq[Wav_ChunkType]
proc parentChunkData*(this: Wav): Riff_ParentChunkData
proc isFormTypeWave*(this: Wav): bool
proc isRiffChunk*(this: Wav): bool
proc chunkId*(this: Wav): Wav_Fourcc
proc formType*(this: Wav): Wav_Fourcc
proc isExtensible*(this: Wav_FormatChunkType): bool
proc isBasicPcm*(this: Wav_FormatChunkType): bool
proc isBasicFloat*(this: Wav_FormatChunkType): bool
proc isCbSizeMeaningful*(this: Wav_FormatChunkType): bool
proc chunkData*(this: Wav_InfoChunkType): string
proc formType*(this: Wav_ListChunkType): Wav_Fourcc
proc subchunks*(this: Wav_ListChunkType): seq[Wav_InfoChunkType]
proc chunkId*(this: Wav_ChunkType): Wav_Fourcc
proc chunkData*(this: Wav_ChunkType): KaitaiStruct


##[
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 <a href="http://soundfile.sapp.org/doc/WaveFormat/">Source</a>
@see <a href="https://www.mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html">Source</a>
@see <a href="https://web.archive.org/web/20101031101749/http://www.ebu.ch/fr/technical/publications/userguides/bwf_user_guide.php">Source</a>
]##
proc read*(_: typedesc[Wav], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct): Wav =
  template this: untyped = result
  this = new(Wav)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent

  let chunkExpr = Riff_Chunk.read(this.io, this.root, this)
  this.chunk = chunkExpr

proc subchunks(this: Wav): seq[Wav_ChunkType] = 
  if this.subchunksInstFlag:
    return this.subchunksInst
  if this.isFormTypeWave:
    let io = this.parentChunkData.subchunksSlot.io
    let pos = io.pos()
    io.seek(int(0))
    block:
      var i: int
      while not io.isEof:
        let it = Wav_ChunkType.read(io, this.root, this)
        this.subchunksInst.add(it)
        inc i
    io.seek(pos)
  this.subchunksInstFlag = true
  return this.subchunksInst

proc parentChunkData(this: Wav): Riff_ParentChunkData = 
  if this.parentChunkDataInstFlag:
    return this.parentChunkDataInst
  if this.isRiffChunk:
    let io = this.chunk.dataSlot.io
    let pos = io.pos()
    io.seek(int(0))
    let parentChunkDataInstExpr = Riff_ParentChunkData.read(io, this.root, this)
    this.parentChunkDataInst = parentChunkDataInstExpr
    io.seek(pos)
  this.parentChunkDataInstFlag = true
  return this.parentChunkDataInst

proc isFormTypeWave(this: Wav): bool = 
  if this.isFormTypeWaveInstFlag:
    return this.isFormTypeWaveInst
  let isFormTypeWaveInstExpr = bool( ((this.isRiffChunk) and (this.formType == wav.wave)) )
  this.isFormTypeWaveInst = isFormTypeWaveInstExpr
  this.isFormTypeWaveInstFlag = true
  return this.isFormTypeWaveInst

proc isRiffChunk(this: Wav): bool = 
  if this.isRiffChunkInstFlag:
    return this.isRiffChunkInst
  let isRiffChunkInstExpr = bool(this.chunkId == wav.riff)
  this.isRiffChunkInst = isRiffChunkInstExpr
  this.isRiffChunkInstFlag = true
  return this.isRiffChunkInst

proc chunkId(this: Wav): Wav_Fourcc = 
  if this.chunkIdInstFlag:
    return this.chunkIdInst
  let chunkIdInstExpr = Wav_Fourcc(Wav_Fourcc(this.chunk.id))
  this.chunkIdInst = chunkIdInstExpr
  this.chunkIdInstFlag = true
  return this.chunkIdInst

proc formType(this: Wav): Wav_Fourcc = 
  if this.formTypeInstFlag:
    return this.formTypeInst
  let formTypeInstExpr = Wav_Fourcc(Wav_Fourcc(this.parentChunkData.formType))
  this.formTypeInst = formTypeInstExpr
  this.formTypeInstFlag = true
  return this.formTypeInst

proc fromFile*(_: typedesc[Wav], filename: string): Wav =
  Wav.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Wav_SampleType], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct): Wav_SampleType =
  template this: untyped = result
  this = new(Wav_SampleType)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent

  let sampleExpr = this.io.readU2le()
  this.sample = sampleExpr

proc fromFile*(_: typedesc[Wav_SampleType], filename: string): Wav_SampleType =
  Wav_SampleType.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Wav_FormatChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_FormatChunkType =
  template this: untyped = result
  this = new(Wav_FormatChunkType)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent

  let wFormatTagExpr = Wav_WFormatTagType(this.io.readU2le())
  this.wFormatTag = wFormatTagExpr
  let nChannelsExpr = this.io.readU2le()
  this.nChannels = nChannelsExpr
  let nSamplesPerSecExpr = this.io.readU4le()
  this.nSamplesPerSec = nSamplesPerSecExpr
  let nAvgBytesPerSecExpr = this.io.readU4le()
  this.nAvgBytesPerSec = nAvgBytesPerSecExpr
  let nBlockAlignExpr = this.io.readU2le()
  this.nBlockAlign = nBlockAlignExpr
  let wBitsPerSampleExpr = this.io.readU2le()
  this.wBitsPerSample = wBitsPerSampleExpr
  if not(this.isBasicPcm):
    let cbSizeExpr = this.io.readU2le()
    this.cbSize = cbSizeExpr
  if this.isCbSizeMeaningful:
    let wValidBitsPerSampleExpr = this.io.readU2le()
    this.wValidBitsPerSample = wValidBitsPerSampleExpr
  if this.isExtensible:
    let channelMaskAndSubformatExpr = Wav_ChannelMaskAndSubformatType.read(this.io, this.root, this)
    this.channelMaskAndSubformat = channelMaskAndSubformatExpr

proc isExtensible(this: Wav_FormatChunkType): bool = 
  if this.isExtensibleInstFlag:
    return this.isExtensibleInst
  let isExtensibleInstExpr = bool(this.wFormatTag == wav.extensible)
  this.isExtensibleInst = isExtensibleInstExpr
  this.isExtensibleInstFlag = true
  return this.isExtensibleInst

proc isBasicPcm(this: Wav_FormatChunkType): bool = 
  if this.isBasicPcmInstFlag:
    return this.isBasicPcmInst
  let isBasicPcmInstExpr = bool(this.wFormatTag == wav.pcm)
  this.isBasicPcmInst = isBasicPcmInstExpr
  this.isBasicPcmInstFlag = true
  return this.isBasicPcmInst

proc isBasicFloat(this: Wav_FormatChunkType): bool = 
  if this.isBasicFloatInstFlag:
    return this.isBasicFloatInst
  let isBasicFloatInstExpr = bool(this.wFormatTag == wav.ieee_float)
  this.isBasicFloatInst = isBasicFloatInstExpr
  this.isBasicFloatInstFlag = true
  return this.isBasicFloatInst

proc isCbSizeMeaningful(this: Wav_FormatChunkType): bool = 
  if this.isCbSizeMeaningfulInstFlag:
    return this.isCbSizeMeaningfulInst
  let isCbSizeMeaningfulInstExpr = bool( ((not(this.isBasicPcm)) and (this.cbSize != 0)) )
  this.isCbSizeMeaningfulInst = isCbSizeMeaningfulInstExpr
  this.isCbSizeMeaningfulInstFlag = true
  return this.isCbSizeMeaningfulInst

proc fromFile*(_: typedesc[Wav_FormatChunkType], filename: string): Wav_FormatChunkType =
  Wav_FormatChunkType.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Wav_PmxChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_PmxChunkType =
  template this: untyped = result
  this = new(Wav_PmxChunkType)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent


  ##[
  XMP data
  @see <a href="https://github.com/adobe/XMP-Toolkit-SDK/blob/v2022.06/docs/XMPSpecificationPart3.pdf">Source</a>
  ]##
  let dataExpr = encode(this.io.readBytesFull(), "UTF-8")
  this.data = dataExpr

proc fromFile*(_: typedesc[Wav_PmxChunkType], filename: string): Wav_PmxChunkType =
  Wav_PmxChunkType.read(newKaitaiFileStream(filename), nil, nil)


##[
required for all non-PCM formats
(`w_format_tag != w_format_tag_type::pcm` or `not is_basic_pcm` in
`format_chunk_type` context)

]##
proc read*(_: typedesc[Wav_FactChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_FactChunkType =
  template this: untyped = result
  this = new(Wav_FactChunkType)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent

  let numSamplesPerChannelExpr = this.io.readU4le()
  this.numSamplesPerChannel = numSamplesPerChannelExpr

proc fromFile*(_: typedesc[Wav_FactChunkType], filename: string): Wav_FactChunkType =
  Wav_FactChunkType.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Wav_GuidType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChannelMaskAndSubformatType): Wav_GuidType =
  template this: untyped = result
  this = new(Wav_GuidType)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent

  let data1Expr = this.io.readU4le()
  this.data1 = data1Expr
  let data2Expr = this.io.readU2le()
  this.data2 = data2Expr
  let data3Expr = this.io.readU2le()
  this.data3 = data3Expr
  let data4Expr = this.io.readU4be()
  this.data4 = data4Expr
  let data4aExpr = this.io.readU4be()
  this.data4a = data4aExpr

proc fromFile*(_: typedesc[Wav_GuidType], filename: string): Wav_GuidType =
  Wav_GuidType.read(newKaitaiFileStream(filename), nil, nil)


##[
@see <a href="https://en.wikipedia.org/wiki/IXML">Source</a>
]##
proc read*(_: typedesc[Wav_IxmlChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_IxmlChunkType =
  template this: untyped = result
  this = new(Wav_IxmlChunkType)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent

  let dataExpr = encode(this.io.readBytesFull(), "UTF-8")
  this.data = dataExpr

proc fromFile*(_: typedesc[Wav_IxmlChunkType], filename: string): Wav_IxmlChunkType =
  Wav_IxmlChunkType.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Wav_InfoChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ListChunkType): Wav_InfoChunkType =
  template this: untyped = result
  this = new(Wav_InfoChunkType)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent

  let chunkExpr = Riff_Chunk.read(this.io, this.root, this)
  this.chunk = chunkExpr

proc chunkData(this: Wav_InfoChunkType): string = 
  if this.chunkDataInstFlag:
    return this.chunkDataInst
  let io = this.chunk.dataSlot.io
  let pos = io.pos()
  io.seek(int(0))
  let chunkDataInstExpr = encode(io.readBytesTerm(0, false, true, true), "ASCII")
  this.chunkDataInst = chunkDataInstExpr
  io.seek(pos)
  this.chunkDataInstFlag = true
  return this.chunkDataInst

proc fromFile*(_: typedesc[Wav_InfoChunkType], filename: string): Wav_InfoChunkType =
  Wav_InfoChunkType.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Wav_CuePointType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_CueChunkType): Wav_CuePointType =
  template this: untyped = result
  this = new(Wav_CuePointType)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent

  let dwNameExpr = this.io.readU4le()
  this.dwName = dwNameExpr
  let dwPositionExpr = this.io.readU4le()
  this.dwPosition = dwPositionExpr
  let fccChunkExpr = this.io.readU4le()
  this.fccChunk = fccChunkExpr
  let dwChunkStartExpr = this.io.readU4le()
  this.dwChunkStart = dwChunkStartExpr
  let dwBlockStartExpr = this.io.readU4le()
  this.dwBlockStart = dwBlockStartExpr
  let dwSampleOffsetExpr = this.io.readU4le()
  this.dwSampleOffset = dwSampleOffsetExpr

proc fromFile*(_: typedesc[Wav_CuePointType], filename: string): Wav_CuePointType =
  Wav_CuePointType.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Wav_DataChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_DataChunkType =
  template this: untyped = result
  this = new(Wav_DataChunkType)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent

  let dataExpr = this.io.readBytesFull()
  this.data = dataExpr

proc fromFile*(_: typedesc[Wav_DataChunkType], filename: string): Wav_DataChunkType =
  Wav_DataChunkType.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Wav_SamplesType], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct): Wav_SamplesType =
  template this: untyped = result
  this = new(Wav_SamplesType)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent

  let samplesExpr = this.io.readU4le()
  this.samples = samplesExpr

proc fromFile*(_: typedesc[Wav_SamplesType], filename: string): Wav_SamplesType =
  Wav_SamplesType.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Wav_ChannelMaskAndSubformatType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_FormatChunkType): Wav_ChannelMaskAndSubformatType =
  template this: untyped = result
  this = new(Wav_ChannelMaskAndSubformatType)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent

  let dwChannelMaskExpr = Wav_ChannelMaskType.read(this.io, this.root, this)
  this.dwChannelMask = dwChannelMaskExpr
  let subformatExpr = Wav_GuidType.read(this.io, this.root, this)
  this.subformat = subformatExpr

proc fromFile*(_: typedesc[Wav_ChannelMaskAndSubformatType], filename: string): Wav_ChannelMaskAndSubformatType =
  Wav_ChannelMaskAndSubformatType.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Wav_CueChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_CueChunkType =
  template this: untyped = result
  this = new(Wav_CueChunkType)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent

  let dwCuePointsExpr = this.io.readU4le()
  this.dwCuePoints = dwCuePointsExpr
  for i in 0 ..< int(this.dwCuePoints):
    let it = Wav_CuePointType.read(this.io, this.root, this)
    this.cuePoints.add(it)

proc fromFile*(_: typedesc[Wav_CueChunkType], filename: string): Wav_CueChunkType =
  Wav_CueChunkType.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Wav_ListChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_ListChunkType =
  template this: untyped = result
  this = new(Wav_ListChunkType)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent

  let parentChunkDataExpr = Riff_ParentChunkData.read(this.io, this.root, this)
  this.parentChunkData = parentChunkDataExpr

proc formType(this: Wav_ListChunkType): Wav_Fourcc = 
  if this.formTypeInstFlag:
    return this.formTypeInst
  let formTypeInstExpr = Wav_Fourcc(Wav_Fourcc(this.parentChunkData.formType))
  this.formTypeInst = formTypeInstExpr
  this.formTypeInstFlag = true
  return this.formTypeInst

proc subchunks(this: Wav_ListChunkType): seq[Wav_InfoChunkType] = 
  if this.subchunksInstFlag:
    return this.subchunksInst
  let io = this.parentChunkData.subchunksSlot.io
  let pos = io.pos()
  io.seek(int(0))
  block:
    var i: int
    while not io.isEof:
      block:
        let on = this.formType
        if on == wav.info:
          let it = Wav_InfoChunkType.read(io, this.root, this)
          this.subchunksInst.add(it)
      inc i
  io.seek(pos)
  this.subchunksInstFlag = true
  return this.subchunksInst

proc fromFile*(_: typedesc[Wav_ListChunkType], filename: string): Wav_ListChunkType =
  Wav_ListChunkType.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Wav_ChannelMaskType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChannelMaskAndSubformatType): Wav_ChannelMaskType =
  template this: untyped = result
  this = new(Wav_ChannelMaskType)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent

  let frontRightOfCenterExpr = this.io.readBitsIntBe(1) != 0
  this.frontRightOfCenter = frontRightOfCenterExpr
  let frontLeftOfCenterExpr = this.io.readBitsIntBe(1) != 0
  this.frontLeftOfCenter = frontLeftOfCenterExpr
  let backRightExpr = this.io.readBitsIntBe(1) != 0
  this.backRight = backRightExpr
  let backLeftExpr = this.io.readBitsIntBe(1) != 0
  this.backLeft = backLeftExpr
  let lowFrequencyExpr = this.io.readBitsIntBe(1) != 0
  this.lowFrequency = lowFrequencyExpr
  let frontCenterExpr = this.io.readBitsIntBe(1) != 0
  this.frontCenter = frontCenterExpr
  let frontRightExpr = this.io.readBitsIntBe(1) != 0
  this.frontRight = frontRightExpr
  let frontLeftExpr = this.io.readBitsIntBe(1) != 0
  this.frontLeft = frontLeftExpr
  let topCenterExpr = this.io.readBitsIntBe(1) != 0
  this.topCenter = topCenterExpr
  let sideRightExpr = this.io.readBitsIntBe(1) != 0
  this.sideRight = sideRightExpr
  let sideLeftExpr = this.io.readBitsIntBe(1) != 0
  this.sideLeft = sideLeftExpr
  let backCenterExpr = this.io.readBitsIntBe(1) != 0
  this.backCenter = backCenterExpr
  let topBackLeftExpr = this.io.readBitsIntBe(1) != 0
  this.topBackLeft = topBackLeftExpr
  let topFrontRightExpr = this.io.readBitsIntBe(1) != 0
  this.topFrontRight = topFrontRightExpr
  let topFrontCenterExpr = this.io.readBitsIntBe(1) != 0
  this.topFrontCenter = topFrontCenterExpr
  let topFrontLeftExpr = this.io.readBitsIntBe(1) != 0
  this.topFrontLeft = topFrontLeftExpr
  let unused1Expr = this.io.readBitsIntBe(6)
  this.unused1 = unused1Expr
  let topBackRightExpr = this.io.readBitsIntBe(1) != 0
  this.topBackRight = topBackRightExpr
  let topBackCenterExpr = this.io.readBitsIntBe(1) != 0
  this.topBackCenter = topBackCenterExpr
  let unused2Expr = this.io.readBitsIntBe(8)
  this.unused2 = unused2Expr

proc fromFile*(_: typedesc[Wav_ChannelMaskType], filename: string): Wav_ChannelMaskType =
  Wav_ChannelMaskType.read(newKaitaiFileStream(filename), nil, nil)


##[
@see <a href="https://www.mmsp.ece.mcgill.ca/Documents/Downloads/AFsp/">Source</a>
]##
proc read*(_: typedesc[Wav_AfspChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_AfspChunkType =
  template this: untyped = result
  this = new(Wav_AfspChunkType)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent

  let magicExpr = this.io.readBytes(int(4))
  this.magic = magicExpr

  ##[
  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 <a href="https://www.mmsp.ece.mcgill.ca/Documents/Software/Packages/AFsp/libtsp/AF/AFsetInfo.html">Source</a>
  @see <a href="https://www.mmsp.ece.mcgill.ca/Documents/Software/Packages/AFsp/libtsp/AF/AFprintInfoRecs.html">Source</a>
  ]##
  block:
    var i: int
    while not this.io.isEof:
      let it = encode(this.io.readBytesTerm(0, false, true, true), "ASCII")
      this.infoRecords.add(it)
      inc i

proc fromFile*(_: typedesc[Wav_AfspChunkType], filename: string): Wav_AfspChunkType =
  Wav_AfspChunkType.read(newKaitaiFileStream(filename), nil, nil)


##[
@see <a href="https://tech.ebu.ch/docs/tech/tech3285s5.pdf">Source</a>
]##
proc read*(_: typedesc[Wav_AxmlChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_AxmlChunkType =
  template this: untyped = result
  this = new(Wav_AxmlChunkType)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent

  let dataExpr = encode(this.io.readBytesFull(), "UTF-8")
  this.data = dataExpr

proc fromFile*(_: typedesc[Wav_AxmlChunkType], filename: string): Wav_AxmlChunkType =
  Wav_AxmlChunkType.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Wav_ChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav): Wav_ChunkType =
  template this: untyped = result
  this = new(Wav_ChunkType)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent

  let chunkExpr = Riff_Chunk.read(this.io, this.root, this)
  this.chunk = chunkExpr

proc chunkId(this: Wav_ChunkType): Wav_Fourcc = 
  if this.chunkIdInstFlag:
    return this.chunkIdInst
  let chunkIdInstExpr = Wav_Fourcc(Wav_Fourcc(this.chunk.id))
  this.chunkIdInst = chunkIdInstExpr
  this.chunkIdInstFlag = true
  return this.chunkIdInst

proc chunkData(this: Wav_ChunkType): KaitaiStruct = 
  if this.chunkDataInstFlag:
    return this.chunkDataInst
  let io = this.chunk.dataSlot.io
  let pos = io.pos()
  io.seek(int(0))
  block:
    let on = this.chunkId
    if on == wav.fact:
      let chunkDataInstExpr = Wav_FactChunkType.read(io, this.root, this)
      this.chunkDataInst = chunkDataInstExpr
    elif on == wav.list:
      let chunkDataInstExpr = Wav_ListChunkType.read(io, this.root, this)
      this.chunkDataInst = chunkDataInstExpr
    elif on == wav.fmt:
      let chunkDataInstExpr = Wav_FormatChunkType.read(io, this.root, this)
      this.chunkDataInst = chunkDataInstExpr
    elif on == wav.afsp:
      let chunkDataInstExpr = Wav_AfspChunkType.read(io, this.root, this)
      this.chunkDataInst = chunkDataInstExpr
    elif on == wav.bext:
      let chunkDataInstExpr = Wav_BextChunkType.read(io, this.root, this)
      this.chunkDataInst = chunkDataInstExpr
    elif on == wav.cue:
      let chunkDataInstExpr = Wav_CueChunkType.read(io, this.root, this)
      this.chunkDataInst = chunkDataInstExpr
    elif on == wav.ixml:
      let chunkDataInstExpr = Wav_IxmlChunkType.read(io, this.root, this)
      this.chunkDataInst = chunkDataInstExpr
    elif on == wav.pmx:
      let chunkDataInstExpr = Wav_PmxChunkType.read(io, this.root, this)
      this.chunkDataInst = chunkDataInstExpr
    elif on == wav.axml:
      let chunkDataInstExpr = Wav_AxmlChunkType.read(io, this.root, this)
      this.chunkDataInst = chunkDataInstExpr
    elif on == wav.data:
      let chunkDataInstExpr = Wav_DataChunkType.read(io, this.root, this)
      this.chunkDataInst = chunkDataInstExpr
  io.seek(pos)
  this.chunkDataInstFlag = true
  return this.chunkDataInst

proc fromFile*(_: typedesc[Wav_ChunkType], filename: string): Wav_ChunkType =
  Wav_ChunkType.read(newKaitaiFileStream(filename), nil, nil)


##[
@see <a href="https://en.wikipedia.org/wiki/Broadcast_Wave_Format">Source</a>
]##
proc read*(_: typedesc[Wav_BextChunkType], io: KaitaiStream, root: KaitaiStruct, parent: Wav_ChunkType): Wav_BextChunkType =
  template this: untyped = result
  this = new(Wav_BextChunkType)
  let root = if root == nil: cast[Wav](this) else: cast[Wav](root)
  this.io = io
  this.root = root
  this.parent = parent

  let descriptionExpr = encode(this.io.readBytes(int(256)).bytesTerminate(0, false), "ASCII")
  this.description = descriptionExpr
  let originatorExpr = encode(this.io.readBytes(int(32)).bytesTerminate(0, false), "ASCII")
  this.originator = originatorExpr
  let originatorReferenceExpr = encode(this.io.readBytes(int(32)).bytesTerminate(0, false), "ASCII")
  this.originatorReference = originatorReferenceExpr
  let originationDateExpr = encode(this.io.readBytes(int(10)), "ASCII")
  this.originationDate = originationDateExpr
  let originationTimeExpr = encode(this.io.readBytes(int(8)), "ASCII")
  this.originationTime = originationTimeExpr
  let timeReferenceLowExpr = this.io.readU4le()
  this.timeReferenceLow = timeReferenceLowExpr
  let timeReferenceHighExpr = this.io.readU4le()
  this.timeReferenceHigh = timeReferenceHighExpr
  let versionExpr = this.io.readU2le()
  this.version = versionExpr
  let umidExpr = this.io.readBytes(int(64))
  this.umid = umidExpr
  let loudnessValueExpr = this.io.readU2le()
  this.loudnessValue = loudnessValueExpr
  let loudnessRangeExpr = this.io.readU2le()
  this.loudnessRange = loudnessRangeExpr
  let maxTruePeakLevelExpr = this.io.readU2le()
  this.maxTruePeakLevel = maxTruePeakLevelExpr
  let maxMomentaryLoudnessExpr = this.io.readU2le()
  this.maxMomentaryLoudness = maxMomentaryLoudnessExpr
  let maxShortTermLoudnessExpr = this.io.readU2le()
  this.maxShortTermLoudness = maxShortTermLoudnessExpr

proc fromFile*(_: typedesc[Wav_BextChunkType], filename: string): Wav_BextChunkType =
  Wav_BextChunkType.read(newKaitaiFileStream(filename), nil, nil)