private void ToHold(RawHitObject hitObject, string others) { var index = others.SpanIndexOf(":"); var holdEnd = others.Substring(0, index); var extra = others.Substring(index + 1); hitObject.HoldEnd = int.Parse(holdEnd); hitObject.Extras = extra; }
private void ToSpinner(RawHitObject hitObject, string others) { var infos = others.SpanSplit(","); var holdEnd = infos[0]; hitObject.HoldEnd = int.Parse(holdEnd); if (infos.Length > 1) { var extra = infos[1]; hitObject.Extras = extra; } }
public void Match(string line) { if (HitObjectList == null) { HitObjectList = new List <RawHitObject>(); } string[] param = line.Split(','); var x = int.Parse(param[0]); var y = int.Parse(param[1]); var offset = int.Parse(param[2]); var type = (RawObjectType)Enum.Parse(typeof(RawObjectType), param[3]); var hitsound = (HitsoundType)Enum.Parse(typeof(HitsoundType), param[4]); var notImplementedInfo = string.Join(",", param.Skip(5)); var hitObject = new RawHitObject { X = x, Y = y, Offset = offset, RawType = type, Hitsound = hitsound }; if ((type & RawObjectType.Circle) == RawObjectType.Circle) { ConvertToCircle(hitObject, notImplementedInfo); } else if ((type & RawObjectType.Slider) == RawObjectType.Slider) { ConvertToSlider(hitObject, notImplementedInfo); } else if ((type & RawObjectType.Spinner) == RawObjectType.Spinner) { ConvertToSpinner(hitObject, notImplementedInfo); } else if ((type & RawObjectType.Hold) == RawObjectType.Hold) { ConvertToHold(hitObject, notImplementedInfo); } HitObjectList.Add(hitObject); }
public override void Match(string line) { string[] param = line.SpanSplit(","); var x = int.Parse(param[0]); var y = int.Parse(param[1]); var offset = int.Parse(param[2]); var type = (RawObjectType)Enum.Parse(typeof(RawObjectType), param[3]); var hitsound = (HitsoundType)Enum.Parse(typeof(HitsoundType), param[4]); var others = string.Join(",", param.Skip(5)); var hitObject = new RawHitObject { X = x, Y = y, Offset = offset, RawType = type, Hitsound = hitsound }; switch (hitObject.ObjectType) { case HitObjectType.Circle: ToCircle(hitObject, others); break; case HitObjectType.Slider: ToSlider(hitObject, others); break; case HitObjectType.Spinner: ToSpinner(hitObject, others); break; case HitObjectType.Hold: ToHold(hitObject, others); break; default: throw new ArgumentOutOfRangeException(); } HitObjectList.Add(hitObject); }
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; }
private static float GetObjectVolume(RawHitObject obj, TimingPoint timingPoint) { return((obj.SampleVolume != 0 ? obj.SampleVolume : timingPoint.Volume) / 100f); }
private IEnumerable <(string, HitsoundType)> AnalyzeHitsoundFiles( HitsoundType itemHitsound, ObjectSamplesetType itemSample, ObjectSamplesetType itemAddition, TimingPoint timingPoint, RawHitObject hitObject, HashSet <string> waves) { if (!string.IsNullOrEmpty(hitObject.FileName)) { return(new[] { ValueTuple.Create( _cache.GetFileUntilFind(_sourceFolder, Path.GetFileNameWithoutExtension(hitObject.FileName)), itemHitsound ) }); } var tuples = new List <(string, HitsoundType)>(); // hitnormal, sliderslide var sampleStr = itemSample != ObjectSamplesetType.Auto ? itemSample.ToHitsoundString(null) : timingPoint.TimingSampleset.ToHitsoundString(); // hitclap, hitfinish, hitwhistle, slidertick, sliderwhistle string additionStr = itemAddition.ToHitsoundString(sampleStr); if (hitObject.ObjectType == HitObjectType.Slider && hitObject.SliderInfo.EdgeHitsounds == null) { var hitsounds = GetHitsounds(itemHitsound, sampleStr, additionStr); tuples.AddRange(hitsounds); } else { var hitsounds = GetHitsounds(itemHitsound, sampleStr, additionStr); tuples.AddRange(_osuFile.General.Mode == GameMode.Mania ? hitsounds.Take(1) : hitsounds); } for (var i = 0; i < tuples.Count; i++) { var fileNameWithoutIndex = tuples[i].Item1; var hitsoundType = tuples[i].Item2; int baseIndex = hitObject.CustomIndex > 0 ? hitObject.CustomIndex : timingPoint.Track; string indexStr = baseIndex > 1 ? baseIndex.ToString() : ""; var fileNameWithoutExt = fileNameWithoutIndex + indexStr; string filePath; if (timingPoint.Track == 0) { filePath = Path.Combine(Domain.DefaultPath, fileNameWithoutExt + Information.WavExtension); } else if (waves.Contains(fileNameWithoutExt)) { filePath = _cache.GetFileUntilFind(_sourceFolder, fileNameWithoutExt); } else { filePath = Path.Combine(Domain.DefaultPath, fileNameWithoutIndex + Information.WavExtension); } tuples[i] = (filePath, hitsoundType); } return(tuples); }
private void ConvertToSlider(RawHitObject hitObject, string notImplementedInfo) { string extra = notImplementedInfo.Split(',').Last(); bool isSupportExtra = extra.IndexOf(":", StringComparison.Ordinal) != -1; var infos = notImplementedInfo.Split(','); var sliderType = infos[0].Split('|')[0]; var curvePoints = infos[0].Split('|').Skip(1).ToArray(); Point[] points = new Point[curvePoints.Length]; for (var i = 0; i < curvePoints.Length; i++) { var point = curvePoints[i]; var xy = point.Split(':').Select(int.Parse).ToArray(); points[i] = new Point(xy[0], xy[1]); } int repeat = int.Parse(infos[1]); decimal pixelLength = decimal.Parse(infos[2]); HitsoundType[] edgeHitsounds; SampleAdditonEnum[] edgeSamples; SampleAdditonEnum[] edgeAdditions; if (infos.Length == 3) { edgeHitsounds = null; edgeSamples = null; edgeAdditions = null; } else if (infos.Length == 4) { edgeHitsounds = infos[3].Split('|').Select(t => t.ParseToEnum <HitsoundType>()).ToArray(); edgeSamples = null; edgeAdditions = null; } else { edgeHitsounds = infos[3].Split('|').Select(t => t.ParseToEnum <HitsoundType>()).ToArray(); string[] edgeAdditionsStr = infos[4].Split('|'); edgeSamples = new SampleAdditonEnum[repeat + 1]; edgeAdditions = new SampleAdditonEnum[repeat + 1]; for (int i = 0; i < edgeAdditionsStr.Length; i++) { var sampAdd = edgeAdditionsStr[i].Split(':'); edgeSamples[i] = sampAdd[0].ParseToEnum <SampleAdditonEnum>(); edgeAdditions[i] = sampAdd[1].ParseToEnum <SampleAdditonEnum>(); } } RawTimingPoint[] lastRedLinesIfExsist = _timingPoints.TimingList.Where(t => !t.Inherit) .Where(t => t.Offset <= hitObject.Offset).ToArray(); RawTimingPoint lastRedLine; // hitobjects before lines is allowed if (lastRedLinesIfExsist.Length == 0) { lastRedLine = _timingPoints.TimingList.First(t => !t.Inherit); } else { double lastRedLineOffset = lastRedLinesIfExsist.Max(t => t.Offset); lastRedLine = _timingPoints.TimingList.First(t => t.Offset == lastRedLineOffset && !t.Inherit); } RawTimingPoint[] lastLinesIfExist = _timingPoints.TimingList.Where(t => t.Offset <= hitObject.Offset).ToArray(); RawTimingPoint[] lastLines; // 1 red + 1 green is allowed RawTimingPoint lastLine; // hitobjects before lines is allowed if (lastLinesIfExist.Length == 0) { lastLines = new[] { _timingPoints.TimingList.First(t => !t.Inherit) } } ; else { double lastLineOffset = lastLinesIfExist.Max(t => t.Offset); // 1 red + 1 green is allowed, so maybe here are two results lastLines = _timingPoints.TimingList.Where(t => t.Offset == lastLineOffset).ToArray(); } if (lastLines.Length > 1) { if (lastLines.Length == 2) { if (lastLines[0].Inherit != lastLines[1].Inherit) { lastLine = lastLines.First(t => t.Inherit); } else { throw new MultiTimingSectionException("存在同一时刻两条相同类型的Timing Section。"); } } else { throw new MultiTimingSectionException("存在同一时刻多条Timing Section。"); } } else { lastLine = lastLines[0]; } hitObject.SliderInfo = new SliderInfo(hitObject.Offset, lastRedLine.Factor, _difficulty.SliderMultiplier * lastLine.Multiple) { CurvePoints = points, EdgeAdditions = edgeAdditions, EdgeHitsounds = edgeHitsounds, EdgeSamples = edgeSamples, PixelLength = pixelLength, Repeat = repeat, SliderType = sliderType.ParseToEnum <SliderTypeEnum>() }; hitObject.Extras = isSupportExtra ? extra : null; }
private void ConvertToCircle(RawHitObject hitObject, string notImplementedInfo) { bool isSupportExtra = notImplementedInfo.IndexOf(":", StringComparison.Ordinal) != -1; hitObject.Extras = isSupportExtra ? notImplementedInfo : null; }
private void ConvertToHold(RawHitObject hitObject, string notImplementedInfo) { //throw new NotImplementedException(); }
private void ToSlider(RawHitObject hitObject, string others) { var infos = others.SpanSplit(","); // extra string notSureExtra = infos[infos.Length - 1]; bool supportExtra = notSureExtra.IndexOf(":", StringComparison.Ordinal) != -1; hitObject.Extras = supportExtra ? notSureExtra : null; // slider curve var curveInfo = infos[0].SpanSplit("|"); var sliderType = infos[0].Split('|')[0]; var points = new Vector2 <float> [curveInfo.Length - 1]; // curvePoints skip 1 for (var i = 1; i < curveInfo.Length; i++) { var point = curveInfo[i]; var xy = point.SpanSplit(":"); points[i - 1] = new Vector2 <float>(int.Parse(xy[0]), int.Parse(xy[1])); } // repeat int repeat = int.Parse(infos[1]); // length decimal pixelLength = decimal.Parse(infos[2]); // edge hitsounds HitsoundType[] edgeHitsounds; ObjectSamplesetType[] edgeSamples; ObjectSamplesetType[] edgeAdditions; if (infos.Length == 3) { edgeHitsounds = null; edgeSamples = null; edgeAdditions = null; } else if (infos.Length == 4) { edgeHitsounds = infos[3].SpanSplit("|").Select(t => t.ParseToEnum <HitsoundType>()).ToArray(); edgeSamples = null; edgeAdditions = null; } else { edgeHitsounds = infos[3].SpanSplit("|").Select(t => t.ParseToEnum <HitsoundType>()).ToArray(); string[] edgeAdditionsStrArr = infos[4].SpanSplit("|"); edgeSamples = new ObjectSamplesetType[repeat + 1]; edgeAdditions = new ObjectSamplesetType[repeat + 1]; for (int i = 0; i < edgeAdditionsStrArr.Length; i++) { var sampAdd = edgeAdditionsStrArr[i].SpanSplit(":"); edgeSamples[i] = sampAdd[0].ParseToEnum <ObjectSamplesetType>(); edgeAdditions[i] = sampAdd[1].ParseToEnum <ObjectSamplesetType>(); } } TimingPoint[] lastRedLinesIfExsist = _timingPoints.TimingList.Where(t => !t.Inherit) .Where(t => t.Offset <= hitObject.Offset).ToArray(); TimingPoint lastRedLine; // hitobjects before lines is allowed if (lastRedLinesIfExsist.Length == 0) { lastRedLine = _timingPoints.TimingList.First(t => !t.Inherit); } else { double lastRedLineOffset = lastRedLinesIfExsist.Max(t => t.Offset); //duplicate red lines, select the last one lastRedLine = _timingPoints.TimingList.Last(t => t.Offset == lastRedLineOffset && !t.Inherit); } TimingPoint[] lastLinesIfExist = _timingPoints.TimingList.Where(t => t.Offset <= hitObject.Offset).ToArray(); TimingPoint[] lastLines; // 1 red + 1 green is allowed TimingPoint lastLine; // hitobjects before lines is allowed if (lastLinesIfExist.Length == 0) { lastLines = new[] { _timingPoints.TimingList.First(t => !t.Inherit) } } ; //red line multiple default 1.0 else { double lastLineOffset = lastLinesIfExist.Max(t => t.Offset); // 1 red + 1 green is allowed, so maybe here are two results lastLines = _timingPoints.TimingList.Where(t => t.Offset == lastLineOffset).ToArray(); } if (lastLines.Length > 1) { lastLine = lastLines.LastOrDefault(k => k.Inherit) ?? lastLines.Last(k => !k.Inherit); //if (lastLines.Length == 2) //{ // if (lastLines[0].Inherit != lastLines[1].Inherit) // { // lastLine = lastLines.First(t => t.Inherit); // } // else // throw new RepeatTimingSectionException("存在同一时刻两条相同类型的Timing Section。"); //} //else // throw new RepeatTimingSectionException("存在同一时刻多条Timing Section。"); } else { lastLine = lastLines[0]; } hitObject.SliderInfo = new SliderInfo(new Vector2 <float>(hitObject.X, hitObject.Y), hitObject.Offset, lastRedLine.Factor, _difficulty.SliderMultiplier * lastLine.Multiple, _difficulty.SliderTickRate, pixelLength) { CurvePoints = points, EdgeAdditions = edgeAdditions, EdgeHitsounds = edgeHitsounds, EdgeSamples = edgeSamples, Repeat = repeat, SliderType = sliderType.ParseToEnum <SliderType>() }; }
private void ToCircle(RawHitObject hitObject, string others) { // extra hitObject.Extras = others; }