mirror of
https://github.com/wader/fq.git
synced 2024-11-26 21:55:57 +03:00
8e0dde03d0
This will allow passing both cli options and format options to sub decoder. Ex: pass keylog option to a tls decoder when decoding a pcap. Ex: pass decode options to a format inside a http body inside a pcap. Add ArgAs method to lookup argument based on type. This also makes the format decode function have same signature as sub decoders in the decode API. This change decode.Format a bit: DecodeFn is now just func(d *D) any DecodeInArg renamed to DefaultInArg
165 lines
4.4 KiB
Go
165 lines
4.4 KiB
Go
package riff
|
|
|
|
// http://soundfile.sapp.org/doc/WaveFormat/
|
|
// https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/wavdec.c
|
|
// https://tech.ebu.ch/docs/tech/tech3285.pdf
|
|
// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
|
|
// TODO: audio/wav
|
|
// TODO: default little endian
|
|
|
|
import (
|
|
"github.com/wader/fq/format"
|
|
"github.com/wader/fq/pkg/decode"
|
|
"github.com/wader/fq/pkg/interp"
|
|
"github.com/wader/fq/pkg/scalar"
|
|
)
|
|
|
|
var wavHeaderFormat decode.Group
|
|
var wavFooterFormat decode.Group
|
|
|
|
func init() {
|
|
interp.RegisterFormat(decode.Format{
|
|
Name: format.WAV,
|
|
ProbeOrder: format.ProbeOrderBinFuzzy, // after most others (overlap some with webp)
|
|
Description: "WAV file",
|
|
Groups: []string{format.PROBE},
|
|
DecodeFn: wavDecode,
|
|
Dependencies: []decode.Dependency{
|
|
{Names: []string{format.ID3V2}, Group: &wavHeaderFormat},
|
|
{Names: []string{format.ID3V1, format.ID3V11}, Group: &wavFooterFormat},
|
|
},
|
|
})
|
|
}
|
|
|
|
const (
|
|
formatExtensible = 0xfffe
|
|
)
|
|
|
|
var (
|
|
subFormatPCMBytes = [16]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
|
subFormatIEEEFloat = [16]byte{0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
|
|
)
|
|
|
|
const wavRiffType = "WAVE"
|
|
|
|
var subFormatNames = scalar.RawBytesMap{
|
|
{Bytes: subFormatPCMBytes[:], Scalar: scalar.BitBuf{Sym: "pcm"}},
|
|
{Bytes: subFormatIEEEFloat[:], Scalar: scalar.BitBuf{Sym: "ieee_float"}},
|
|
}
|
|
|
|
func wavDecode(d *decode.D) any {
|
|
d.Endian = decode.LittleEndian
|
|
|
|
// there are wav files in the wild with id3v2 header id3v1 footer
|
|
_, _, _ = d.TryFieldFormat("header", wavHeaderFormat, nil)
|
|
|
|
var riffType string
|
|
riffDecode(
|
|
d,
|
|
nil,
|
|
func(d *decode.D, path path) (string, int64) {
|
|
id := d.FieldUTF8("id", 4, chunkIDDescriptions)
|
|
|
|
const restOfFileLen = 0xffffffff
|
|
size := int64(d.FieldScalarUintFn("size", func(d *decode.D) scalar.Uint {
|
|
l := d.U32()
|
|
if l == restOfFileLen {
|
|
return scalar.Uint{Actual: l, DisplayFormat: scalar.NumberHex, Description: "Rest of file"}
|
|
}
|
|
return scalar.Uint{Actual: l, DisplayFormat: scalar.NumberDecimal}
|
|
}).Actual)
|
|
|
|
if size == restOfFileLen {
|
|
size = d.BitsLeft() / 8
|
|
}
|
|
|
|
return id, size
|
|
},
|
|
func(d *decode.D, id string, path path) (bool, any) {
|
|
switch id {
|
|
case "RIFF":
|
|
riffType = d.FieldUTF8("format", 4, d.StrAssert(wavRiffType))
|
|
return true, nil
|
|
|
|
case "LIST":
|
|
typ := d.FieldUTF8("type", 4)
|
|
switch typ {
|
|
case "strl":
|
|
return true, &aviStrl{}
|
|
}
|
|
|
|
return true, nil
|
|
|
|
case "fmt ":
|
|
audioFormat := d.FieldU16("audio_format", format.WAVTagNames)
|
|
d.FieldU16("num_channels")
|
|
d.FieldU32("sample_rate")
|
|
d.FieldU32("byte_rate")
|
|
d.FieldU16("block_align")
|
|
d.FieldU16("bits_per_sample")
|
|
|
|
if audioFormat == formatExtensible && d.BitsLeft() > 0 {
|
|
d.FieldU16("extension_size")
|
|
d.FieldU16("valid_bits_per_sample")
|
|
d.FieldU32("channel_mask")
|
|
d.FieldRawLen("sub_format", 16*8, subFormatNames)
|
|
}
|
|
return false, nil
|
|
|
|
case "data":
|
|
d.FieldRawLen("samples", d.BitsLeft())
|
|
return false, nil
|
|
|
|
case "fact":
|
|
d.FieldU32("sample_length")
|
|
return false, nil
|
|
|
|
case "smpl":
|
|
d.FieldU32("manufacturer")
|
|
d.FieldU32("product")
|
|
d.FieldU32("sample_period")
|
|
d.FieldU32("midi_unity_note")
|
|
d.FieldU32("midi_pitch_fraction")
|
|
d.FieldU32("smpte_format")
|
|
d.FieldU32("smpte_offset")
|
|
numSampleLoops := int(d.FieldU32("number_of_sample_loops"))
|
|
samplerDataBytes := int(d.FieldU32("sampler_data_bytes"))
|
|
d.FieldArray("samples_loops", func(d *decode.D) {
|
|
for i := 0; i < numSampleLoops; i++ {
|
|
d.FieldStruct("sample_loop", func(d *decode.D) {
|
|
d.FieldUTF8("id", 4)
|
|
d.FieldU32("type", scalar.UintMapSymStr{
|
|
0: "forward",
|
|
1: "forward_backward",
|
|
2: "backward",
|
|
})
|
|
d.FieldU32("start")
|
|
d.FieldU32("end")
|
|
d.FieldU32("fraction")
|
|
d.FieldU32("number_of_times")
|
|
})
|
|
}
|
|
})
|
|
d.FieldRawLen("sampler_data", int64(samplerDataBytes)*8)
|
|
return false, nil
|
|
|
|
default:
|
|
if riffIsStringChunkID(id) {
|
|
d.FieldUTF8NullFixedLen("value", int(d.BitsLeft())/8)
|
|
return false, nil
|
|
}
|
|
|
|
d.FieldRawLen("data", d.BitsLeft())
|
|
return false, nil
|
|
}
|
|
},
|
|
)
|
|
|
|
if riffType != wavRiffType {
|
|
d.Errorf("wrong or no AVI riff type found (%s)", riffType)
|
|
}
|
|
_, _, _ = d.TryFieldFormat("footer", wavFooterFormat, nil)
|
|
|
|
return nil
|
|
}
|