public override async Task <IEnumerable <SoundElement> > GetSoundElements() { var elements = new ConcurrentBag <SoundElement>(); var samples = _osuFile.Events.SampleInfo; if (samples == null) { return(new List <SoundElement>(elements)); } await Task.Run(() => { samples.AsParallel() .WithDegreeOfParallelism(Environment.ProcessorCount > 1 ? Environment.ProcessorCount - 1 : 1) .ForAll(sample => { var element = SoundElement.Create(sample.Offset, sample.Volume / 100f, 0, _player.GetFileUntilFind(_sourceFolder, Path.GetFileNameWithoutExtension(sample.Filename)) ); elements.Add(element); }); }).ConfigureAwait(false); var elementList = new List <SoundElement>(elements); if (PlaybackRate.Equals(1.5f) && !UseTempo) { var duration = MathEx.Max(_player.MusicChannel.ChannelEndTime, _player.HitsoundChannel.ChannelEndTime, TimeSpan.FromMilliseconds(samples.Count == 0 ? 0 : samples.Max(k => k.Offset)) ); _nightcore = new NightcoreTilingProvider(_osuFile, duration); elementList.AddRange(await _nightcore.GetSoundElements().ConfigureAwait(false)); } return(elementList); }
private async Task AddSingleHitObject(RawHitObject obj, HashSet <string> waves, ConcurrentBag <SoundElement> elements) { if (obj.ObjectType != HitObjectType.Slider) { var itemOffset = obj.ObjectType == HitObjectType.Spinner ? obj.HoldEnd // spinner : obj.Offset; // hold & circle var timingPoint = _osuFile.TimingPoints.GetLine(itemOffset); float balance = GetObjectBalance(obj.X); float volume = GetObjectVolume(obj, timingPoint); var tuples = AnalyzeHitsoundFiles(obj.Hitsound, obj.SampleSet, obj.AdditionSet, timingPoint, obj, waves); foreach (var(filePath, _) in tuples) { var element = SoundElement.Create(itemOffset, volume, balance, filePath); elements.Add(element); } } else // sliders { // edges bool forceUseSlide = obj.SliderInfo.EdgeSamples == null; foreach (var item in obj.SliderInfo.Edges) { var itemOffset = item.Offset; var timingPoint = _osuFile.TimingPoints.GetLine(itemOffset); float balance = GetObjectBalance(item.Point.X); float volume = GetObjectVolume(obj, timingPoint); var hs = forceUseSlide ? obj.Hitsound : item.EdgeHitsound; var addition = forceUseSlide ? obj.AdditionSet : item.EdgeAddition; var sample = forceUseSlide ? obj.SampleSet : item.EdgeSample; var tuples = AnalyzeHitsoundFiles(hs, sample, addition, timingPoint, obj, waves); foreach (var(filePath, _) in tuples) { var element = SoundElement.Create(itemOffset, volume, balance, filePath); elements.Add(element); } } // ticks var ticks = obj.SliderInfo.Ticks; foreach (var sliderTick in ticks) { var itemOffset = sliderTick.Offset; var timingPoint = _osuFile.TimingPoints.GetLine(itemOffset); float balance = GetObjectBalance(sliderTick.Point.X); float volume = GetObjectVolume(obj, timingPoint) * 1.25f; // ticks x1.25 var(filePath, _) = AnalyzeHitsoundFiles(HitsoundType.Tick, obj.SampleSet, obj.AdditionSet, timingPoint, obj, waves).First(); var element = SoundElement.Create(itemOffset, volume, balance, filePath); elements.Add(element); } // sliding { var slideElements = new List <SoundElement>(); var startOffset = obj.Offset; var endOffset = obj.SliderInfo.Edges[obj.SliderInfo.Edges.Length - 1].Offset; var timingPoint = _osuFile.TimingPoints.GetLine(startOffset); float balance = GetObjectBalance(obj.X); float volume = GetObjectVolume(obj, timingPoint); // start sliding var tuples = AnalyzeHitsoundFiles( obj.Hitsound & HitsoundType.SlideWhistle | HitsoundType.Slide, obj.SampleSet, obj.AdditionSet, timingPoint, obj, waves); foreach (var(filePath, hitsoundType) in tuples) { int channel; if (hitsoundType.HasFlag(HitsoundType.Slide)) { channel = 0; } else if (hitsoundType.HasFlag(HitsoundType.SlideWhistle)) { channel = 1; } else { continue; } var element = SoundElement.CreateLoopSignal(startOffset, volume, balance, filePath, channel); slideElements.Add(element); } // change sample (will optimize if only adjust volume) by inherit timing point var timingsOnSlider = _osuFile.TimingPoints.TimingList .Where(k => k.Offset > startOffset && k.Offset < endOffset) .ToList(); for (var i = 0; i < timingsOnSlider.Count; i++) { var timing = timingsOnSlider[i]; var prevTiming = i == 0 ? timingPoint : timingsOnSlider[i - 1]; if (timing.Track != prevTiming.Track || timing.TimingSampleset != prevTiming.TimingSampleset) { volume = GetObjectVolume(obj, timing); tuples = AnalyzeHitsoundFiles( obj.Hitsound & HitsoundType.SlideWhistle | HitsoundType.Slide, obj.SampleSet, obj.AdditionSet, timing, obj, waves); foreach (var(filePath, hitsoundType) in tuples) { SoundElement element; if (hitsoundType.HasFlag(HitsoundType.Slide) && slideElements .Last(k => k.PlaybackType == PlaybackType.Loop) .FilePath == filePath) { // optimize by only change volume element = SoundElement.CreateLoopVolumeSignal(timing.Offset, volume); } else { int channel; if (hitsoundType.HasFlag(HitsoundType.Slide)) { channel = 0; } else if (hitsoundType.HasFlag(HitsoundType.SlideWhistle)) { channel = 1; } else { continue; } // new sample element = SoundElement.CreateLoopSignal(timing.Offset, volume, balance, filePath, channel); } slideElements.Add(element); } continue; } // optimize useless timing point timingsOnSlider.RemoveAt(i); i--; } // end slide var stopElement = SoundElement.CreateLoopStopSignal(endOffset, 0); var stopElement2 = SoundElement.CreateLoopStopSignal(endOffset, 1); slideElements.Add(stopElement); slideElements.Add(stopElement2); foreach (var slideElement in slideElements) { elements.Add(slideElement); } } // change balance while sliding (not supported in original game) var trails = obj.SliderInfo.BallTrail; var all = trails .Select(k => new { offset = k.Offset, balance = GetObjectBalance(k.Point.X) }) .Select(k => SoundElement.CreateLoopBalanceSignal(k.offset, k.balance)); foreach (var balanceElement in all) { elements.Add(balanceElement); } } await Task.CompletedTask; }
public override async Task <IEnumerable <SoundElement> > GetSoundElements() { return(new[] { SoundElement.Create(_delay, _control.Volume, _control.Balance, _audioPath) }); }