1package flac 2 3// https://xiph.org/flac/format.html 4// https://wiki.hydrogenaud.io/index.php?title=FLAC_decoder_testbench 5// TODO: 6// 16 - Part 6 of Ladybug Castle (partition order 8 with escape codes) 7// 32 - Part 5 of The Four of Us Are Dying (partition order 8 with escape codes) 8 9import ( 10 "bytes" 11 "crypto/md5" 12 "fmt" 13 14 "github.com/wader/fq/format" 15 "github.com/wader/fq/format/registry" 16 "github.com/wader/fq/internal/num" 17 "github.com/wader/fq/pkg/bitio" 18 "github.com/wader/fq/pkg/decode" 19 "github.com/wader/fq/pkg/scalar" 20) 21 22var flacMetadatablocksFormat decode.Group 23var flacFrameFormat decode.Group 24 25func init() { 26 registry.MustRegister(decode.Format{ 27 Name: format.FLAC, 28 Description: "Free Lossless Audio Codec file", 29 Groups: []string{format.PROBE}, 30 DecodeFn: flacDecode, 31 Dependencies: []decode.Dependency{ 32 {Names: []string{format.FLAC_METADATABLOCKS}, Group: &flacMetadatablocksFormat}, 33 {Names: []string{format.FLAC_FRAME}, Group: &flacFrameFormat}, 34 }, 35 }) 36} 37 38func flacDecode(d *decode.D, in interface{}) interface{} { 39 d.FieldUTF8("magic", 4, d.AssertStr("fLaC")) 40 41 var streamInfo format.FlacStreamInfo 42 var flacFrameIn format.FlacFrameIn 43 var framesNDecodedSamples uint64 44 var streamTotalSamples uint64 45 var streamDecodedSamples uint64 46 47 _, v := d.FieldFormat("metadatablocks", flacMetadatablocksFormat, nil) 48 flacMetadatablockOut, ok := v.(format.FlacMetadatablocksOut) 49 if !ok { 50 panic(fmt.Sprintf("expected FlacMetadatablockOut got %#+v", v)) 51 } 52 if flacMetadatablockOut.HasStreamInfo { 53 streamInfo = flacMetadatablockOut.StreamInfo 54 streamTotalSamples = streamInfo.TotalSamplesInStream 55 flacFrameIn = format.FlacFrameIn{StreamInfo: streamInfo} 56 } 57 58 md5Samples := md5.New() 59 d.FieldArray("frames", func(d *decode.D) { 60 for d.NotEnd() { 61 // flac frame might need some fields from stream info to decode 62 _, v := d.FieldFormat("frame", flacFrameFormat, flacFrameIn) 63 ffo, ok := v.(format.FlacFrameOut) 64 if !ok { 65 panic(fmt.Sprintf("expected FlacFrameOut got %#+v", v)) 66 } 67 68 samplesInFrame := ffo.Samples 69 if streamTotalSamples > 0 { 70 samplesInFrame = num.MinUInt64(streamTotalSamples-streamDecodedSamples, ffo.Samples) 71 } 72 frameStreamSamplesBuf := ffo.SamplesBuf[0 : samplesInFrame*uint64(ffo.Channels*ffo.BitsPerSample/8)] 73 framesNDecodedSamples += ffo.Samples 74 75 d.MustCopy(md5Samples, bytes.NewReader(frameStreamSamplesBuf)) 76 streamDecodedSamples += ffo.Samples 77 78 // reuse buffer if possible 79 flacFrameIn.SamplesBuf = ffo.SamplesBuf 80 } 81 }) 82 83 md5CalcValue := d.FieldRootBitBuf("md5_calculated", bitio.NewBufferFromBytes(md5Samples.Sum(nil), -1)) 84 _ = md5CalcValue.TryScalarFn(d.ValidateBitBuf(streamInfo.MD5), scalar.RawHex) 85 d.FieldValueU("decoded_samples", framesNDecodedSamples) 86 87 return nil 88} 89