public HitObject(string[] anArgs, Beatmap aBeatmap) { beatmap = aBeatmap; code = String.Join(",", anArgs); Position = GetPosition(anArgs); time = GetTime(anArgs); type = GetTypeFlags(anArgs); hitSound = GetHitSound(anArgs); // extras Tuple <Beatmap.Sampleset, Beatmap.Sampleset, int?, int?, string> extras = GetExtras(anArgs); if (extras != null) { // custom index and volume are by default 0 if there are edge hitsounds or similar sampleset = extras.Item1; addition = extras.Item2; customIndex = extras.Item3 == 0 ? null : extras.Item3; volume = extras.Item4 == 0 ? null : extras.Item4; // hitsound filenames only apply to circles and hold notes string hitSoundFile = extras.Item5; if (hitSoundFile.Trim() != "" && (type.HasFlag(Type.Circle) || type.HasFlag(Type.ManiaHoldNote))) { filename = PathStatic.ParsePath(hitSoundFile); } } }
private Tuple <Beatmap.Sampleset, Beatmap.Sampleset, int?, int?, string> GetExtras(string[] args) { string extras = args.Last(); // Hold notes have "endTime:extras" as format. int index = HasType(Type.ManiaHoldNote) ? 1 : 0; if (extras.Contains(":")) { Beatmap.Sampleset samplesetValue = (Beatmap.Sampleset) int.Parse(extras.Split(':')[index]); Beatmap.Sampleset additionsValue = (Beatmap.Sampleset) int.Parse(extras.Split(':')[index + 1]); int?customIndexValue = int.Parse(extras.Split(':')[index + 2]); // Does not exist in file v11. int?volumeValue = null; if (extras.Split(':').Count() > index + 3) { volumeValue = int.Parse(extras.Split(':')[index + 3]); } string filenameValue = ""; if (extras.Split(':').Count() > index + 4) { filenameValue = extras.Split(':')[index + 4]; } return(Tuple.Create(samplesetValue, additionsValue, customIndexValue, volumeValue, filenameValue)); } return(null); }
public Slider(string[] anArgs, Beatmap aBeatmap) : base(anArgs, aBeatmap) { curveType = GetSliderType(anArgs); nodePositions = GetNodes(anArgs).ToList(); edgeAmount = GetEdgeAmount(anArgs); pixelLength = GetPixelLength(anArgs); // hit sounding var edgeHitSounds = GetEdgeHitSounds(anArgs); var edgeAdditions = GetEdgeAdditions(anArgs); startHitSound = edgeHitSounds.Item1; startSampleset = edgeAdditions.Item1; startAddition = edgeAdditions.Item2; endHitSound = edgeHitSounds.Item2; endSampleset = edgeAdditions.Item3; endAddition = edgeAdditions.Item4; reverseHitSounds = edgeHitSounds.Item3.ToList(); reverseSamplesets = edgeAdditions.Item5.ToList(); reverseAdditions = edgeAdditions.Item6.ToList(); // non-explicit if (beatmap != null) { redAnchorPositions = GetRedAnchors().ToList(); pathPxPositions = GetPathPxPositions(); endTime = GetEndTime(); sliderTickTimes = GetSliderTickTimes(); UnstackedEndPosition = edgeAmount % 2 == 1 ? pathPxPositions.Last() : UnstackedPosition; } }
public TimingLine(string[] anArgs) { code = String.Join(",", anArgs); offset = GetOffset(anArgs); meter = GetMeter(anArgs); sampleset = GetSampleset(anArgs); customIndex = GetCustomIndex(anArgs); volume = GetVolume(anArgs); uninherited = IsUninherited(anArgs); type = GetType(anArgs); kiai = type.HasFlag(Type.Kiai); omitsBarLine = type.HasFlag(Type.OmitBarLine); // may not be explicit svMult = GetSvMult(anArgs); }
IEnumerable <Beatmap.Sampleset>, IEnumerable <Beatmap.Sampleset> > GetEdgeAdditions(string[] anArgs) { Beatmap.Sampleset edgeStartSampleset = 0; Beatmap.Sampleset edgeStartAddition = 0; Beatmap.Sampleset edgeEndSampleset = 0; Beatmap.Sampleset edgeEndAddition = 0; IEnumerable <Beatmap.Sampleset> edgeReverseSamplesets = new List <Beatmap.Sampleset>(); IEnumerable <Beatmap.Sampleset> edgeReverseAdditions = new List <Beatmap.Sampleset>(); if (anArgs.Count() > 9) { string edgeAdditions = anArgs[9]; // not set in some situations if (edgeAdditions.Contains("|")) { for (int i = 0; i < edgeAdditions.Split('|').Length; ++i) { Beatmap.Sampleset sampleset = (Beatmap.Sampleset) int.Parse(edgeAdditions.Split('|')[i].Split(':')[0]); Beatmap.Sampleset addition = (Beatmap.Sampleset) int.Parse(edgeAdditions.Split('|')[i].Split(':')[1]); if (i == 0) { edgeStartSampleset = sampleset; edgeStartAddition = addition; } else if (i == edgeAdditions.Split('|').Length - 1) { edgeEndSampleset = sampleset; edgeEndAddition = addition; } else { edgeReverseSamplesets = edgeReverseSamplesets.Concat(new Beatmap.Sampleset[] { sampleset }); edgeReverseAdditions = edgeReverseAdditions.Concat(new Beatmap.Sampleset[] { sampleset }); } } } } return(Tuple.Create(edgeStartSampleset, edgeStartAddition, edgeEndSampleset, edgeEndAddition, edgeReverseSamplesets, edgeReverseAdditions)); }
public HitObject(string[] args, Beatmap beatmap) { this.beatmap = beatmap; code = String.Join(",", args); Position = GetPosition(args); time = GetTime(args); type = GetTypeFlags(args); hitSound = GetHitSound(args); // extras Tuple <Beatmap.Sampleset, Beatmap.Sampleset, int?, int?, string> extras = GetExtras(args); if (extras != null) { // custom index and volume are by default 0 if there are edge hitsounds or similar sampleset = extras.Item1; addition = extras.Item2; customIndex = extras.Item3 == 0 ? null : extras.Item3; volume = extras.Item4 == 0 ? null : extras.Item4; // hitsound filenames only apply to circles and hold notes string hitSoundFile = extras.Item5; if (hitSoundFile.Trim() != "" && (HasType(Type.Circle) || HasType(Type.ManiaHoldNote))) { filename = PathStatic.ParsePath(hitSoundFile, false, true); } } // Sliders and spinners include additional edges which support hit sounding, so we // should handle that after those edges are initialized in Slider/Spinner instead. if (!(this is Slider) && !(this is Spinner)) { usedHitSamples = GetUsedHitSamples().ToList(); } }
/// <summary> Returns all used combinations of customs, samplesets and hit sounds for this object. /// This assumes the game mode is not taiko (special rules apply to taiko only). </summary> private IEnumerable <HitSample> GetUsedHitSamplesNonTaiko() { // Spinners have no impact sound. if (!(this is Spinner)) { // Head foreach (HitSound splitStartHitSound in SplitHitSound(GetStartHitSound().GetValueOrDefault())) { yield return(GetEdgeSample(time, GetStartSampleset(true), splitStartHitSound)); } yield return(GetEdgeSample(time, GetStartSampleset(false), HitSound.Normal)); } // Hold notes can not have a hit sounds on their tails. if (!(this is HoldNote)) { // Tail foreach (HitSound splitEndHitSound in SplitHitSound(GetEndHitSound().GetValueOrDefault())) { yield return(GetEdgeSample(GetEndTime(), GetEndSampleset(true), splitEndHitSound)); } yield return(GetEdgeSample(GetEndTime(), GetEndSampleset(false), HitSound.Normal)); } if (this is Slider slider) { // Reverse for (int i = 0; i < slider.reverseHitSounds.Count; ++i) { HitSound?reverseHitSound = slider.reverseHitSounds.ElementAt(i); double theoreticalStart = time - beatmap.GetTheoreticalUnsnap(time); double reverseTime = Timestamp.Round(theoreticalStart + slider.GetCurveDuration() * (i + 1)); foreach (HitSound splitReverseHitSound in SplitHitSound(reverseHitSound.GetValueOrDefault())) { yield return(GetEdgeSample(reverseTime, slider.GetReverseSampleset(i, true), splitReverseHitSound)); } yield return(GetEdgeSample(reverseTime, slider.GetReverseSampleset(i), HitSound.Normal)); } List <TimingLine> lines = beatmap.timingLines.Where(line => line.offset > slider.time && line.offset <= slider.endTime).ToList(); lines.Add(beatmap.GetTimingLine(slider.time, hitSoundLeniency: true)); // Body, only applies to standard. Catch has droplets instead of body. Taiko and mania have a body but play no background sound. if (beatmap.generalSettings.mode == Beatmap.Mode.Standard) { foreach (TimingLine line in lines) { // Priority: object sampleset > line sampleset // The addition is ignored for sliderslides, it seems. Beatmap.Sampleset effectiveSampleset = sampleset != Beatmap.Sampleset.Auto ? sampleset : line.sampleset; // Additions are not ignored for sliderwhistles, however. if (slider.hitSound == HitSound.Whistle) { effectiveSampleset = addition != Beatmap.Sampleset.Auto ? addition : effectiveSampleset; } // The regular sliderslide will always play regardless of using sliderwhistle. yield return(new HitSample( line.customIndex, effectiveSampleset, HitSound.None, HitSample.HitSource.Body, line.offset)); if (hitSound != HitSound.None) { yield return(new HitSample( line.customIndex, effectiveSampleset, hitSound, HitSample.HitSource.Body, line.offset)); } } } // Tick, only applies to standard and catch. Mania has no ticks, taiko sliders play regular impacts. if (beatmap.generalSettings.mode == Beatmap.Mode.Standard || beatmap.generalSettings.mode == Beatmap.Mode.Catch) { foreach (double tickTime in slider.sliderTickTimes) { TimingLine line = beatmap.GetTimingLine(tickTime); // If no line exists, we use the default settings. int customIndex = line?.customIndex ?? 1; // Unlike the slider body (for sliderwhistles) and edges, slider ticks are unaffected by additions. Beatmap.Sampleset sampleset = GetSampleset(false, tickTime); // Defaults to normal if none is set (before any timing line). if (sampleset == Beatmap.Sampleset.Auto) { sampleset = Beatmap.Sampleset.Normal; } yield return(new HitSample(customIndex, sampleset, null, HitSample.HitSource.Tick, tickTime)); } } } }
/// <summary> Returns all used combinations of customs, samplesets and hit sounds for this object. </summary> public IEnumerable <HitSample> GetUsedHitSamples() { // Head foreach (HitSound splitStartHitSound in SplitHitSound(GetStartHitSound().GetValueOrDefault())) { yield return(GetEdgeSample(time, GetStartSampleset(true), splitStartHitSound)); } yield return(GetEdgeSample(time, GetStartSampleset(false), HitSound.Normal)); // Hold notes can not have a hit sounds on their tails. if (!(this is HoldNote)) { // Tail foreach (HitSound splitEndHitSound in SplitHitSound(GetEndHitSound().GetValueOrDefault())) { yield return(GetEdgeSample(GetEndTime(), GetEndSampleset(true), splitEndHitSound)); } yield return(GetEdgeSample(GetEndTime(), GetEndSampleset(false), HitSound.Normal)); } if (this is Slider slider) { // Reverse for (int i = 0; i < slider.reverseHitSounds.Count; ++i) { HitSound? reverseHitSound = slider.reverseHitSounds.ElementAt(i); Beatmap.Sampleset?reverseSampleset = slider.GetReverseSampleset(i); Beatmap.Sampleset?reverseAddition = slider.reverseAdditions.Any() ? // not a thing in file version 9 slider.reverseAdditions.ElementAt(i) : (Beatmap.Sampleset?)null; double reverseTime = slider.GetCurveDuration() * (i + 1); foreach (HitSound splitReverseHitSound in SplitHitSound(reverseHitSound.GetValueOrDefault())) { yield return(GetEdgeSample(reverseTime, reverseAddition ?? reverseSampleset, splitReverseHitSound)); } yield return(GetEdgeSample(reverseTime, reverseSampleset, HitSound.Normal)); } List <TimingLine> lines = beatmap.timingLines.Where(aLine => aLine.offset > slider.time && aLine.offset <= slider.endTime).ToList(); lines.Add(beatmap.GetTimingLine(slider.time, true)); // Body foreach (TimingLine line in lines) { yield return(new HitSample(line.customIndex, line.sampleset, hitSound, HitSample.HitSource.Body, line.offset)); } // Tick foreach (double tickTime in slider.sliderTickTimes) { TimingLine line = beatmap.GetTimingLine(tickTime); // If no line exists, we use the default settings. int customIndex = line?.customIndex ?? 1; Beatmap.Sampleset sampleset = GetSampleset(true, tickTime); // Defaults to normal if none is set (before any timing line). if (sampleset == Beatmap.Sampleset.Auto) { sampleset = Beatmap.Sampleset.Normal; } yield return(new HitSample(customIndex, sampleset, null, HitSample.HitSource.Tick, tickTime)); } } }
List <Beatmap.Sampleset>, List <Beatmap.Sampleset> > GetEdgeAdditions(string[] args) { Beatmap.Sampleset edgeStartSampleset = 0; Beatmap.Sampleset edgeStartAddition = 0; Beatmap.Sampleset edgeEndSampleset = 0; Beatmap.Sampleset edgeEndAddition = 0; List <Beatmap.Sampleset> edgeReverseSamplesets = new List <Beatmap.Sampleset>(); List <Beatmap.Sampleset> edgeReverseAdditions = new List <Beatmap.Sampleset>(); if (args.Count() > 9) { // Not set in some situations (e.g. older file versions or no hit sounds). string edgeAdditions = args[9]; if (edgeAdditions.Contains("|")) { for (int i = 0; i < edgeAdditions.Split('|').Length; ++i) { Beatmap.Sampleset sampleset = (Beatmap.Sampleset) int.Parse(edgeAdditions.Split('|')[i].Split(':')[0]); Beatmap.Sampleset addition = (Beatmap.Sampleset) int.Parse(edgeAdditions.Split('|')[i].Split(':')[1]); if (i == 0) { edgeStartSampleset = sampleset; edgeStartAddition = addition; } else if (i == edgeAdditions.Split('|').Length - 1) { edgeEndSampleset = sampleset; edgeEndAddition = addition; } else { edgeReverseSamplesets.Add(sampleset); edgeReverseAdditions.Add(addition); } } } } else { // If an object has no complex hit sounding, it omits fields such as edge // hit sounds. Instead, it simply uses one hit sound over everything. edgeStartSampleset = sampleset; edgeEndSampleset = sampleset; for (int i = 0; i < edgeAmount; ++i) { edgeReverseSamplesets.Add(sampleset); } edgeStartAddition = addition; edgeEndAddition = addition; for (int i = 0; i < edgeAmount; ++i) { edgeReverseAdditions.Add(addition); } } return(Tuple.Create(edgeStartSampleset, edgeStartAddition, edgeEndSampleset, edgeEndAddition, edgeReverseSamplesets, edgeReverseAdditions)); }