Kirika/hasher/hasher.go
DataHoarder de2974b87d
All checks were successful
continuous-integration/drone/push Build is passing
Reduce package dependencies
2022-12-06 10:46:32 +01:00

170 lines
3.3 KiB
Go

package hasher
import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"encoding/binary"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format"
"git.gammaspectra.live/S.O.N.G/Kirika/vector"
"hash"
"hash/crc32"
"sync"
"time"
)
const TocPregap = 150
const SectorsPerSecond = 75
const DataTrackGap = 11400
const BytesPerSector = 2352
const CDChannels = 2
const Int16SamplesPerSector = BytesPerSector / (2 * CDChannels)
const CDSampleRate = Int16SamplesPerSector * SectorsPerSecond
type HashType int
const (
HashtypeCrc32 = HashType(iota)
HashtypeSha256
HashtypeSha1
HashtypeMd5
HashtypeAccurateRipV1
HashtypeAccurateRipV1Start
HashtypeAccurateRipV2
HashtypeAccurateRipV2Start
)
type Hasher struct {
hash HashType
hasher hash.Hash
result []byte
channel format.AnalyzerChannel
wg sync.WaitGroup
samples int
duration float64
sampleRate int
bitDepth int
channels int
buffer [][]int32
}
func NewHasher(channel format.AnalyzerChannel, hashType HashType) (h *Hasher) {
h = &Hasher{
hash: hashType,
channel: channel,
}
switch hashType {
case HashtypeCrc32:
h.hasher = crc32.NewIEEE()
case HashtypeSha256:
h.hasher = sha256.New()
case HashtypeSha1:
h.hasher = sha1.New()
case HashtypeMd5:
h.hasher = md5.New()
case HashtypeAccurateRipV1:
h.hasher = NewAccurateRipV1(0)
case HashtypeAccurateRipV1Start:
h.hasher = NewAccurateRipV1(Int16SamplesPerSector*5 - 1)
case HashtypeAccurateRipV2:
h.hasher = NewAccurateRipV2(0)
case HashtypeAccurateRipV2Start:
h.hasher = NewAccurateRipV2(Int16SamplesPerSector*5 - 1)
}
h.startRoutine()
return
}
func (h *Hasher) startRoutine() {
h.wg.Add(1)
go func() {
defer h.wg.Done()
for packet := range h.channel {
h.handlePacket(packet)
}
h.result = h.hasher.Sum([]byte{})
}()
}
func (h *Hasher) handlePacket(packet *format.AnalyzerPacket) {
samples := len(packet.Samples) / packet.Channels
h.samples += samples
if h.sampleRate == 0 {
h.sampleRate = packet.SampleRate
} else if h.sampleRate != packet.SampleRate {
h.sampleRate = -1
}
if h.bitDepth == 0 {
h.bitDepth = packet.BitDepth
} else if h.bitDepth != packet.BitDepth {
h.bitDepth = -1
}
if h.channels == 0 {
h.channels = packet.Channels
} else if h.channels != packet.Channels {
h.channels = -1
}
h.duration += float64(samples) / float64(packet.SampleRate)
var buf []byte
switch packet.BitDepth {
case 8, 16, 24, 32:
buf = vector.Int32ToBytes(packet.Samples, packet.BitDepth)
default:
buf = make([]byte, len(packet.Samples)*4)
for i := range packet.Samples {
binary.LittleEndian.PutUint32(buf[i*4:], uint32(packet.Samples[i]))
}
}
h.hasher.Write(buf)
}
func (h *Hasher) GetSampleCount() int {
return h.samples
}
func (h *Hasher) GetChannels() int {
return h.channels
}
func (h *Hasher) GetSampleRate() int {
return h.sampleRate
}
func (h *Hasher) GetHashType() HashType {
return h.hash
}
func (h *Hasher) GetResult() []byte {
return h.result
}
func (h *Hasher) GetDuration() time.Duration {
if h.sampleRate > 0 {
return time.Duration(float64(time.Second) * (float64(h.samples) / float64(h.sampleRate)))
}
//Fallback calculated duration
return time.Duration(float64(time.Second) * h.duration)
}
func (h *Hasher) GetWaitGroup() *sync.WaitGroup {
return &h.wg
}
func (h *Hasher) Wait() {
h.wg.Wait()
}