Added decode tool to show audio information
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
DataHoarder 2023-01-28 15:18:16 +01:00
parent a7ca061d6c
commit 2f8ffb318f
Signed by: DataHoarder
SSH key fingerprint: SHA256:OLTRf6Fl87G52SiR7sWLGNzlJt4WOX+tfI2yxo0z7xk

141
cmd/decode/decode.go Normal file
View file

@ -0,0 +1,141 @@
package main
import (
"crypto/md5"
"encoding/hex"
"flag"
"git.gammaspectra.live/S.O.N.G/Kirika/audio"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format/guess"
"git.gammaspectra.live/S.O.N.G/Kirika/hasher"
"hash"
"log"
"os"
"runtime"
"strconv"
"time"
"unsafe"
)
func main() {
inputFile := flag.String("input", "", "Input file to decode")
flag.Parse()
fp, err := os.Open(*inputFile)
if err != nil {
log.Panic(err)
return
}
defer fp.Close()
log.Printf("File: %s\n", *inputFile)
decoders, err := guess.GetDecoders(fp, *inputFile)
if err != nil {
log.Panic(err)
return
}
log.Print("Available decoders:\n")
for _, d := range decoders {
if ad, ok := d.(format.AnalyzerDecoder); ok {
log.Printf("\t%s: %s (analyzer)", ad.Name(), ad.DecoderDescription())
} else {
log.Printf("\t%s: %s", d.Name(), d.DecoderDescription())
}
}
startTime := time.Now()
source, analyzer, err := guess.OpenAnalyzer(fp, decoders)
if err != nil {
source, err = guess.Open(fp, decoders)
}
if err != nil {
log.Panic(err)
return
}
var h *hasher.Hasher
if analyzer != nil {
h = hasher.NewHasher(analyzer, hasher.HashtypeMd5)
}
var rawFloat32Hasher hash.Hash
var sampleCount, blockCount uint64
if f32Source, ok := source.(audio.TypedSource[float32]); ok {
rawFloat32Hasher = md5.New()
log.Printf("Decoding as float32 @ %dHz %d channel(s)", f32Source.GetSampleRate(), f32Source.GetChannels())
for b := range f32Source.GetBlocks() {
blockCount++
sampleCount += uint64(len(b))
func() {
defer runtime.KeepAlive(b)
rawFloat32Hasher.Write(unsafe.Slice((*byte)(unsafe.Pointer(&b[0])), len(b)*int(unsafe.Sizeof(float32(0)))))
}()
}
sampleCount /= uint64(f32Source.GetChannels())
} else if i16Source, ok := source.(audio.TypedSource[int16]); ok {
log.Printf("Decoding as int16 %d-bit @ %dHz %d channel(s)", i16Source.GetBitDepth(), i16Source.GetSampleRate(), i16Source.GetChannels())
for b := range i16Source.GetBlocks() {
blockCount++
sampleCount += uint64(len(b))
}
sampleCount /= uint64(i16Source.GetChannels())
} else if i32Source, ok := source.(audio.TypedSource[int32]); ok {
log.Printf("Decoding as int32 %d-bit @ %dHz %d channel(s)", i32Source.GetBitDepth(), i32Source.GetSampleRate(), i32Source.GetChannels())
for b := range i32Source.GetBlocks() {
blockCount++
sampleCount += uint64(len(b))
}
sampleCount /= uint64(i32Source.GetChannels())
} else {
rawFloat32Hasher = md5.New()
f32Source = source.ToFloat32()
log.Printf("Decoding as float32 (generic) %d-bit @ %dHz %d channel(s)", f32Source.GetBitDepth(), f32Source.GetSampleRate(), f32Source.GetChannels())
for b := range f32Source.GetBlocks() {
blockCount++
sampleCount += uint64(len(b))
func() {
defer runtime.KeepAlive(b)
rawFloat32Hasher.Write(unsafe.Slice((*byte)(unsafe.Pointer(&b[0])), len(b)*int(unsafe.Sizeof(float32(0)))))
}()
}
sampleCount /= uint64(f32Source.GetChannels())
}
log.Printf("Decoded %d sample(s)", sampleCount)
if blockCount > 0 {
log.Printf("Processed %d blocks(s) (avg %0.2f sample(s) per block)", blockCount, float64(sampleCount)/float64(blockCount))
}
log.Printf("Duration %s", ((time.Duration(sampleCount) * time.Second) / time.Duration(source.GetSampleRate())).String())
if h != nil {
h.Wait()
log.Printf("MD5 hash: %s", hex.EncodeToString(h.GetResult()))
if _, ok := source.(audio.TypedSource[float32]); ok {
log.Printf("\tCheck via $ ffmpeg -hide_banner -loglevel error -i %s -vn -c:a pcm_f32le -f md5 -", strconv.Quote(*inputFile))
} else {
switch source.GetBitDepth() {
case 8, 16, 24, 32:
log.Printf("\tCheck via $ ffmpeg -hide_banner -loglevel error -i %s -vn -c:a pcm_s%dle -f md5 -", strconv.Quote(*inputFile), source.GetBitDepth())
default:
log.Printf("\tCheck via $ ffmpeg -hide_banner -loglevel error -i %s -vn -c:a pcm_s32le -f md5 -", strconv.Quote(*inputFile))
}
}
} else if rawFloat32Hasher != nil {
log.Printf("MD5 hash: %s", hex.EncodeToString(rawFloat32Hasher.Sum(nil)))
if _, ok := source.(audio.TypedSource[float32]); ok {
log.Printf("\tCheck via $ ffmpeg -hide_banner -loglevel error -i %s -vn -c:a pcm_f32le -f md5 -", strconv.Quote(*inputFile))
log.Printf("\tNote that you might need to specify a decoder with -c:a before -i to perfectly match the hash on lossy decoders, for example, libopus instead of opus")
}
}
log.Printf("Took %s", time.Now().Sub(startTime).String())
}