예제 #1
0
        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;
        }
예제 #2
0
        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;
            }
        }
예제 #3
0
        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);
        }
예제 #4
0
        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);
        }
예제 #5
0
        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;
        }
예제 #6
0
 private static float GetObjectVolume(RawHitObject obj, TimingPoint timingPoint)
 {
     return((obj.SampleVolume != 0 ? obj.SampleVolume : timingPoint.Volume) / 100f);
 }
예제 #7
0
        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);
        }
예제 #8
0
        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;
        }
예제 #9
0
        private void ConvertToCircle(RawHitObject hitObject, string notImplementedInfo)
        {
            bool isSupportExtra = notImplementedInfo.IndexOf(":", StringComparison.Ordinal) != -1;

            hitObject.Extras = isSupportExtra ? notImplementedInfo : null;
        }
예제 #10
0
 private void ConvertToHold(RawHitObject hitObject, string notImplementedInfo)
 {
     //throw new NotImplementedException();
 }
예제 #11
0
        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>()
            };
        }
예제 #12
0
 private void ToCircle(RawHitObject hitObject, string others)
 {
     // extra
     hitObject.Extras = others;
 }