Skip to content

Commit dc4a82e

Browse files
committed
aiff: Add basic decoder
1 parent 73ab188 commit dc4a82e

File tree

9 files changed

+819
-435
lines changed

9 files changed

+819
-435
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ In summary it aims to be jq, hexdump, dd and gdb for files combined into one.
3737
[aac_frame](doc/formats.md#aac_frame),
3838
adts,
3939
adts_frame,
40+
aiff,
4041
amf0,
4142
apev2,
4243
[apple_bookmark](doc/formats.md#apple_bookmark),

doc/formats.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
|[`aac_frame`](#aac_frame) |Advanced&nbsp;Audio&nbsp;Coding&nbsp;frame |<sub></sub>|
88
|`adts` |Audio&nbsp;Data&nbsp;Transport&nbsp;Stream |<sub>`adts_frame`</sub>|
99
|`adts_frame` |Audio&nbsp;Data&nbsp;Transport&nbsp;Stream&nbsp;frame |<sub>`aac_frame`</sub>|
10+
|`aiff` |Audio&nbsp;Interchange&nbsp;File&nbsp;Format |<sub></sub>|
1011
|`amf0` |Action&nbsp;Message&nbsp;Format&nbsp;0 |<sub></sub>|
1112
|`apev2` |APEv2&nbsp;metadata&nbsp;tag |<sub>`image`</sub>|
1213
|[`apple_bookmark`](#apple_bookmark) |Apple&nbsp;BookmarkData |<sub></sub>|
@@ -123,7 +124,7 @@
123124
|`ip_packet` |Group |<sub>`icmp` `icmpv6` `tcp_segment` `udp_datagram`</sub>|
124125
|`link_frame` |Group |<sub>`bsd_loopback_frame` `ether8023_frame` `ipv4_packet` `ipv6_packet` `sll2_packet` `sll_packet`</sub>|
125126
|`mp3_frame_tags` |Group |<sub>`mp3_frame_vbri` `mp3_frame_xing`</sub>|
126-
|`probe` |Group |<sub>`adts` `apple_bookmark` `ar` `avi` `avro_ocf` `bitcoin_blkdat` `bplist` `bzip2` `elf` `flac` `gif` `gzip` `jpeg` `json` `jsonl` `macho` `macho_fat` `matroska` `mp3` `mp4` `mpeg_ts` `ogg` `pcap` `pcapng` `png` `tar` `tiff` `toml` `tzif` `wasm` `wav` `webp` `xml` `yaml` `zip`</sub>|
127+
|`probe` |Group |<sub>`adts` `aiff` `apple_bookmark` `ar` `avi` `avro_ocf` `bitcoin_blkdat` `bplist` `bzip2` `elf` `flac` `gif` `gzip` `jpeg` `json` `jsonl` `macho` `macho_fat` `matroska` `mp3` `mp4` `mpeg_ts` `ogg` `pcap` `pcapng` `png` `tar` `tiff` `toml` `tzif` `wasm` `wav` `webp` `xml` `yaml` `zip`</sub>|
127128
|`tcp_stream` |Group |<sub>`dns_tcp` `rtmp` `tls`</sub>|
128129
|`udp_payload` |Group |<sub>`dns`</sub>|
129130

doc/formats.svg

Lines changed: 446 additions & 434 deletions
Loading

format/all/all.fqtest

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ $ fq -n _registry.groups.probe
2727
"wasm",
2828
"webp",
2929
"zip",
30+
"aiff",
3031
"mp3",
3132
"mpeg_ts",
3233
"wav",
@@ -40,6 +41,7 @@ $ fq --help formats
4041
aac_frame Advanced Audio Coding frame
4142
adts Audio Data Transport Stream
4243
adts_frame Audio Data Transport Stream frame
44+
aiff Audio Interchange File Format
4345
amf0 Action Message Format 0
4446
apev2 APEv2 metadata tag
4547
apple_bookmark Apple BookmarkData

format/format.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const (
2929
AAC_FRAME = "aac_frame"
3030
ADTS = "adts"
3131
ADTS_FRAME = "adts_frame"
32+
AIFF = "aiff"
3233
AMF0 = "amf0"
3334
APEV2 = "apev2"
3435
APPLE_BOOKMARK = "apple_bookmark"

format/riff/aiff.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package riff
2+
3+
// http://midi.teragonaudio.com/tech/aiff.htm
4+
5+
import (
6+
"github.com/wader/fq/format"
7+
"github.com/wader/fq/internal/mathex"
8+
"github.com/wader/fq/pkg/decode"
9+
"github.com/wader/fq/pkg/interp"
10+
"github.com/wader/fq/pkg/scalar"
11+
)
12+
13+
func init() {
14+
interp.RegisterFormat(decode.Format{
15+
Name: format.AIFF,
16+
ProbeOrder: format.ProbeOrderBinFuzzy,
17+
Description: "Audio Interchange File Format",
18+
Groups: []string{format.PROBE},
19+
DecodeFn: aiffDecode,
20+
})
21+
}
22+
23+
const aiffRiffType = "AIFF"
24+
25+
// pstring:
26+
// > 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.
27+
// > A pad byte can be added to the end of the text to accomplish this. This pad byte is not reflected in the count.
28+
func aiffPString(d *decode.D) string {
29+
l := d.U8()
30+
pad := (l + 1) % 2
31+
s := d.UTF8(int(l + pad))
32+
return s[0 : l+1-pad]
33+
}
34+
35+
func aiffDecode(d *decode.D) any {
36+
var riffType string
37+
riffDecode(
38+
d,
39+
nil,
40+
func(d *decode.D, path path) (string, int64) {
41+
id := d.FieldUTF8("id", 4, chunkIDDescriptions)
42+
43+
const restOfFileLen = 0xffffffff
44+
size := int64(d.FieldScalarUintFn("size", func(d *decode.D) scalar.Uint {
45+
l := d.U32()
46+
if l == restOfFileLen {
47+
return scalar.Uint{Actual: l, DisplayFormat: scalar.NumberHex, Description: "Rest of file"}
48+
}
49+
return scalar.Uint{Actual: l, DisplayFormat: scalar.NumberDecimal}
50+
}).Actual)
51+
52+
if size == restOfFileLen {
53+
size = d.BitsLeft() / 8
54+
}
55+
return id, size
56+
},
57+
func(d *decode.D, id string, path path) (bool, any) {
58+
switch id {
59+
case "FORM":
60+
riffType = d.FieldUTF8("format", 4, d.StrAssert(aiffRiffType))
61+
return true, nil
62+
case "COMT":
63+
numComments := d.FieldU16("num_comments")
64+
d.FieldArray("comments", func(d *decode.D) {
65+
for i := 0; i < int(numComments); i++ {
66+
d.FieldStruct("comment", func(d *decode.D) {
67+
d.FieldU32("timestamp")
68+
d.FieldU16("marker_id")
69+
count := d.FieldU16("count")
70+
pad := count % 2
71+
d.FieldUTF8("text", int(count))
72+
if pad != 0 {
73+
d.FieldRawLen("pad", int64(pad)*8)
74+
}
75+
})
76+
}
77+
})
78+
return false, nil
79+
case "COMM":
80+
d.FieldU16("num_channels")
81+
d.FieldU32("num_sample_frames")
82+
d.FieldU16("sample_size")
83+
// TODO: support big float?
84+
d.FieldFltFn("sample_rate", func(d *decode.D) float64 {
85+
return mathex.NewFloat80FromBytes(d.BytesLen(10)).Float64()
86+
})
87+
return false, nil
88+
case "SSND":
89+
d.FieldU32("offset")
90+
d.FieldU32("block_size")
91+
d.FieldRawLen("data", d.BitsLeft())
92+
return false, nil
93+
case "MARK":
94+
numMarkers := d.FieldU16("num_markers")
95+
d.FieldArray("markers", func(d *decode.D) {
96+
for i := 0; i < int(numMarkers); i++ {
97+
d.FieldStruct("marker", func(d *decode.D) {
98+
d.FieldU16("id")
99+
d.FieldU32("position")
100+
d.FieldStrFn("name", aiffPString)
101+
})
102+
}
103+
})
104+
return false, nil
105+
default:
106+
d.FieldRawLen("data", d.BitsLeft())
107+
return false, nil
108+
}
109+
},
110+
)
111+
112+
if riffType != aiffRiffType {
113+
d.Errorf("wrong or no AIFF riff type found (%s)", riffType)
114+
}
115+
116+
return nil
117+
}

format/riff/testdata/sox.aiff

8.7 KB
Binary file not shown.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# ffmpeg -f lavfi -i sine -t 100ms -f wav sox.wav; sox sox.wav six.aiff
2+
$ fq dv sox.aiff
3+
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|.{}: sox.aiff (aiff) 0x0-0x22cb.7 (8908)
4+
0x0000|46 4f 52 4d |FORM | id: "FORM" 0x0-0x3.7 (4)
5+
0x0000| 00 00 22 c4 | ..". | size: 8900 0x4-0x7.7 (4)
6+
0x0000| 41 49 46 46 | AIFF | format: "AIFF" (valid) 0x8-0xb.7 (4)
7+
| | | chunks[0:3]: 0xc-0x22cb.7 (8896)
8+
| | | [0]{}: chunk 0xc-0x2d.7 (34)
9+
0x0000| 43 4f 4d 54| COMT| id: "COMT" 0xc-0xf.7 (4)
10+
0x0010|00 00 00 1a |.... | size: 26 0x10-0x13.7 (4)
11+
0x0010| 00 01 | .. | num_comments: 1 0x14-0x15.7 (2)
12+
| | | comments[0:1]: 0x16-0x2d.7 (24)
13+
| | | [0]{}: comment 0x16-0x2d.7 (24)
14+
0x0010| e0 2f 99 4b | ./.K | timestamp: 3761215819 0x16-0x19.7 (4)
15+
0x0010| 00 00 | .. | marker_id: 0 0x1a-0x1b.7 (2)
16+
0x0010| 00 10 | .. | count: 16 0x1c-0x1d.7 (2)
17+
0x0010| 50 72| Pr| text: "Processed by SoX" 0x1e-0x2d.7 (16)
18+
0x0020|6f 63 65 73 73 65 64 20 62 79 20 53 6f 58 |ocessed by SoX |
19+
| | | [1]{}: chunk 0x2e-0x47.7 (26)
20+
0x0020| 43 4f| CO| id: "COMM" 0x2e-0x31.7 (4)
21+
0x0030|4d 4d |MM |
22+
0x0030| 00 00 00 12 | .... | size: 18 0x32-0x35.7 (4)
23+
0x0030| 00 01 | .. | num_channels: 1 0x36-0x37.7 (2)
24+
0x0030| 00 00 11 3a | ...: | num_sample_frames: 4410 0x38-0x3b.7 (4)
25+
0x0030| 00 10 | .. | sample_size: 16 0x3c-0x3d.7 (2)
26+
0x0030| 40 0e| @.| sample_rate: 44100 0x3e-0x47.7 (10)
27+
0x0040|ac 44 00 00 00 00 00 00 |.D...... |
28+
| | | [2]{}: chunk 0x48-0x22cb.7 (8836)
29+
0x0040| 53 53 4e 44 | SSND | id: "SSND" 0x48-0x4b.7 (4)
30+
0x0040| 00 00 22 7c| .."|| size: 8828 0x4c-0x4f.7 (4)
31+
0x0050|00 00 00 00 |.... | offset: 0 0x50-0x53.7 (4)
32+
0x0050| 00 00 00 00 | .... | block_size: 0 0x54-0x57.7 (4)
33+
0x0050| 00 00 01 00 01 ff 02 fd| ........| data: raw bits 0x58-0x22cb.7 (8820)
34+
0x0060|03 f8 04 ee 05 e0 06 cc 07 b0 08 8d 09 62 0a 2d|.............b.-|
35+
* |until 0x22cb.7 (end) (8820) | |

0 commit comments

Comments
 (0)