2023-01-28 14:18:16 +00:00
package main
import (
"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"
"log"
"os"
"strconv"
"time"
)
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 {
2023-01-29 13:34:26 +00:00
source , analyzer , err = format . NewAnalyzerChannel ( guess . Open ( fp , decoders ) )
2023-01-28 14:18:16 +00:00
}
if err != nil {
log . Panic ( err )
return
}
2023-01-29 13:34:26 +00:00
analyzers := analyzer . Split ( 3 )
2023-01-28 14:18:16 +00:00
2023-01-29 13:34:26 +00:00
hCRC32 := hasher . NewHasher ( analyzers [ 0 ] , hasher . HashtypeCrc32 )
hSHA256 := hasher . NewHasher ( analyzers [ 1 ] , hasher . HashtypeSha256 )
hMD5 := hasher . NewHasher ( analyzers [ 2 ] , hasher . HashtypeMd5 )
2023-01-28 14:18:16 +00:00
var sampleCount , blockCount uint64
if f32Source , ok := source . ( audio . TypedSource [ float32 ] ) ; ok {
log . Printf ( "Decoding as float32 @ %dHz %d channel(s)" , f32Source . GetSampleRate ( ) , f32Source . GetChannels ( ) )
for b := range f32Source . GetBlocks ( ) {
blockCount ++
sampleCount += uint64 ( len ( b ) )
}
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 {
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 ) )
}
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 ( ) )
2023-01-29 13:34:26 +00:00
hCRC32 . Wait ( )
hSHA256 . Wait ( )
hMD5 . Wait ( )
log . Printf ( "CRC32 hash: %s" , hex . EncodeToString ( hCRC32 . GetResult ( ) ) )
log . Printf ( "SHA256 hash: %s" , hex . EncodeToString ( hSHA256 . GetResult ( ) ) )
log . Printf ( "MD5 hash: %s" , hex . EncodeToString ( hMD5 . 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 ) )
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" )
} 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 ) )
2023-01-28 14:18:16 +00:00
}
}
log . Printf ( "Took %s" , time . Now ( ) . Sub ( startTime ) . String ( ) )
}