Ejemplo n.º 1
0
 /* If we want to merge two clusters together, we add all the intervals from one into the other. */
 public void MergeCluster(IntervalCluster cluster)
 {
     foreach (EventInterval interval in cluster.Intervals)
     {
         AddInterval(interval);
     }
 }
Ejemplo n.º 2
0
        /* In order to obtain a ranking for our different hypotheses, we compare the clusters
         * against each other to see which of them are integer multiples within a reasonable margin.
         * Those with many multiples will have a higher score overall than ones without. */
        public static List <IntervalCluster> RateClusters(List <IntervalCluster> clusters)
        {
            foreach (IntervalCluster baseCluster in clusters)
            {
                foreach (IntervalCluster comparisonCluster in clusters)
                {
                    for (int i = 1; i < 9; i++)
                    {
                        if (Math.Abs(baseCluster.MeanLength - (i * comparisonCluster.MeanLength)) < clusterWidth)
                        {
                            baseCluster.Rating += Weight(i) * comparisonCluster.Intervals.Count;
                        }
                    }
                }
            }

            /* Order the clusters by their rating from highest to lowest and then print them if desired. */
            clusters = clusters.OrderByDescending(c => c.Rating).ToList();

            /* Remove clusters with unrealistically large or small intervals,
             * greater than 240 BPM or less than 60 BPM. */
            for (int i = clusters.Count - 1; i >= 0; i--)
            {
                IntervalCluster cluster = clusters[i];
                if (cluster.MeanLength < 250 || cluster.MeanLength > 1000)
                {
                    clusters.Remove(cluster);
                }
            }

            if (debugPrintRatedClusters)
            {
                foreach (IntervalCluster cluster in clusters)
                {
                    Console.WriteLine("Interval Cluster " + cluster.GetBPM() + " BPM, with " + cluster.Intervals.Count + " notes, score " + cluster.Rating + ".");
                }
            }

            return(clusters);
        }
Ejemplo n.º 3
0
        public static BeatTracker FindBeat(List <IntervalCluster> tempoHypotheses, List <BeatEvent> events)
        {
            /* First, create all the trackers. */
            List <BeatTracker> trackers = new List <BeatTracker>();

            for (int i = 0; i < tempoHypotheses.Count; i++)
            {
                IntervalCluster cluster = tempoHypotheses[i];
                foreach (BeatEvent startEvent in events.Where(e => e.Time < initialPeriod).ToList())
                {
                    trackers.Add(new BeatTracker(cluster.MeanLength, startEvent, cluster.Rating));
                }
            }

            /* Iterate through every event in the rhythm, processing each tracker. */
            foreach (BeatEvent _event in events)
            {
                List <BeatTracker> newTrackers = new List <BeatTracker>();

                for (int i = trackers.Count - 1; i >= 0; i--)
                {
                    /* If any tracker has gone too long without detecting a beat candidate, drop it. */
                    BeatTracker tracker = trackers[i];
                    if (_event.Time - tracker.ProcessedItems[tracker.ProcessedItems.Count - 1].Time > maximumInterval)
                    {
                        trackers.RemoveAt(i);
                    }
                    else
                    {
                        /* Catch the trackers up with the current event. */
                        while (tracker.NextPrediction + (outerWindowFactor * tracker.Interval) < _event.Time)
                        {
                            tracker.NextPrediction += tracker.Interval;
                        }

                        /* Check whether the event is a feasible beat time. */
                        if (_event.Time > tracker.NextPrediction - (outerWindowFactor * tracker.Interval) &&
                            (_event.Time < tracker.NextPrediction + (outerWindowFactor * tracker.Interval)))
                        {
                            /* If it's too close to be sure, create another tracker. */
                            if (Math.Abs(_event.Time - tracker.NextPrediction) > innerWindow)
                            {
                                newTrackers.Add(new BeatTracker(tracker));
                            }

                            /* Update the tracker to prepare for the next loop iteration. */
                            double error = _event.Time - tracker.NextPrediction;
                            tracker.Interval      += error * correctionFactor;
                            tracker.NextPrediction = _event.Time + tracker.Interval;
                            tracker.ProcessedItems.Add(_event);

                            // NOTE: It might be useful to have drum specific stuff as well as velocity
                            tracker.Rating += (1 - (Math.Abs(error) / (2 * tracker.NextPrediction))) * _event.Notes.Sum(n => n.Velocity);
                        }
                    }
                }

                // Add new trackers to the list
                foreach (BeatTracker tracker in newTrackers)
                {
                    trackers.Add(tracker);
                }

                // Remove duplicate trackers
                List <BeatTracker> nextTrackers = new List <BeatTracker>();
                foreach (BeatTracker tracker in trackers)
                {
                    bool matchFound = false;
                    foreach (BeatTracker nextTracker in nextTrackers)
                    {
                        if (!matchFound && SimilarTrackers(tracker, nextTracker))
                        {
                            nextTracker.TakeBestTracker(tracker);
                            matchFound = true;
                        }
                    }

                    if (!matchFound)
                    {
                        nextTrackers.Add(tracker);
                    }
                }

                trackers = nextTrackers;
            }

            /* Weight each tracker's rating by the original score, so as to avoid very short beat selections. */
            foreach (BeatTracker tracker in trackers)
            {
                tracker.Rating *= tracker.OriginalScore;
            }

            trackers = trackers.OrderByDescending(t => t.Rating).ToList();

            // TODO: There are a lot of trackers that appear to be duplicates. Investigate.
            for (int i = 0; i < Math.Min(trackers.Count, 10); i++)
            {
                trackers[i].PrintTracker();
            }

            return(trackers[0]);
        }
