package flac // TODO: 24 bit picture length truncate warning // TODO: Cuesheet import ( "github.com/wader/fq/format" "github.com/wader/fq/format/registry" "github.com/wader/fq/pkg/decode" "github.com/wader/fq/pkg/ranges" ) var flacPicture []*decode.Format var vorbisCommentFormat []*decode.Format func init() { registry.MustRegister(&decode.Format{ Name: format.FLAC_METADATABLOCKS, Description: "FLAC metadatablocks", DecodeFn: metadatablocskDecode, RootV: decode.Array{}, RootName: "metadatablocks", Dependencies: []decode.Dependency{ {Names: []string{format.FLAC_PICTURE}, Formats: &flacPicture}, {Names: []string{format.VORBIS_COMMENT}, Formats: &vorbisCommentFormat}, }, }) } const ( MetadataBlockStreaminfo = 0 MetadataBlockPadding = 1 MetadataBlockApplication = 2 MetadataBlockSeektable = 3 MetadataBlockVorbisComment = 4 MetadataBlockCuesheet = 5 MetadataBlockPicture = 6 ) var metadataBlockNames = map[uint]string{ MetadataBlockStreaminfo: "Streaminfo", MetadataBlockPadding: "Padding", MetadataBlockApplication: "Application", MetadataBlockSeektable: "Seektable", MetadataBlockVorbisComment: "Vorbis comment", MetadataBlockCuesheet: "Cuesheet", MetadataBlockPicture: "Picture", } func metadatablocskDecode(d *decode.D, in interface{}) interface{} { mb := format.FlacMetadatablocksOut{} isLastBlock := false for !isLastBlock { d.FieldStructFn("metadatablock", func(d *decode.D) { isLastBlock = d.FieldBool("last_block") typ := d.FieldUFn("type", func() (uint64, decode.DisplayFormat, string) { t := d.U7() name := "Unknown" if s, ok := metadataBlockNames[uint(t)]; ok { name = s } return t, decode.NumberDecimal, name }) length := d.FieldU24("length") switch typ { case MetadataBlockStreaminfo: d.FieldU16("minimum_block_size") d.FieldU16("maximum_block_size") d.FieldU24("minimum_frame_size") d.FieldU24("maximum_frame_size") sampleRate := d.FieldU("sample_rate", 20) // <3> (number of channels)-1. FLAC supports from 1 to 8 channels d.FieldUFn("channels", func() (uint64, decode.DisplayFormat, string) { return d.U3() + 1, decode.NumberDecimal, "" }) // <5> (bits per sample)-1. FLAC supports from 4 to 32 bits per sample. Currently the reference encoder and decoders only support up to 24 bits per sample. bitPerSample := d.FieldUFn("bits_per_sample", func() (uint64, decode.DisplayFormat, string) { return d.U5() + 1, decode.NumberDecimal, "" }) totalSamplesInStream := d.FieldU("total_samples_in_stream", 36) md5Range := ranges.Range{Start: d.Pos(), Len: 16 * 8} d.FieldBitBufLen("md5", 16*8) mb.StreamInfo = format.FlacMetadatablockStreamInfo{ SampleRate: sampleRate, BitPerSample: bitPerSample, TotalSamplesInStream: totalSamplesInStream, MD5Range: md5Range, } mb.HasStreamInfo = true case MetadataBlockVorbisComment: d.FieldFormatLen("comment", int64(length*8), vorbisCommentFormat, nil) case MetadataBlockPicture: d.FieldFormatLen("picture", int64(length*8), flacPicture, nil) case MetadataBlockSeektable: seektableCount := length / 18 d.FieldArrayFn("seekpoints", func(d *decode.D) { for i := uint64(0); i < seektableCount; i++ { d.FieldStructFn("seekpoint", func(d *decode.D) { d.FieldUFn("sample_number", func() (uint64, decode.DisplayFormat, string) { n := d.U64() d := "" if n == 0xffffffffffffffff { d = "Placeholder" } return n, decode.NumberDecimal, d }) d.FieldU64("offset") d.FieldU16("number_of_samples") }) } }) default: if typ < 127 { d.FieldBitBufLen("data", int64(length*8)) } else { d.Invalid("invalid block type") } } }) } return mb }