// Clusters the provided 'taps' into an optimal set of beats and estimates the average BPM and the offset of the
// first beats (on the assumption that the BPM is fixed).
//
// The supplied list of 'taps' may contain multiple loops, with each loop being a separate row in the list.
//
// The forgetting factor is used to discount earlier loops in favour of later loops, on the grounds that later 'taps'
// will probably be more accurate. A forgetting factor of 0.0 assumes all taps are equally accurate, while a value of
// 0.1 discounts each loop by 10% over the subsequent loop. A forgetting factor of -0.1 discounts each subsequent loop
// by 10% over the preceding loop.
Clusters the provided 'taps' into an optimal set of beats and estimates the average BPM and the offset of the
first beats (on the assumption that the BPM is fixed).

The supplied list of 'taps' may contain multiple loops, with each loop being a separate row in the list.

The forgetting factor is used to discount earlier loops in favour of later loops, on the grounds that later 'taps'
will probably be more accurate. A forgetting factor of 0.0 assumes all taps are equally accurate, while a value of
0.1 discounts each loop by 10% over the subsequent loop. A forgetting factor of -0.1 discounts each subsequent loop
by 10% over the preceding loop.
// Clusters the provided ' taps ' into an optimal set of beats and estimates the average BPM and the offset of the // first beats ( on the assumption that the BPM is fixed ) . // // The supplied list of ' taps ' may contain multiple loops with each loop being a separate row in the list . // // The forgetting factor is used to discount earlier loops in favour of later loops on the grounds that later ' taps ' // will probably be more accurate . A forgetting factor of 0 . 0 assumes all taps are equally accurate while a value of // 0 . 1 discounts each loop by 10% over the subsequent loop . A forgetting factor of - 0 . 1 discounts each subsequent loop // by 10% over the preceding loop .
func Taps2Beats(taps [][]time.Duration, forgetting float64) Beats {
	data := []float64{}
	for _, row := range taps {
		for _, t := range row {
			data = append(data, t.Seconds())
		}
	}
	clusters := ckmeans.CKMeans1dDp(data, weights(taps, forgetting))
	beats := make([]Beat, len(clusters))
	for i, cluster := range clusters {
		beats[i] = makeBeat(cluster.Center, cluster)
	}
	BPM, offset := bpm(beats)
	sort.SliceStable(beats, func(i, j int) bool { return beats[i].At < beats[j].At })
	result := Beats{
		BPM:    BPM,
		Offset: offset,
		Beats:  beats,
	}
	if len(beats) > 0 {
		variance := 0.0
		for _, b := range beats {
			variance += b.Variance.Seconds()
		}
		variance = variance / float64(len(beats))
		result.Variance = &variance
	}
	return result
}
func Taps2Beats ( taps [ ] [ ] time . Duration , forgetting float64 ) Beats { data := [ ] float64 { } for _ , row := range taps { for _ , t := range row { data = append ( data , t . Seconds ( ) ) } } clusters := ckmeans . CKMeans1dDp ( data , weights ( taps , forgetting ) ) beats := make ( [ ] Beat , len ( clusters ) ) for i , cluster := range clusters { beats [ i ] = makeBeat ( cluster . Center , cluster ) } BPM , offset := bpm ( beats ) sort . SliceStable ( beats , func ( i , j int ) bool { return beats [ i ] . At < beats [ j ] . At } ) result := Beats { BPM : BPM , Offset : offset , Beats : beats , } if len ( beats ) > 0 { variance := 0.0 for _ , b := range beats { variance += b . Variance . Seconds ( ) } variance = variance / float64 ( len ( beats ) ) result . Variance = & variance } return result }
Clusters the provided 'taps' into an optimal set of beats and estimates the average BPM and the offset of the
first beats (on the assumption that the BPM is fixed).
Clusters the provided ' taps ' into an optimal set of beats and estimates the average BPM and the offset of the first beats ( on the assumption that the BPM is fixed ) .