mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 10:52:53 +08:00
Merge branch 'master' into no-more-difficulty-control-points-info
This commit is contained in:
commit
aa380a11c1
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@ -1 +1,2 @@
|
||||
github: ppy
|
||||
custom: https://osu.ppy.sh/home/support
|
||||
|
@ -52,7 +52,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1004.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1012.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1013.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
|
@ -45,8 +45,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
{
|
||||
new Spinner
|
||||
{
|
||||
Duration = 2000,
|
||||
Position = OsuPlayfield.BASE_SIZE / 2
|
||||
Duration = 6000,
|
||||
Position = OsuPlayfield.BASE_SIZE / 2,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
104
osu.Game.Tests/Editing/Checks/CheckAudioInVideoTest.cs
Normal file
104
osu.Game.Tests/Editing/Checks/CheckAudioInVideoTest.cs
Normal file
@ -0,0 +1,104 @@
|
||||
// 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 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.Storyboards;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Tests.Resources;
|
||||
using FileInfo = osu.Game.IO.FileInfo;
|
||||
|
||||
namespace osu.Game.Tests.Editing.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class CheckAudioInVideoTest
|
||||
{
|
||||
private CheckAudioInVideo check;
|
||||
private IBeatmap beatmap;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
check = new CheckAudioInVideo();
|
||||
beatmap = new Beatmap<HitObject>
|
||||
{
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
BeatmapSet = new BeatmapSetInfo
|
||||
{
|
||||
Files = new List<BeatmapSetFileInfo>(new[]
|
||||
{
|
||||
new BeatmapSetFileInfo
|
||||
{
|
||||
Filename = "abc123.mp4",
|
||||
FileInfo = new FileInfo { Hash = "abcdef" }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRegularVideoFile()
|
||||
{
|
||||
using (var resourceStream = TestResources.OpenResource("Videos/test-video.mp4"))
|
||||
Assert.IsEmpty(check.Run(getContext(resourceStream)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestVideoFileWithAudio()
|
||||
{
|
||||
using (var resourceStream = TestResources.OpenResource("Videos/test-video-with-audio.mp4"))
|
||||
{
|
||||
var issues = check.Run(getContext(resourceStream)).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckAudioInVideo.IssueTemplateHasAudioTrack);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestVideoFileWithTrackButNoAudio()
|
||||
{
|
||||
using (var resourceStream = TestResources.OpenResource("Videos/test-video-with-track-but-no-audio.mp4"))
|
||||
{
|
||||
var issues = check.Run(getContext(resourceStream)).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckAudioInVideo.IssueTemplateHasAudioTrack);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMissingFile()
|
||||
{
|
||||
beatmap.BeatmapInfo.BeatmapSet.Files.Clear();
|
||||
|
||||
var issues = check.Run(getContext(null)).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckAudioInVideo.IssueTemplateMissingFile);
|
||||
}
|
||||
|
||||
private BeatmapVerifierContext getContext(Stream resourceStream)
|
||||
{
|
||||
var storyboard = new Storyboard();
|
||||
var layer = storyboard.GetLayer("Video");
|
||||
layer.Add(new StoryboardVideo("abc123.mp4", 0));
|
||||
|
||||
var mockWorkingBeatmap = new Mock<TestWorkingBeatmap>(beatmap, null, null);
|
||||
mockWorkingBeatmap.Setup(w => w.GetStream(It.IsAny<string>())).Returns(resourceStream);
|
||||
mockWorkingBeatmap.As<IWorkingBeatmap>().SetupGet(w => w.Storyboard).Returns(storyboard);
|
||||
|
||||
return new BeatmapVerifierContext(beatmap, mockWorkingBeatmap.Object);
|
||||
}
|
||||
}
|
||||
}
|
128
osu.Game.Tests/Editing/Checks/CheckTooShortAudioFilesTest.cs
Normal file
128
osu.Game.Tests/Editing/Checks/CheckTooShortAudioFilesTest.cs
Normal file
@ -0,0 +1,128 @@
|
||||
// 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()
|
||||
{
|
||||
using (var resourceStream = TestResources.OpenResource("Samples/test-sample.mp3"))
|
||||
{
|
||||
Assert.IsEmpty(check.Run(getContext(resourceStream)));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBlankAudioFile()
|
||||
{
|
||||
using (var resourceStream = TestResources.OpenResource("Samples/blank.wav"))
|
||||
{
|
||||
// This is a 0 ms duration audio file, commonly used to silence sliderslides/ticks, and so should be fine.
|
||||
Assert.IsEmpty(check.Run(getContext(resourceStream)));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTooShortAudioFile()
|
||||
{
|
||||
using (var resourceStream = TestResources.OpenResource("Samples/test-sample-cut.mp3"))
|
||||
{
|
||||
var issues = check.Run(getContext(resourceStream)).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckTooShortAudioFiles.IssueTemplateTooShort);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMissingAudioFile()
|
||||
{
|
||||
using (var resourceStream = TestResources.OpenResource("Samples/missing.mp3"))
|
||||
{
|
||||
Assert.IsEmpty(check.Run(getContext(resourceStream, allowMissing: true)));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCorruptAudioFile()
|
||||
{
|
||||
using (var resourceStream = TestResources.OpenResource("Samples/corrupt.wav"))
|
||||
{
|
||||
var issues = check.Run(getContext(resourceStream)).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckTooShortAudioFiles.IssueTemplateBadFormat);
|
||||
}
|
||||
}
|
||||
|
||||
private BeatmapVerifierContext getContext(Stream resourceStream, bool allowMissing = false)
|
||||
{
|
||||
var mockWorkingBeatmap = new Mock<TestWorkingBeatmap>(beatmap, null, null);
|
||||
mockWorkingBeatmap.Setup(w => w.GetStream(It.IsAny<string>())).Returns(resourceStream);
|
||||
|
||||
return new BeatmapVerifierContext(beatmap, mockWorkingBeatmap.Object);
|
||||
}
|
||||
}
|
||||
}
|
86
osu.Game.Tests/Editing/Checks/CheckZeroByteFilesTest.cs
Normal file
86
osu.Game.Tests/Editing/Checks/CheckZeroByteFilesTest.cs
Normal file
@ -0,0 +1,86 @@
|
||||
// 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 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 FileInfo = osu.Game.IO.FileInfo;
|
||||
|
||||
namespace osu.Game.Tests.Editing.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class CheckZeroByteFilesTest
|
||||
{
|
||||
private CheckZeroByteFiles check;
|
||||
private IBeatmap beatmap;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
check = new CheckZeroByteFiles();
|
||||
beatmap = new Beatmap<HitObject>
|
||||
{
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
BeatmapSet = new BeatmapSetInfo
|
||||
{
|
||||
Files = new List<BeatmapSetFileInfo>(new[]
|
||||
{
|
||||
new BeatmapSetFileInfo
|
||||
{
|
||||
Filename = "abc123.jpg",
|
||||
FileInfo = new FileInfo { Hash = "abcdef" }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNonZeroBytes()
|
||||
{
|
||||
Assert.IsEmpty(check.Run(getContext(byteLength: 44)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestZeroBytes()
|
||||
{
|
||||
var issues = check.Run(getContext(byteLength: 0)).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckZeroByteFiles.IssueTemplateZeroBytes);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMissing()
|
||||
{
|
||||
Assert.IsEmpty(check.Run(getContextMissing()));
|
||||
}
|
||||
|
||||
private BeatmapVerifierContext getContext(long byteLength)
|
||||
{
|
||||
var mockStream = new Mock<Stream>();
|
||||
mockStream.Setup(s => s.Length).Returns(byteLength);
|
||||
|
||||
var mockWorkingBeatmap = new Mock<IWorkingBeatmap>();
|
||||
mockWorkingBeatmap.Setup(w => w.GetStream(It.IsAny<string>())).Returns(mockStream.Object);
|
||||
|
||||
return new BeatmapVerifierContext(beatmap, mockWorkingBeatmap.Object);
|
||||
}
|
||||
|
||||
private BeatmapVerifierContext getContextMissing()
|
||||
{
|
||||
var mockWorkingBeatmap = new Mock<IWorkingBeatmap>();
|
||||
mockWorkingBeatmap.Setup(w => w.GetStream(It.IsAny<string>())).Returns((Stream)null);
|
||||
|
||||
return new BeatmapVerifierContext(beatmap, mockWorkingBeatmap.Object);
|
||||
}
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ namespace osu.Game.Tests
|
||||
protected virtual TestOsuGameBase LoadOsuIntoHost(GameHost host, bool withBeatmap = false)
|
||||
{
|
||||
var osu = new TestOsuGameBase(withBeatmap);
|
||||
Task.Run(() => host.Run(osu))
|
||||
Task.Factory.StartNew(() => host.Run(osu), TaskCreationOptions.LongRunning)
|
||||
.ContinueWith(t => Assert.Fail($"Host threw exception {t.Exception}"), TaskContinuationOptions.OnlyOnFaulted);
|
||||
|
||||
waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time");
|
||||
|
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.
BIN
osu.Game.Tests/Resources/Videos/test-video-with-audio.mp4
Normal file
BIN
osu.Game.Tests/Resources/Videos/test-video-with-audio.mp4
Normal file
Binary file not shown.
Binary file not shown.
BIN
osu.Game.Tests/Resources/Videos/test-video.mp4
Normal file
BIN
osu.Game.Tests/Resources/Videos/test-video.mp4
Normal file
Binary file not shown.
@ -14,7 +14,7 @@ namespace osu.Game.Tournament.Tests.NonVisual
|
||||
public static TournamentGameBase LoadTournament(GameHost host, TournamentGameBase tournament = null)
|
||||
{
|
||||
tournament ??= new TournamentGameBase();
|
||||
Task.Run(() => host.Run(tournament))
|
||||
Task.Factory.StartNew(() => host.Run(tournament), TaskCreationOptions.LongRunning)
|
||||
.ContinueWith(t => Assert.Fail($"Host threw exception {t.Exception}"), TaskContinuationOptions.OnlyOnFaulted);
|
||||
WaitForOrAssert(() => tournament.IsLoaded, @"osu! failed to start in a reasonable amount of time");
|
||||
return tournament;
|
||||
|
30
osu.Game/IO/FileAbstraction/StreamFileAbstraction.cs
Normal file
30
osu.Game/IO/FileAbstraction/StreamFileAbstraction.cs
Normal file
@ -0,0 +1,30 @@
|
||||
// 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;
|
||||
using System.IO;
|
||||
|
||||
namespace osu.Game.IO.FileAbstraction
|
||||
{
|
||||
public class StreamFileAbstraction : TagLib.File.IFileAbstraction
|
||||
{
|
||||
public StreamFileAbstraction(string filename, Stream fileStream)
|
||||
{
|
||||
ReadStream = fileStream;
|
||||
Name = filename;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public Stream ReadStream { get; }
|
||||
public Stream WriteStream => ReadStream;
|
||||
|
||||
public void CloseStream(Stream stream)
|
||||
{
|
||||
if (stream == null)
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
|
||||
stream.Close();
|
||||
}
|
||||
}
|
||||
}
|
@ -130,12 +130,6 @@ namespace osu.Game.Online.Rooms
|
||||
set => MaxAttempts.Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The position of this <see cref="Room"/> in the list. This is not read from or written to the API.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public readonly Bindable<long> Position = new Bindable<long>(-1); // Todo: This does not need to exist.
|
||||
|
||||
public Room()
|
||||
{
|
||||
Password.BindValueChanged(p => HasPassword.Value = !string.IsNullOrEmpty(p.NewValue));
|
||||
@ -192,8 +186,6 @@ namespace osu.Game.Online.Rooms
|
||||
RecentParticipants.Clear();
|
||||
RecentParticipants.AddRange(other.RecentParticipants);
|
||||
}
|
||||
|
||||
Position.Value = other.Position.Value;
|
||||
}
|
||||
|
||||
public void RemoveExpiredPlaylistItems()
|
||||
|
@ -24,6 +24,11 @@ namespace osu.Game.Rulesets.Edit
|
||||
new CheckAudioQuality(),
|
||||
new CheckMutedObjects(),
|
||||
new CheckFewHitsounds(),
|
||||
new CheckTooShortAudioFiles(),
|
||||
new CheckAudioInVideo(),
|
||||
|
||||
// Files
|
||||
new CheckZeroByteFiles(),
|
||||
|
||||
// Compose
|
||||
new CheckUnsnappedObjects(),
|
||||
|
112
osu.Game/Rulesets/Edit/Checks/CheckAudioInVideo.cs
Normal file
112
osu.Game/Rulesets/Edit/Checks/CheckAudioInVideo.cs
Normal file
@ -0,0 +1,112 @@
|
||||
// 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 osu.Game.IO.FileAbstraction;
|
||||
using osu.Game.Rulesets.Edit.Checks.Components;
|
||||
using osu.Game.Storyboards;
|
||||
using TagLib;
|
||||
using File = TagLib.File;
|
||||
|
||||
namespace osu.Game.Rulesets.Edit.Checks
|
||||
{
|
||||
public class CheckAudioInVideo : ICheck
|
||||
{
|
||||
public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Audio, "Audio track in video files");
|
||||
|
||||
public IEnumerable<IssueTemplate> PossibleTemplates => new IssueTemplate[]
|
||||
{
|
||||
new IssueTemplateHasAudioTrack(this),
|
||||
new IssueTemplateMissingFile(this),
|
||||
new IssueTemplateFileError(this)
|
||||
};
|
||||
|
||||
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
|
||||
{
|
||||
var beatmapSet = context.Beatmap.BeatmapInfo.BeatmapSet;
|
||||
var videoPaths = new List<string>();
|
||||
|
||||
foreach (var layer in context.WorkingBeatmap.Storyboard.Layers)
|
||||
{
|
||||
foreach (var element in layer.Elements)
|
||||
{
|
||||
if (!(element is StoryboardVideo video))
|
||||
continue;
|
||||
|
||||
// Ensures we don't check the same video file multiple times in case of multiple elements using it.
|
||||
if (!videoPaths.Contains(video.Path))
|
||||
videoPaths.Add(video.Path);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var filename in videoPaths)
|
||||
{
|
||||
string storagePath = beatmapSet.GetPathForFile(filename);
|
||||
|
||||
if (storagePath == null)
|
||||
{
|
||||
// There's an element in the storyboard that requires this resource, so it being missing is worth warning about.
|
||||
yield return new IssueTemplateMissingFile(this).Create(filename);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Issue issue;
|
||||
|
||||
try
|
||||
{
|
||||
// We use TagLib here for platform invariance; BASS cannot detect audio presence on Linux.
|
||||
using (Stream data = context.WorkingBeatmap.GetStream(storagePath))
|
||||
using (File tagFile = File.Create(new StreamFileAbstraction(filename, data)))
|
||||
{
|
||||
if (tagFile.Properties.AudioChannels == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
issue = new IssueTemplateHasAudioTrack(this).Create(filename);
|
||||
}
|
||||
catch (CorruptFileException)
|
||||
{
|
||||
issue = new IssueTemplateFileError(this).Create(filename, "Corrupt file");
|
||||
}
|
||||
catch (UnsupportedFormatException)
|
||||
{
|
||||
issue = new IssueTemplateFileError(this).Create(filename, "Unsupported format");
|
||||
}
|
||||
|
||||
yield return issue;
|
||||
}
|
||||
}
|
||||
|
||||
public class IssueTemplateHasAudioTrack : IssueTemplate
|
||||
{
|
||||
public IssueTemplateHasAudioTrack(ICheck check)
|
||||
: base(check, IssueType.Problem, "\"{0}\" has an audio track.")
|
||||
{
|
||||
}
|
||||
|
||||
public Issue Create(string filename) => new Issue(this, filename);
|
||||
}
|
||||
|
||||
public class IssueTemplateFileError : IssueTemplate
|
||||
{
|
||||
public IssueTemplateFileError(ICheck check)
|
||||
: base(check, IssueType.Error, "Could not check whether \"{0}\" has an audio track ({1}).")
|
||||
{
|
||||
}
|
||||
|
||||
public Issue Create(string filename, string errorReason) => new Issue(this, filename, errorReason);
|
||||
}
|
||||
|
||||
public class IssueTemplateMissingFile : IssueTemplate
|
||||
{
|
||||
public IssueTemplateMissingFile(ICheck check)
|
||||
: base(check, IssueType.Warning, "Could not check whether \"{0}\" has an audio track, because it is missing.")
|
||||
{
|
||||
}
|
||||
|
||||
public Issue Create(string filename) => new Issue(this, filename);
|
||||
}
|
||||
}
|
||||
}
|
85
osu.Game/Rulesets/Edit/Checks/CheckTooShortAudioFiles.cs
Normal file
85
osu.Game/Rulesets/Edit/Checks/CheckTooShortAudioFiles.cs
Normal file
@ -0,0 +1,85 @@
|
||||
// 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)
|
||||
{
|
||||
using (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 | BassFlags.Prescan, 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:0} ms), should be at least {2:0} 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);
|
||||
}
|
||||
}
|
||||
}
|
43
osu.Game/Rulesets/Edit/Checks/CheckZeroByteFiles.cs
Normal file
43
osu.Game/Rulesets/Edit/Checks/CheckZeroByteFiles.cs
Normal file
@ -0,0 +1,43 @@
|
||||
// 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 osu.Game.Rulesets.Edit.Checks.Components;
|
||||
|
||||
namespace osu.Game.Rulesets.Edit.Checks
|
||||
{
|
||||
public class CheckZeroByteFiles : ICheck
|
||||
{
|
||||
public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Files, "Zero-byte files");
|
||||
|
||||
public IEnumerable<IssueTemplate> PossibleTemplates => new IssueTemplate[]
|
||||
{
|
||||
new IssueTemplateZeroBytes(this)
|
||||
};
|
||||
|
||||
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
|
||||
{
|
||||
var beatmapSet = context.Beatmap.BeatmapInfo.BeatmapSet;
|
||||
|
||||
foreach (var file in beatmapSet.Files)
|
||||
{
|
||||
using (Stream data = context.WorkingBeatmap.GetStream(file.FileInfo.StoragePath))
|
||||
{
|
||||
if (data?.Length == 0)
|
||||
yield return new IssueTemplateZeroBytes(this).Create(file.Filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class IssueTemplateZeroBytes : IssueTemplate
|
||||
{
|
||||
public IssueTemplateZeroBytes(ICheck check)
|
||||
: base(check, IssueType.Problem, "\"{0}\" is a 0-byte file.")
|
||||
{
|
||||
}
|
||||
|
||||
public Issue Create(string filename) => new Issue(this, filename);
|
||||
}
|
||||
}
|
||||
}
|
@ -116,8 +116,6 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
if (ignoredRooms.Contains(room.RoomID.Value.Value))
|
||||
return;
|
||||
|
||||
room.Position.Value = -room.RoomID.Value.Value;
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var pi in room.Playlist)
|
||||
|
@ -129,7 +129,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
private void updateSorting()
|
||||
{
|
||||
foreach (var room in roomFlow)
|
||||
roomFlow.SetLayoutPosition(room, room.Room.Position.Value);
|
||||
roomFlow.SetLayoutPosition(room, -(room.Room.RoomID.Value ?? 0));
|
||||
}
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
|
@ -36,11 +36,12 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="10.6.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.1012.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.1013.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1004.0" />
|
||||
<PackageReference Include="Sentry" Version="3.9.4" />
|
||||
<PackageReference Include="SharpCompress" Version="0.29.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||
<PackageReference Include="TagLibSharp" Version="2.2.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -70,7 +70,7 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.1012.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.1013.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1004.0" />
|
||||
</ItemGroup>
|
||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||
@ -93,7 +93,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.1012.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.1013.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.28.3" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user