mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 13:22:55 +08:00
Merge branch 'master' into JSON-format-fix
This commit is contained in:
commit
56196885e6
@ -230,5 +230,23 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
|
||||
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(new SampleInfo { Name = "hitnormal" });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeCustomHitObjectSamples()
|
||||
{
|
||||
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
|
||||
using (var resStream = Resource.OpenResource("custom-hitobject-samples.osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var hitObjects = decoder.Decode(stream).HitObjects;
|
||||
|
||||
Assert.AreEqual("hit_1.wav", hitObjects[0].Samples[0].LookupNames.First());
|
||||
Assert.AreEqual("hit_2.wav", hitObjects[1].Samples[0].LookupNames.First());
|
||||
Assert.AreEqual("hitnormal2", getTestableSampleInfo(hitObjects[2]).Name);
|
||||
Assert.AreEqual("hit_1.wav", hitObjects[3].Samples[0].LookupNames.First());
|
||||
}
|
||||
|
||||
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(new SampleInfo { Name = "hitnormal" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
16
osu.Game.Tests/Resources/custom-hitobject-samples.osu
Normal file
16
osu.Game.Tests/Resources/custom-hitobject-samples.osu
Normal file
@ -0,0 +1,16 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
SampleSet: Normal
|
||||
|
||||
[TimingPoints]
|
||||
2170,468.75,4,1,0,40,1,0
|
||||
2638,-100,4,1,1,40,0,0
|
||||
3107,-100,4,1,2,40,0,0
|
||||
3576,-100,4,1,0,40,0,0
|
||||
|
||||
[HitObjects]
|
||||
255,193,2170,1,0,0:0:0:0:hit_1.wav
|
||||
256,191,2638,5,0,0:0:0:0:hit_2.wav
|
||||
255,193,3107,1,0,0:0:0:0:
|
||||
256,191,3576,1,0,0:0:0:0:hit_1.wav
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Audio
|
||||
{
|
||||
@ -33,6 +34,20 @@ namespace osu.Game.Audio
|
||||
/// </summary>
|
||||
public int Volume;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve all possible filenames that can be used as a source, returned in order of preference (highest first).
|
||||
/// </summary>
|
||||
public virtual IEnumerable<string> LookupNames
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Namespace))
|
||||
yield return $"{Namespace}/{Bank}-{Name}";
|
||||
|
||||
yield return $"{Bank}-{Name}"; // Without namespace as a fallback even when we have a namespace
|
||||
}
|
||||
}
|
||||
|
||||
public SampleInfo Clone() => (SampleInfo)MemberwiseClone();
|
||||
}
|
||||
}
|
||||
|
@ -250,7 +250,8 @@ namespace osu.Game
|
||||
new VolumeControlReceptor
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ActionRequested = action => volume.Adjust(action)
|
||||
ActionRequested = action => volume.Adjust(action),
|
||||
ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise),
|
||||
},
|
||||
mainContent = new Container { RelativeSizeAxes = Axes.Both },
|
||||
overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue },
|
||||
|
@ -186,7 +186,7 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
{
|
||||
if (bindTarget.IsHovered)
|
||||
{
|
||||
bindTarget.UpdateKeyCombination(new KeyCombination(KeyCombination.FromInputState(state).Keys.Append(state.Mouse.ScrollDelta.Y > 0 ? InputKey.MouseWheelUp : InputKey.MouseWheelDown)));
|
||||
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state, state.Mouse.ScrollDelta));
|
||||
finalise();
|
||||
return true;
|
||||
}
|
||||
|
@ -9,11 +9,13 @@ using osu.Game.Input.Bindings;
|
||||
|
||||
namespace osu.Game.Overlays.Volume
|
||||
{
|
||||
public class VolumeControlReceptor : Container, IKeyBindingHandler<GlobalAction>, IHandleGlobalInput
|
||||
public class VolumeControlReceptor : Container, IScrollBindingHandler<GlobalAction>, IHandleGlobalInput
|
||||
{
|
||||
public Func<GlobalAction, bool> ActionRequested;
|
||||
public Func<GlobalAction, float, bool, bool> ScrollActionRequested;
|
||||
|
||||
public bool OnPressed(GlobalAction action) => ActionRequested?.Invoke(action) ?? false;
|
||||
public bool OnScroll(GlobalAction action, float amount, bool isPrecise) => ScrollActionRequested?.Invoke(action, amount, isPrecise) ?? false;
|
||||
public bool OnReleased(GlobalAction action) => false;
|
||||
}
|
||||
}
|
||||
|
@ -12,17 +12,15 @@ using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Input.Bindings;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Volume
|
||||
{
|
||||
public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
|
||||
public class VolumeMeter : Container
|
||||
{
|
||||
private CircularProgress volumeCircle;
|
||||
private CircularProgress volumeCircleGlow;
|
||||
@ -226,59 +224,27 @@ namespace osu.Game.Overlays.Volume
|
||||
|
||||
private const float adjust_step = 0.05f;
|
||||
|
||||
public void Increase() => adjust(1);
|
||||
public void Decrease() => adjust(-1);
|
||||
|
||||
private void adjust(int direction)
|
||||
{
|
||||
float amount = adjust_step * direction;
|
||||
|
||||
// handle the case where the OnPressed action was actually a mouse wheel.
|
||||
// this allows for precise wheel handling.
|
||||
var state = GetContainingInputManager().CurrentState;
|
||||
if (state.Mouse?.ScrollDelta.Y != 0)
|
||||
{
|
||||
OnScroll(state);
|
||||
return;
|
||||
}
|
||||
|
||||
Volume += amount;
|
||||
}
|
||||
|
||||
public bool OnPressed(GlobalAction action)
|
||||
{
|
||||
if (!IsHovered) return false;
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case GlobalAction.DecreaseVolume:
|
||||
Decrease();
|
||||
return true;
|
||||
case GlobalAction.IncreaseVolume:
|
||||
Increase();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
public void Increase(double amount = 1, bool isPrecise = false) => adjust(amount, isPrecise);
|
||||
public void Decrease(double amount = 1, bool isPrecise = false) => adjust(-amount, isPrecise);
|
||||
|
||||
// because volume precision is set to 0.01, this local is required to keep track of more precise adjustments and only apply when possible.
|
||||
private double scrollAmount;
|
||||
private double adjustAccumulator;
|
||||
|
||||
private void adjust(double delta, bool isPrecise)
|
||||
{
|
||||
adjustAccumulator += delta * adjust_step * (isPrecise ? 0.1 : 1);
|
||||
if (Math.Abs(adjustAccumulator) < Bindable.Precision)
|
||||
return;
|
||||
Volume += adjustAccumulator;
|
||||
adjustAccumulator = 0;
|
||||
}
|
||||
|
||||
protected override bool OnScroll(InputState state)
|
||||
{
|
||||
scrollAmount += adjust_step * state.Mouse.ScrollDelta.Y * (state.Mouse.HasPreciseScroll ? 0.1f : 1);
|
||||
|
||||
if (Math.Abs(scrollAmount) < Bindable.Precision)
|
||||
return true;
|
||||
|
||||
Volume += scrollAmount;
|
||||
scrollAmount = 0;
|
||||
adjust(state.Mouse.ScrollDelta.Y, state.Mouse.HasPreciseScroll);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool OnReleased(GlobalAction action) => false;
|
||||
|
||||
private const float transition_length = 500;
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
|
@ -93,7 +93,7 @@ namespace osu.Game.Overlays
|
||||
muteButton.Current.ValueChanged += _ => Show();
|
||||
}
|
||||
|
||||
public bool Adjust(GlobalAction action)
|
||||
public bool Adjust(GlobalAction action, float amount = 1, bool isPrecise = false)
|
||||
{
|
||||
if (!IsLoaded) return false;
|
||||
|
||||
@ -103,13 +103,13 @@ namespace osu.Game.Overlays
|
||||
if (State == Visibility.Hidden)
|
||||
Show();
|
||||
else
|
||||
volumeMeterMaster.Decrease();
|
||||
volumeMeterMaster.Decrease(amount, isPrecise);
|
||||
return true;
|
||||
case GlobalAction.IncreaseVolume:
|
||||
if (State == Visibility.Hidden)
|
||||
Show();
|
||||
else
|
||||
volumeMeterMaster.Increase();
|
||||
volumeMeterMaster.Increase(amount, isPrecise);
|
||||
return true;
|
||||
case GlobalAction.ToggleMute:
|
||||
Show();
|
||||
|
@ -6,6 +6,7 @@ using osu.Game.Rulesets.Objects.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Audio;
|
||||
using System.Linq;
|
||||
@ -196,9 +197,6 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
var bank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[0]);
|
||||
var addbank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[1]);
|
||||
|
||||
// Let's not implement this for now, because this doesn't fit nicely into the bank structure
|
||||
//string sampleFile = split2.Length > 4 ? split2[4] : string.Empty;
|
||||
|
||||
string stringBank = bank.ToString().ToLower();
|
||||
if (stringBank == @"none")
|
||||
stringBank = null;
|
||||
@ -211,6 +209,8 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
|
||||
if (split.Length > 3)
|
||||
bankInfo.Volume = int.Parse(split[3]);
|
||||
|
||||
bankInfo.Filename = split.Length > 4 ? split[4] : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -252,6 +252,10 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
|
||||
private List<SampleInfo> convertSoundType(LegacySoundType type, SampleBankInfo bankInfo)
|
||||
{
|
||||
// Todo: This should return the normal SampleInfos if the specified sample file isn't found, but that's a pretty edge-case scenario
|
||||
if (!string.IsNullOrEmpty(bankInfo.Filename))
|
||||
return new List<SampleInfo> { new FileSampleInfo { Filename = bankInfo.Filename } };
|
||||
|
||||
var soundTypes = new List<SampleInfo>
|
||||
{
|
||||
new SampleInfo
|
||||
@ -297,14 +301,24 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
|
||||
private class SampleBankInfo
|
||||
{
|
||||
public string Filename;
|
||||
|
||||
public string Normal;
|
||||
public string Add;
|
||||
public int Volume;
|
||||
|
||||
public SampleBankInfo Clone()
|
||||
public SampleBankInfo Clone() => (SampleBankInfo)MemberwiseClone();
|
||||
}
|
||||
|
||||
private class FileSampleInfo : SampleInfo
|
||||
{
|
||||
public string Filename;
|
||||
|
||||
public override IEnumerable<string> LookupNames => new[]
|
||||
{
|
||||
return (SampleBankInfo)MemberwiseClone();
|
||||
}
|
||||
Filename,
|
||||
Path.ChangeExtension(Filename, null)
|
||||
};
|
||||
}
|
||||
|
||||
[Flags]
|
||||
|
@ -44,19 +44,17 @@ namespace osu.Game.Skinning
|
||||
|
||||
private SampleChannel loadChannel(SampleInfo info, Func<string, SampleChannel> getSampleFunction)
|
||||
{
|
||||
SampleChannel ch = null;
|
||||
foreach (var lookup in info.LookupNames)
|
||||
{
|
||||
var ch = getSampleFunction($"Gameplay/{lookup}");
|
||||
if (ch == null)
|
||||
continue;
|
||||
|
||||
if (info.Namespace != null)
|
||||
ch = getSampleFunction($"Gameplay/{info.Namespace}/{info.Bank}-{info.Name}");
|
||||
|
||||
// try without namespace as a fallback.
|
||||
if (ch == null)
|
||||
ch = getSampleFunction($"Gameplay/{info.Bank}-{info.Name}");
|
||||
|
||||
if (ch != null)
|
||||
ch.Volume.Value = info.Volume / 100.0;
|
||||
return ch;
|
||||
}
|
||||
|
||||
return ch;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.1.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2018.629.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2018.705.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.17.1" />
|
||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user