Ejemplo n.º 4
0
        /* EventsToClusters takes a list of beat events, finds all the intervals between them
         * that are less than 2 seconds long, and then clusters the intervals together into a list of
         * tempo hypotheses. */
        public static List <IntervalCluster> EventsToClusters(List <BeatEvent> beatEvents)
        {
            List <IntervalCluster> clusters = new List <IntervalCluster>();

            for (int i = 0; i < beatEvents.Count; i++)
            {
                for (int j = i + 1; j < beatEvents.Count; j++)
                {
                    EventInterval interval = new EventInterval(beatEvents[i], beatEvents[j]);
                    if (interval.Length < 2000)
                    {
                        bool   clusterFound    = false;
                        int    clusterIndex    = 0;
                        double clusterDistance = double.PositiveInfinity;

                        for (int c = 0; c < clusters.Count; c++)
                        {
                            IntervalCluster cluster    = clusters[c];
                            double          difference = Math.Abs(cluster.MeanLength - interval.Length);
                            if (difference < clusterWidth && difference < clusterDistance)
                            {
                                clusterFound    = true;
                                clusterIndex    = c;
                                clusterDistance = difference;
                            }
                        }

                        if (clusterFound)
                        {
                            clusters[clusterIndex].AddInterval(interval);
                        }
                        else
                        {
                            clusters.Add(new IntervalCluster(interval));
                        }
                    }
                }
            }

            /* Now cluster the clusters just in case any of the averages have strayed close together. */
            List <IntervalCluster> newClusters = new List <IntervalCluster>();

            foreach (IntervalCluster cluster in clusters)
            {
                bool matchFound = false;
                foreach (IntervalCluster newCluster in newClusters)
                {
                    if (!matchFound && Math.Abs(cluster.MeanLength - newCluster.MeanLength) < clusterWidth)
                    {
                        newCluster.MergeCluster(cluster);
                        matchFound = true;
                    }
                }

                if (!matchFound)
                {
                    newClusters.Add(cluster);
                }
            }

            /* If the cluster printing flag is set, then display a list of all the found clusters. */
            if (debugPrintClusters)
            {
                foreach (IntervalCluster cluster in newClusters)
                {
                    Console.WriteLine("Interval Cluster " + cluster.MeanLength + "ms, with " + cluster.Intervals.Count + " notes.");
                }
            }

            return(newClusters);
        }