75 lines
2.1 KiB
Go
75 lines
2.1 KiB
Go
package subsync
|
|||
|
|
|
||
|
|
import "math/bits"
|
||
|
|
|
||
|
|
// signal is "somebody is speaking" as one bit per time bin.
|
||
|
|
//
|
||
|
|
// A bitset rather than a []bool because the search is the whole cost of this feature: a
|
||
|
|
// two-hour film at 50ms is around 144,000 bins, and every one of ~2,400 shifts has to
|
||
|
|
// compare all of them against every stretch candidate. Packed into words, one comparison
|
||
|
|
// is an AND and a population count over 2,250 words instead of 144,000 byte reads, which
|
||
|
|
// is the difference between a button that answers while somebody is still looking at the
|
||
|
|
// menu and one they wait on. math/bits.OnesCount64 compiles to a single instruction on
|
||
|
|
// every architecture this runs on.
|
||
|
|
type signal struct {
|
||
|
|
words []uint64
|
||
|
|
bins int
|
||
|
|
on int
|
||
|
|
}
|
||
|
|
|
||
|
|
func newSignal(bins int) *signal {
|
||
|
|
if bins < 1 {
|
||
|
|
bins = 1
|
||
|
|
}
|
||
|
|
return &signal{words: make([]uint64, (bins+63)/64), bins: bins}
|
||
|
|
}
|
||
|
|
|
||
|
|
// set marks the half-open bin range [from, to) as speech.
|
||
|
|
func (s *signal) set(from, to int) {
|
||
|
|
if from < 0 {
|
||
|
|
from = 0
|
||
|
|
}
|
||
|
|
if to > s.bins {
|
||
|
|
to = s.bins
|
||
|
|
}
|
||
|
|
for i := from; i < to; i++ {
|
||
|
|
word, bit := i/64, uint(i%64)
|
||
|
|
if s.words[word]&(1<<bit) == 0 {
|
||
|
|
s.words[word] |= 1 << bit
|
||
|
|
s.on++
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// overlap counts the bins where both signals are speaking, with other displaced by shift
|
||
|
|
// bins. A positive shift means other is read later — the value to add to this signal's
|
||
|
|
// times to bring them onto other's.
|
||
|
|
//
|
||
|
|
// Negative shifts are answered by swapping the two, which is the same count from the other
|
||
|
|
// side and saves writing the word arithmetic twice in mirror image.
|
||
|
|
func (s *signal) overlap(other *signal, shift int) int {
|
||
|
|
if shift < 0 {
|
||
|
|
return other.overlap(s, -shift)
|
||
|
|
}
|
||
|
|
wordShift, bitShift := shift/64, uint(shift%64)
|
||
|
|
total := 0
|
||
|
|
for i := range s.words {
|
||
|
|
mine := s.words[i]
|
||
|
|
if mine == 0 {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
j := i + wordShift
|
||
|
|
if j >= len(other.words) {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
theirs := other.words[j] >> bitShift
|
||
|
|
// Go defines a shift of 64 or more as zero, so this term vanishes when bitShift
|
||
|
|
// is zero rather than needing a branch of its own.
|
||
|
|
if j+1 < len(other.words) {
|
||
|
|
theirs |= other.words[j+1] << (64 - bitShift)
|
||
|
|
}
|
||
|
|
total += bits.OnesCount64(mine & theirs)
|
||
|
|
}
|
||
|
|
return total
|
||
|
|
}
|