1
1
mirror of https://github.com/wader/fq.git synced 2024-12-01 02:30:32 +03:00
fq/format/riff/aiff.go
2023-11-25 15:02:09 +01:00

116 lines
3.0 KiB
Go

package riff
// http://midi.teragonaudio.com/tech/aiff.htm
import (
"github.com/wader/fq/format"
"github.com/wader/fq/pkg/decode"
"github.com/wader/fq/pkg/interp"
"github.com/wader/fq/pkg/scalar"
)
func init() {
interp.RegisterFormat(
format.AIFF,
&decode.Format{
ProbeOrder: format.ProbeOrderBinFuzzy,
Description: "Audio Interchange File Format",
Groups: []*decode.Group{format.Probe},
DecodeFn: aiffDecode,
})
}
const aiffRiffType = "AIFF"
// pstring:
// > Pascal-style string, a one-byte count followed by that many text bytes. The total number of bytes in this data type should be even.
// > A pad byte can be added to the end of the text to accomplish this. This pad byte is not reflected in the count.
func aiffPString(d *decode.D) string {
l := d.U8()
pad := (l + 1) % 2
s := d.UTF8(int(l + pad))
return s[0 : l+1-pad]
}
func aiffDecode(d *decode.D) any {
var riffType string
riffDecode(
d,
nil,
func(d *decode.D, path path) (string, int64) {
id := d.FieldUTF8("id", 4, scalar.ActualTrimSpace, 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 "FORM":
riffType = d.FieldUTF8("format", 4, d.StrAssert(aiffRiffType))
return true, nil
case "COMT":
numComments := d.FieldU16("num_comments")
d.FieldArray("comments", func(d *decode.D) {
for i := 0; i < int(numComments); i++ {
d.FieldStruct("comment", func(d *decode.D) {
d.FieldU32("timestamp")
d.FieldU16("marker_id")
count := d.FieldU16("count")
pad := count % 2
d.FieldUTF8("text", int(count))
if pad != 0 {
d.FieldRawLen("pad", int64(pad)*8)
}
})
}
})
return false, nil
case "COMM":
d.FieldU16("num_channels")
d.FieldU32("num_sample_frames")
d.FieldU16("sample_size")
// TODO: support big float?
d.FieldF80("sample_rate")
return false, nil
case "SSND":
d.FieldU32("offset")
d.FieldU32("block_size")
d.FieldRawLen("data", d.BitsLeft())
return false, nil
case "MARK":
numMarkers := d.FieldU16("num_markers")
d.FieldArray("markers", func(d *decode.D) {
for i := 0; i < int(numMarkers); i++ {
d.FieldStruct("marker", func(d *decode.D) {
d.FieldU16("id")
d.FieldU32("position")
d.FieldStrFn("name", aiffPString)
})
}
})
return false, nil
default:
d.FieldRawLen("data", d.BitsLeft())
return false, nil
}
},
)
if riffType != aiffRiffType {
d.Errorf("wrong or no AIFF riff type found (%s)", riffType)
}
return nil
}