Esempio n. 1
0
        public override IEnumerable <Issue> GetIssues(BeatmapSet beatmapSet)
        {
            foreach (string hsFile in beatmapSet.hitSoundFiles)
            {
                HitObject hitObjectActiveAt = GetHitObjectActiveAt(beatmapSet, hsFile);
                if (hitObjectActiveAt == null)
                {
                    // Hit sound is never active, so delay does not matter.
                    continue;
                }

                string hsPath = Path.Combine(beatmapSet.songPath, hsFile);

                List <float[]> peaks     = null;
                Exception      exception = null;
                try
                {
                    peaks = AudioBASS.GetPeaks(hsPath);
                }
                catch (Exception ex)
                {
                    exception = ex;
                }

                if (exception == null)
                {
                    if (!(peaks?.Count > 0) || !(peaks.Sum(peak => peak.Sum()) > 0))
                    {
                        // Muted files don't have anything to be delayed, hence ignore.
                        continue;
                    }

                    double maxStrength = peaks.Select(value => Math.Abs(value.Sum())).Max();

                    int    delay     = 0;
                    int    pureDelay = 0;
                    double strength  = 0;
                    while (delay + pureDelay < peaks.Count)
                    {
                        strength += Math.Abs(peaks[delay].Sum());

                        if (strength >= maxStrength / 2)
                        {
                            break;
                        }

                        strength *= 0.95;

                        // The delay added by MP3 encoding still has very slight volume where it's basically silent.
                        if (strength < 0.001)
                        {
                            strength = 0;
                            ++pureDelay;
                            ++delay;
                        }
                        else
                        {
                            ++delay;
                        }
                    }

                    if (pureDelay >= 5)
                    {
                        yield return(new Issue(GetTemplate("Pure Delay"), null,
                                               hsFile, $"{pureDelay:0.##}"));
                    }

                    else if (delay + pureDelay >= 5)
                    {
                        yield return(new Issue(GetTemplate("Delay"), null,
                                               hsFile, $"{pureDelay:0.##}", $"{delay:0.##}",
                                               Timestamp.Get(hitObjectActiveAt), hitObjectActiveAt.beatmap));
                    }

                    else if (delay + pureDelay >= 1)
                    {
                        yield return(new Issue(GetTemplate("Minor Delay"), null,
                                               hsFile, $"{pureDelay:0.##}", $"{delay:0.##}"));
                    }
                }
                else
                {
                    yield return(new Issue(GetTemplate("Unable to check"), null,
                                           hsFile, Common.ExceptionTag(exception)));
                }
            }
        }
Esempio n. 2
0
        public override IEnumerable <Issue> GetIssues(BeatmapSet beatmapSet)
        {
            foreach (string hsFile in beatmapSet.hitSoundFiles)
            {
                string hsPath = Path.Combine(beatmapSet.songPath, hsFile);

                int            channels  = 0;
                List <float[]> peaks     = null;
                Exception      exception = null;
                try
                {
                    channels = AudioBASS.GetChannels(hsPath);
                    peaks    = AudioBASS.GetPeaks(hsPath);
                }
                catch (Exception ex)
                { exception = ex; }

                // Cannot yield in catch statements, hence externally handled.
                if (exception != null)
                {
                    yield return(new Issue(GetTemplate("Unable to check"), null,
                                           hsFile, Common.ExceptionTag(exception)));

                    continue;
                }

                // Mono cannot be imbalanced; same audio on both sides.
                if (channels < 2)
                {
                    continue;
                }

                // Silent audio cannot be imbalanced.
                if (peaks.Count == 0)
                {
                    continue;
                }

                float leftSum  = peaks.Sum(peak => peak?[0] ?? 0);
                float rightSum = peaks.Sum(peak => peak.Count() > 1 ? peak?[1] ?? 0 : 0);
                if (leftSum == 0 || rightSum == 0)
                {
                    yield return(new Issue(GetTemplate("Warning Silent"), null,
                                           hsFile, leftSum - rightSum > 0 ? "left" : "right"));

                    continue;
                }

                // 2 would mean one is double the sum of the other.
                float relativeVolume =
                    leftSum > rightSum ?
                    leftSum / rightSum :
                    rightSum / leftSum;

                if (relativeVolume < 2)
                {
                    continue;
                }

                // Imbalance is only an issue if it is used frequently in a short timespan or it's overall common.
                Common.CollectHitSoundFrequency(beatmapSet, hsFile, scoreThreshold: 14 / relativeVolume,
                                                out string mostFrequentTimestamp, out Dictionary <Beatmap, int> uses);

                if (mostFrequentTimestamp != null)
                {
                    yield return(new Issue(GetTemplate("Warning Timestamp"), null,
                                           hsFile, leftSum - rightSum > 0 ? "left" : "right", mostFrequentTimestamp));
                }
                else
                {
                    Beatmap mapCommonlyUsedIn =
                        Common.GetBeatmapCommonlyUsedIn(beatmapSet, uses, commonUsageThreshold: 10000);

                    if (mapCommonlyUsedIn != null)
                    {
                        yield return(new Issue(GetTemplate("Warning Common"), null,
                                               hsFile, leftSum - rightSum > 0 ? "left" : "right", mapCommonlyUsedIn));
                    }
                    else
                    {
                        yield return(new Issue(GetTemplate("Minor"), null,
                                               hsFile, leftSum - rightSum > 0 ? "left" : "right"));
                    }
                }
            }
        }