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() }