Skip to content

Instantly share code, notes, and snippets.

@mateon1
Last active July 20, 2021 16:56
Show Gist options
  • Save mateon1/dd2ba3c8e3e5476fdfabe505b7fcf1bc to your computer and use it in GitHub Desktop.
Save mateon1/dd2ba3c8e3e5476fdfabe505b7fcf1bc to your computer and use it in GitHub Desktop.
Kaitai MP3 parser
meta:
id: mp3
file-extension: mp3
seq:
- id: frame0
type: frame
- id: frames
type: frame
repeat: expr
repeat-expr: 50
# TODO: just testing - can't EOS because the file is truncated and the last frame errors
types:
frame:
instances:
channels:
value: "header.is_mono ? 1 : 2"
hz:
value: header.sample_rate_hz
layer:
value: 4 - header.layer
bitrate_kbps:
value: header.bitrate_kbps
seq:
- id: header
type: header
valid:
expr: "
_.mpeg1 == _parent.frame0.header.mpeg1 and
_.layer == _parent.frame0.header.layer and
_.sample_rate == _parent.frame0.header.sample_rate and
_.free_format == _parent.frame0.header.free_format
"
- id: data
type: mp3_data(header)
size: header.frame_bytes+header.padding_bytes-4
mp3_data:
params:
- id: header
type: header
seq:
- id: crc
type: b16
if: not header.crc_absent
- id: side_info
type: mp3_side_info(header)
- type: b0 # force alignment
- id: main_data
size-eos: true
mp3_side_info:
params:
- id: header
type: header
instances:
sr_idx:
value: "header.my_sample_rate == 0 ? 0 : header.my_sample_rate - 1"
gr_cnt:
value: "(header.is_mono ? 1 : 2) * (header.mpeg1 ? 2 : 1)"
main_data_begin:
value: "header.mpeg1 ? hack.main_data_begin_raw9 : (gr_cnt == 1 ? hack.main_data_begin_raw9 >> 1 : hack.main_data_begin_raw10 >> 2)"
part_23_sum:
value: "
gr[0].part_23_len +
(gr.size > 1 ? gr[1].part_23_len : 0) +
(gr.size > 2 ? gr[2].part_23_len : 0) +
(gr.size > 3 ? gr[3].part_23_len : 0)
"
seq:
- id: hack
type: side_info_bitalign_hack(header.mpeg1, gr_cnt)
- id: gr
type: gr_info(header)
repeat: expr
repeat-expr: gr_cnt
side_info_bitalign_hack:
params:
- id: mpeg1
type: b1
- id: gr_cnt
type: u4
seq:
- id: main_data_begin_raw9
type: b9
if: mpeg1 or gr_cnt == 1
- id: main_data_begin_raw10
type: b10
if: not mpeg1 and gr_cnt == 2
- id: scfsi_raw9
type: b9
if: mpeg1 and gr_cnt == 2
- id: scfsi_raw11
type: b11
if: mpeg1 and gr_cnt == 4
gr_info:
params:
- id: header
type: header
seq:
- id: part_23_len
type: b12
- id: big_values
type: b9
valid:
max: 288
- id: global_gain
type: b8
- id: scalefac_compress_raw4
type: b4
if: header.mpeg1
- id: scalefac_compress_raw9
type: b9
if: not header.mpeg1
- id: block_bit
type: b1
- id: tables0
type: b15
if: not block_bit
- id: region_cnt0_raw0
type: b4
if: not block_bit
- id: region_cnt1_raw0
type: b3
if: not block_bit
- id: block_type_raw1
type: b2
valid:
expr: _ != 0
if: block_bit
- id: mixed_block_flag_raw1
type: b1
if: block_bit
- id: tables1
type: b10
if: block_bit
- id: subblock_gain_raw1
type: b3
repeat: expr
repeat-expr: 3
if: block_bit
- id: preflag_raw
type: b1
if: header.mpeg1
- id: scalefac_scale
type: b1
- id: count1_table
type: b1
header:
instances:
is_layer_1:
value: layer == 3
is_frame_576:
value: not mpeg1 and layer == 0b01
my_sample_rate:
value: "sample_rate + ((mpeg1 ? 1 : 0) + (not_mpeg25 ? 1 : 0)) * 3"
sample_rate_hz:
value: "(sample_rate == 0 ? 44100 : sample_rate == 1 ? 48000 : 32000) >> (mpeg1 ? 0 : 1) >> (not_mpeg25 ? 0 : 1)"
frame_samples:
value: "is_layer_1 ? 384 : (is_frame_576 ? 576 : 1152)"
bitrate_helper:
value: "layer == 2 ? bitrate + 1 : bitrate"
bitrate_kbps:
doc: |
Lookup table: halfrate[mpeg1][layer-1][bitrate]
{ { 0, 4, 8,12,16,20,24, 28, 32, 40, 48, 56, 64, 72, 80 },
{ 0, 4, 8,12,16,20,24, 28, 32, 40, 48, 56, 64, 72, 80 },
{ 0,16,24,28,32,40,48, 56, 64, 72, 80, 88, 96,112,128 } },
{ { 0,16,20,24,28,32,40, 48, 56, 64, 80, 96,112,128,160 },
{ 0,16,24,28,32,40,48, 56, 64, 80, 96,112,128,160,192 },
{ 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } },
Deltas:
{ { -, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8 },
{ -, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8 },
{ -,16, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 16, 16 } },
{ { -,16, 4, 4, 4, 4, 8, 8, 8, 8, 16, 16, 16, 16, 32 },
{ -,16, 8, 4, 4, 8, 8, 8, 8, 16, 16, 16, 16, 32, 32 },
{ -,16,16,16,16,16,16, 16, 16, 16, 16, 16, 16, 16, 16 } },
value: "2 *
(mpeg1 ?
(layer == 3 or bitrate <= 1 ?
16 * bitrate :
(bitrate_helper <= 5 ?
12 + bitrate_helper * 4:
(bitrate_helper <= 9 ?
-8 + bitrate_helper * 8 :
(bitrate_helper <= 13 ?
-80 + bitrate_helper * 16 :
-288 + bitrate_helper * 32))) ) :
(layer == 3 ?
(bitrate <= 1 ?
16 * bitrate :
(bitrate <= 4 ?
24 + 4 * (bitrate - 2) :
(bitrate <= 12 ?
32 + 8 * (bitrate - 4) :
(bitrate == 13 ? 160 : 192)))) :
bitrate <= 8 ? 4 * bitrate : 32 + 8 * (bitrate - 8)))
"
frame_bytes:
value: "(frame_samples * bitrate_kbps*125/sample_rate_hz) & (is_layer_1 ? ~3 : ~0)"
free_format:
value: bitrate == 0
padding_bytes:
value: "has_padding ? (is_layer_1 ? 4 : 1) : 0"
is_mono:
value: "stereo_mode == 3"
is_ms_stereo:
value: "stereo_mode == 1 and stereo_mode_ext & 2 == 2"
test_ms_stereo:
value: "stereo_mode_ext & 2 == 2"
test_i_stereo:
value: "stereo_mode_ext & 1 == 1"
seq:
- id: sync
type: b11
valid: 0x7ff
- id: not_mpeg25
type: b1
- id: mpeg1
type: b1
- id: layer
type: b2
valid:
expr: _ != 0
- id: crc_absent
type: b1
# check 0xFFE2/0xFFE3 header validity here
valid: "not_mpeg25 or is_frame_576"
# byte 2
- id: bitrate
type: b4
valid:
expr: _ != 15
- id: sample_rate
type: b2
valid:
expr: _ != 3
- id: has_padding
type: b1
- id: unk1
type: b1
# byte 3
- id: stereo_mode
type: b2
- id: stereo_mode_ext
type: b2
- id: unk2
type: b4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment