mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 09:32:55 +08:00
Add too short audio files check and tests
This commit is contained in:
parent
0a8fd01b99
commit
a4a1919842
117
osu.Game.Tests/Editing/Checks/CheckTooShortAudioFilesTest.cs
Normal file
117
osu.Game.Tests/Editing/Checks/CheckTooShortAudioFilesTest.cs
Normal file
@ -0,0 +1,117 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ManagedBass;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Checks;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osuTK.Audio;
|
||||
using FileInfo = osu.Game.IO.FileInfo;
|
||||
|
||||
namespace osu.Game.Tests.Editing.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class CheckTooShortAudioFilesTest
|
||||
{
|
||||
private CheckTooShortAudioFiles check;
|
||||
private IBeatmap beatmap;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
check = new CheckTooShortAudioFiles();
|
||||
beatmap = new Beatmap<HitObject>
|
||||
{
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
BeatmapSet = new BeatmapSetInfo
|
||||
{
|
||||
Files = new List<BeatmapSetFileInfo>(new[]
|
||||
{
|
||||
new BeatmapSetFileInfo
|
||||
{
|
||||
Filename = "abc123.wav",
|
||||
FileInfo = new FileInfo { Hash = "abcdef" }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 0 = No output device. This still allows decoding.
|
||||
if (!Bass.Init(0) && Bass.LastError != Errors.Already)
|
||||
throw new AudioException("Could not initialize Bass.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDifferentExtension()
|
||||
{
|
||||
beatmap.BeatmapInfo.BeatmapSet.Files.Clear();
|
||||
beatmap.BeatmapInfo.BeatmapSet.Files.Add(new BeatmapSetFileInfo
|
||||
{
|
||||
Filename = "abc123.jpg",
|
||||
FileInfo = new FileInfo { Hash = "abcdef" }
|
||||
});
|
||||
|
||||
// Should fail to load, but not produce an error due to the extension not being expected to load.
|
||||
Assert.IsEmpty(check.Run(getContext(null, allowMissing: true)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRegularAudioFile()
|
||||
{
|
||||
Assert.IsEmpty(check.Run(getContext("Samples/test-sample.mp3")));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBlankAudioFile()
|
||||
{
|
||||
// This is a 0 ms duration audio file, commonly used to silence sliderslides/ticks, and so should be fine.
|
||||
Assert.IsEmpty(check.Run(getContext("Samples/blank.wav")));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTooShortAudioFile()
|
||||
{
|
||||
var issues = check.Run(getContext("Samples/test-sample-cut.mp3")).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckTooShortAudioFiles.IssueTemplateTooShort);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMissingAudioFile()
|
||||
{
|
||||
Assert.IsEmpty(check.Run(getContext("Samples/missing.mp3", allowMissing: true)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCorruptAudioFile()
|
||||
{
|
||||
var issues = check.Run(getContext("Samples/corrupt.wav")).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckTooShortAudioFiles.IssueTemplateBadFormat);
|
||||
}
|
||||
|
||||
private BeatmapVerifierContext getContext(string resourceName, bool allowMissing = false)
|
||||
{
|
||||
Stream resourceStream = string.IsNullOrEmpty(resourceName) ? null : TestResources.OpenResource(resourceName);
|
||||
if (!allowMissing && resourceStream == null)
|
||||
throw new FileNotFoundException($"The requested test resource \"{resourceName}\" does not exist.");
|
||||
|
||||
var mockWorkingBeatmap = new Mock<TestWorkingBeatmap>(beatmap, null, null);
|
||||
mockWorkingBeatmap.Setup(w => w.GetStream(It.IsAny<string>())).Returns(resourceStream);
|
||||
|
||||
return new BeatmapVerifierContext(beatmap, mockWorkingBeatmap.Object);
|
||||
}
|
||||
}
|
||||
}
|
BIN
osu.Game.Tests/Resources/Samples/blank.wav
Normal file
BIN
osu.Game.Tests/Resources/Samples/blank.wav
Normal file
Binary file not shown.
BIN
osu.Game.Tests/Resources/Samples/corrupt.wav
Normal file
BIN
osu.Game.Tests/Resources/Samples/corrupt.wav
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
BIN
osu.Game.Tests/Resources/Samples/test-sample-cut.mp3
Normal file
BIN
osu.Game.Tests/Resources/Samples/test-sample-cut.mp3
Normal file
Binary file not shown.
@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
new CheckAudioQuality(),
|
||||
new CheckMutedObjects(),
|
||||
new CheckFewHitsounds(),
|
||||
new CheckTooShortAudioFiles(),
|
||||
|
||||
// Files
|
||||
new CheckZeroByteFiles(),
|
||||
|
83
osu.Game/Rulesets/Edit/Checks/CheckTooShortAudioFiles.cs
Normal file
83
osu.Game/Rulesets/Edit/Checks/CheckTooShortAudioFiles.cs
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ManagedBass;
|
||||
using osu.Framework.Audio.Callbacks;
|
||||
using osu.Game.Rulesets.Edit.Checks.Components;
|
||||
|
||||
namespace osu.Game.Rulesets.Edit.Checks
|
||||
{
|
||||
public class CheckTooShortAudioFiles : ICheck
|
||||
{
|
||||
private const int ms_threshold = 25;
|
||||
private const int min_bytes_threshold = 100;
|
||||
|
||||
private readonly string[] audioExtensions = { "mp3", "ogg", "wav" };
|
||||
|
||||
public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Audio, "Too short audio files");
|
||||
|
||||
public IEnumerable<IssueTemplate> PossibleTemplates => new IssueTemplate[]
|
||||
{
|
||||
new IssueTemplateTooShort(this),
|
||||
new IssueTemplateBadFormat(this)
|
||||
};
|
||||
|
||||
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
|
||||
{
|
||||
var beatmapSet = context.Beatmap.BeatmapInfo.BeatmapSet;
|
||||
|
||||
foreach (var file in beatmapSet.Files)
|
||||
{
|
||||
Stream data = context.WorkingBeatmap.GetStream(file.FileInfo.StoragePath);
|
||||
if (data == null)
|
||||
continue;
|
||||
|
||||
var fileCallbacks = new FileCallbacks(new DataStreamFileProcedures(data));
|
||||
int decodeStream = Bass.CreateStream(StreamSystem.NoBuffer, BassFlags.Decode, fileCallbacks.Callbacks, fileCallbacks.Handle);
|
||||
|
||||
if (decodeStream == 0)
|
||||
{
|
||||
// If the file is not likely to be properly parsed by Bass, we don't produce Error issues about it.
|
||||
// Image files and audio files devoid of audio data both fail, for example, but neither would be issues in this check.
|
||||
if (hasAudioExtension(file.Filename) && probablyHasAudioData(data))
|
||||
yield return new IssueTemplateBadFormat(this).Create(file.Filename);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
long length = Bass.ChannelGetLength(decodeStream);
|
||||
double ms = Bass.ChannelBytes2Seconds(decodeStream, length) * 1000;
|
||||
|
||||
// Extremely short audio files do not play on some soundcards, resulting in nothing being heard in-game for some users.
|
||||
if (ms > 0 && ms < ms_threshold)
|
||||
yield return new IssueTemplateTooShort(this).Create(file.Filename, ms);
|
||||
}
|
||||
}
|
||||
|
||||
private bool hasAudioExtension(string filename) => audioExtensions.Any(filename.ToLower().EndsWith);
|
||||
private bool probablyHasAudioData(Stream data) => data.Length > min_bytes_threshold;
|
||||
|
||||
public class IssueTemplateTooShort : IssueTemplate
|
||||
{
|
||||
public IssueTemplateTooShort(ICheck check)
|
||||
: base(check, IssueType.Problem, "\"{0}\" is too short ({1:0f} ms), should be at least {2:0f} ms.")
|
||||
{
|
||||
}
|
||||
|
||||
public Issue Create(string filename, double ms) => new Issue(this, filename, ms, ms_threshold);
|
||||
}
|
||||
|
||||
public class IssueTemplateBadFormat : IssueTemplate
|
||||
{
|
||||
public IssueTemplateBadFormat(ICheck check)
|
||||
: base(check, IssueType.Error, "Could not check whether \"{0}\" is too short (code \"{1}\").")
|
||||
{
|
||||
}
|
||||
|
||||
public Issue Create(string filename) => new Issue(this, filename, Bass.LastError);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user