Expose algorithm internal settings

This commit is contained in:
ConfusedPolarBear 2022-09-02 01:27:49 -05:00
parent 6dc3a5fa41
commit cf99dde0e2
3 changed files with 102 additions and 48 deletions

View File

@ -62,17 +62,6 @@ public class PluginConfiguration : BasePluginConfiguration
/// </summary>
public int MinimumIntroDuration { get; set; } = 15;
/// <summary>
/// Gets or sets the maximum amount of noise (in dB) that is considered silent.
/// Lowering this number will increase the filter's sensitivity to noise.
/// </summary>
public int SilenceDetectionMaximumNoise { get; set; } = -50;
/// <summary>
/// Gets or sets the minimum duration of audio (in seconds) that is considered silent.
/// </summary>
public double SilenceDetectionMinimumDuration { get; set; } = 0.50;
// ===== Playback settings =====
/// <summary>
@ -93,5 +82,34 @@ public class PluginConfiguration : BasePluginConfiguration
/// <summary>
/// Gets or sets the amount of intro to play (in seconds).
/// </summary>
public int SecondsOfIntroToPlay { get; set; } = 2;
public int SecondsOfIntroToPlay { get; set; } = 3;
// ===== Internal algorithm settings =====
/// <summary>
/// Gets or sets the maximum number of bits (out of 32 total) that can be different between two Chromaprint points before they are considered dissimilar.
/// Defaults to 6 (81% similar).
/// </summary>
public int MaximumFingerprintPointDifferences { get; set; } = 6;
/// <summary>
/// Gets or sets the maximum number of seconds that can pass between two similar fingerprint points before a new time range is started.
/// </summary>
public double MaximumTimeSkip { get; set; } = 3.5;
/// <summary>
/// Gets or sets the amount to shift inverted indexes by.
/// </summary>
public int InvertedIndexShift { get; set; } = 2;
/// <summary>
/// Gets or sets the maximum amount of noise (in dB) that is considered silent.
/// Lowering this number will increase the filter's sensitivity to noise.
/// </summary>
public int SilenceDetectionMaximumNoise { get; set; } = -50;
/// <summary>
/// Gets or sets the minimum duration of audio (in seconds) that is considered silent.
/// </summary>
public double SilenceDetectionMinimumDuration { get; set; } = 0.33;
}

View File

@ -154,6 +154,32 @@
Increasing either of these settings will cause episode analysis to take much longer.
</p>
</details>
<details id="silence">
<summary>Silence detection options</summary>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="SilenceDetectionMaximumNoise">
Noise tolerance
</label>
<input id="SilenceDetectionMaximumNoise" type="number" is="emby-input" min="-90"
max="0" />
<div class="fieldDescription">
Noise tolerance in negative decibels.
</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="SilenceDetectionMinimumDuration">
Minimum silence duration
</label>
<input id="SilenceDetectionMinimumDuration" type="number" is="emby-input" min="0"
step="0.01" />
<div class="fieldDescription">
Minimum silence duration in seconds before adjusting introduction end time.
</div>
</div>
</details>
</fieldset>
<fieldset class="verticalSection-extrabottompadding">
@ -190,6 +216,16 @@
Seconds after the introduction starts to hide the skip prompt at.
</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="SecondsOfIntroToPlay">
Seconds of intro to play
</label>
<input id="SecondsOfIntroToPlay" type="number" is="emby-input" min="0" />
<div class="fieldDescription">
Seconds of introduction that should be played. Defaults to 2.
</div>
</div>
</fieldset>
<div>
@ -201,12 +237,6 @@
<button id="btnEraseTimestamps" is="emby-button" class="raised block emby-button">
<span>Erase introduction timestamps</span>
</button>
<p>
Erasing introduction timestamps is only necessary after upgrading the plugin if specifically
requested to do so in the plugin's changelog. After the timestamps are erased, run the
Analyze episodes scheduled task to re-analyze all media on the server.
</p>
</div>
</form>
</div>
@ -337,14 +367,20 @@
// all plugin configuration fields that can be get or set with .value (i.e. strings or numbers).
var configurationFields = [
// analysis
"MaxParallelism",
"SelectedLibraries",
"AnalysisPercent",
"AnalysisLengthLimit",
"MinimumIntroDuration",
"EdlAction",
// playback
"ShowPromptAdjustment",
"HidePromptAdjustment"
"HidePromptAdjustment",
"SecondsOfIntroToPlay",
// internals
"SilenceDetectionMaximumNoise",
"SilenceDetectionMinimumDuration",
]
// visualizer elements

View File

@ -15,22 +15,6 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
/// </summary>
public class AnalyzeEpisodesTask : IScheduledTask
{
/// <summary>
/// Maximum number of bits (out of 32 total) that can be different between segments before they are considered dissimilar.
/// 6 bits means the audio must be at least 81% similar (1 - 6 / 32).
/// </summary>
private const double MaximumDifferences = 6;
/// <summary>
/// Maximum time (in seconds) permitted between timestamps before they are considered non-contiguous.
/// </summary>
private const double MaximumDistance = 3.5;
/// <summary>
/// Amount to shift inverted index offsets by.
/// </summary>
private const int InvertedIndexShift = 2;
/// <summary>
/// Seconds of audio in one fingerprint point. This value is defined by the Chromaprint library and should not be changed.
/// </summary>
@ -57,6 +41,14 @@ public class AnalyzeEpisodesTask : IScheduledTask
/// </summary>
private static int minimumIntroDuration = 15;
private static int maximumDifferences = 6;
private static int invertedIndexShift = 2;
private static double maximumTimeSkip = 3.5;
private static double silenceDetectionMinimumDuration = 0.33;
/// <summary>
/// Initializes a new instance of the <see cref="AnalyzeEpisodesTask"/> class.
/// </summary>
@ -124,6 +116,13 @@ public class AnalyzeEpisodesTask : IScheduledTask
"No episodes to analyze. If you are limiting the list of libraries to analyze, check that all library names have been spelled correctly.");
}
// Load analysis settings from configuration
var config = Plugin.Instance?.Configuration ?? new Configuration.PluginConfiguration();
maximumDifferences = config.MaximumFingerprintPointDifferences;
invertedIndexShift = config.InvertedIndexShift;
maximumTimeSkip = config.MaximumTimeSkip;
silenceDetectionMinimumDuration = config.SilenceDetectionMinimumDuration;
// Log EDL settings
EdlManager.LogConfiguration();
@ -448,7 +447,7 @@ public class AnalyzeEpisodesTask : IScheduledTask
{
var originalPoint = kvp.Key;
for (var i = -1 * InvertedIndexShift; i <= InvertedIndexShift; i++)
for (var i = -1 * invertedIndexShift; i <= invertedIndexShift; i++)
{
var modifiedPoint = (uint)(originalPoint + i);
@ -542,7 +541,7 @@ public class AnalyzeEpisodesTask : IScheduledTask
var diff = lhs[lhsPosition] ^ rhs[rhsPosition];
// If the difference between the samples is small, flag both times as similar.
if (CountBits(diff) > MaximumDifferences)
if (CountBits(diff) > maximumDifferences)
{
continue;
}
@ -559,25 +558,25 @@ public class AnalyzeEpisodesTask : IScheduledTask
rhsTimes.Add(double.MaxValue);
// Now that both fingerprints have been compared at this shift, see if there's a contiguous time range.
var lContiguous = TimeRangeHelpers.FindContiguous(lhsTimes.ToArray(), MaximumDistance);
var lContiguous = TimeRangeHelpers.FindContiguous(lhsTimes.ToArray(), maximumTimeSkip);
if (lContiguous is null || lContiguous.Duration < minimumIntroDuration)
{
return (new TimeRange(), new TimeRange());
}
// Since LHS had a contiguous time range, RHS must have one also.
var rContiguous = TimeRangeHelpers.FindContiguous(rhsTimes.ToArray(), MaximumDistance)!;
var rContiguous = TimeRangeHelpers.FindContiguous(rhsTimes.ToArray(), maximumTimeSkip)!;
// Tweak the end timestamps just a bit to ensure as little content as possible is skipped over.
if (lContiguous.Duration >= 90)
{
lContiguous.End -= 2 * MaximumDistance;
rContiguous.End -= 2 * MaximumDistance;
lContiguous.End -= 2 * maximumTimeSkip;
rContiguous.End -= 2 * maximumTimeSkip;
}
else if (lContiguous.Duration >= 30)
{
lContiguous.End -= MaximumDistance;
rContiguous.End -= MaximumDistance;
lContiguous.End -= maximumTimeSkip;
rContiguous.End -= maximumTimeSkip;
}
return (lContiguous, rContiguous);
@ -612,9 +611,8 @@ public class AnalyzeEpisodesTask : IScheduledTask
continue;
}
// Since we only want to adjust the end timestamp of the intro, create a new TimeRange
// that covers the last few seconds.
var originalIntroEnd = new TimeRange(originalIntro.IntroEnd - 10, originalIntro.IntroEnd);
// Only adjust the end timestamp of the intro
var originalIntroEnd = new TimeRange(originalIntro.IntroEnd - 15, originalIntro.IntroEnd);
_logger.LogTrace(
"{Name} original intro: {Start} - {End}",
@ -636,8 +634,10 @@ public class AnalyzeEpisodesTask : IScheduledTask
// Ignore any silence that:
// * doesn't intersect the ending of the intro, or
// * is less than half a second long
if (!originalIntroEnd.Intersects(currentRange) || currentRange.Duration < 0.5)
// * is shorter than the user defined minimum duration
if (
!originalIntroEnd.Intersects(currentRange) ||
currentRange.Duration < silenceDetectionMinimumDuration)
{
continue;
}