public OsuPattern FromSelected(Beatmap beatmap, string name, out Beatmap patternBeatmap) { // Copy it so the changes dont affect the given beatmap object patternBeatmap = beatmap.DeepCopy(); // Remove the storyboarding patternBeatmap.StoryboardLayerFail.Clear(); patternBeatmap.StoryboardLayerPass.Clear(); patternBeatmap.StoryboardLayerBackground.Clear(); patternBeatmap.StoryboardLayerForeground.Clear(); patternBeatmap.StoryboardLayerOverlay.Clear(); // Keep the selected subset of hit objects patternBeatmap.HitObjects = patternBeatmap.HitObjects.Where(h => h.IsSelected).ToList(); var startTime = patternBeatmap.GetHitObjectStartTime() - Padding; var endTime = patternBeatmap.GetHitObjectEndTime() + Padding; // Keep the timing points in the range of the hitobjects patternBeatmap.BeatmapTiming.TimingPoints = patternBeatmap.BeatmapTiming.TimingPoints .Where(tp => tp.Offset >= startTime && tp.Offset <= endTime).ToList(); // Add some earlier timing points if necessary var firstUnInheritedTimingPoint = patternBeatmap.HitObjects.First().UnInheritedTimingPoint; var firstNormalTimingPoint = patternBeatmap.HitObjects.First().TimingPoint; if (!patternBeatmap.BeatmapTiming.TimingPoints.Contains(firstUnInheritedTimingPoint)) { patternBeatmap.BeatmapTiming.TimingPoints.Add(firstUnInheritedTimingPoint); } if (!patternBeatmap.BeatmapTiming.TimingPoints.Contains(firstNormalTimingPoint)) { patternBeatmap.BeatmapTiming.TimingPoints.Add(firstNormalTimingPoint); } patternBeatmap.BeatmapTiming.Sort(); // Generate a file name and save the pattern var now = DateTime.Now; var fileName = GenerateUniquePatternFileName(name, now); return(new OsuPattern { Name = name, CreationTime = now, LastUsedTime = now, FileName = fileName, ObjectCount = patternBeatmap.HitObjects.Count, Duration = TimeSpan.FromMilliseconds(endTime - startTime - 2 * Padding), BeatLength = patternBeatmap.BeatmapTiming.GetBeatLength(startTime + Padding, endTime - Padding, true) }); }
public OsuPattern FromSelected(Beatmap beatmap, out Beatmap patternBeatmap, string name) { // Check if it has selected objects if (!beatmap.HitObjects.Any(h => h.IsSelected)) { throw new Exception("No selected hit objects found."); } // Copy it so the changes dont affect the given beatmap object patternBeatmap = beatmap.DeepCopy(); RemoveStoryboard(patternBeatmap); RemoveEverythingThatIsNotTheseHitObjects(patternBeatmap, patternBeatmap.HitObjects.Where(h => h.IsSelected).ToList()); return(FromBeatmap(patternBeatmap, name)); }
private string Copy_Hitsounds(HitsoundCopierVm arg, BackgroundWorker worker) { var doMutedIndex = arg.MutedIndex >= 0; var paths = arg.PathTo.Split('|'); var mapsDone = 0; var sampleSchema = new SampleSchema(); var reader = EditorReaderStuff.GetFullEditorReaderOrNot(); foreach (var pathTo in paths) { BeatmapEditor editorTo = EditorReaderStuff.GetNewestVersionOrNot(pathTo, reader);; Beatmap beatmapTo = editorTo.Beatmap; Beatmap beatmapFrom; if (!string.IsNullOrEmpty(arg.PathFrom)) { var editorFrom = EditorReaderStuff.GetNewestVersionOrNot(arg.PathFrom, reader); beatmapFrom = editorFrom.Beatmap; } else { // Copy from an empty beatmap similar to the map to copy to beatmapFrom = beatmapTo.DeepCopy(); beatmapFrom.HitObjects.Clear(); beatmapFrom.BeatmapTiming.Clear(); } Timeline processedTimeline; if (arg.CopyMode == 0) { // Every defined hitsound and sampleset on hitsound gets copied to their copyTo destination // Timelines var tlTo = beatmapTo.GetTimeline(); var tlFrom = beatmapFrom.GetTimeline(); var volumeMuteTimes = arg.CopyVolumes && arg.AlwaysPreserve5Volume ? new List <double>() : null; if (arg.CopyHitsounds) { ResetHitObjectHitsounds(beatmapTo); CopyHitsounds(arg, tlFrom, tlTo); } // Save tlo times where timingpoint volume is 5% // Timingpointchange all the undefined tlo from copyFrom volumeMuteTimes?.AddRange(from tloTo in tlTo.TimelineObjects where tloTo.CanCopy && Math.Abs(tloTo.SampleVolume) < Precision.DOUBLE_EPSILON && Math.Abs(tloTo.FenoSampleVolume - 5) < Precision.DOUBLE_EPSILON select tloTo.Time); // Volumes and samplesets and customindices greenlines get copied with timingpointchanges and allafter enabled var timingPointsChanges = beatmapFrom.BeatmapTiming.TimingPoints.Select(tp => new TimingPointsChange(tp, sampleset: arg.CopySampleSets, index: arg.CopySampleSets, volume: arg.CopyVolumes)).ToList(); // Apply the timingpoint changes TimingPointsChange.ApplyChanges(beatmapTo.BeatmapTiming, timingPointsChanges, true); processedTimeline = tlTo; // Return 5% volume to tlo that had it before if (volumeMuteTimes != null) { var timingPointsChangesMute = new List <TimingPointsChange>(); processedTimeline.GiveTimingPoints(beatmapTo.BeatmapTiming); // Exclude objects which use their own sample volume property instead foreach (var tloTo in processedTimeline.TimelineObjects.Where(o => Math.Abs(o.SampleVolume) < Precision.DOUBLE_EPSILON)) { if (volumeMuteTimes.Contains(tloTo.Time)) { // Add timingpointschange to copy timingpoint hitsounds var tp = tloTo.HitsoundTimingPoint.Copy(); tp.Offset = tloTo.Time; tp.Volume = 5; timingPointsChangesMute.Add(new TimingPointsChange(tp, volume: true)); } else { // Add timingpointschange to preserve index and volume var tp = tloTo.HitsoundTimingPoint.Copy(); tp.Offset = tloTo.Time; tp.Volume = tloTo.FenoSampleVolume; timingPointsChangesMute.Add(new TimingPointsChange(tp, volume: true)); } } // Apply the timingpoint changes TimingPointsChange.ApplyChanges(beatmapTo.BeatmapTiming, timingPointsChangesMute); } } else { // Smarty mode // Copy the defined hitsounds literally (not feno, that will be reserved for cleaner). Only the tlo that have been defined by copyFrom get overwritten. var tlTo = beatmapTo.GetTimeline(); var tlFrom = beatmapFrom.GetTimeline(); var timingPointsChanges = new List <TimingPointsChange>(); var mode = (GameMode)beatmapTo.General["Mode"].IntValue; var mapDir = editorTo.GetParentFolder(); var firstSamples = HitsoundImporter.AnalyzeSamples(mapDir); if (arg.CopyHitsounds) { CopyHitsounds(arg, beatmapTo, tlFrom, tlTo, timingPointsChanges, mode, mapDir, firstSamples, ref sampleSchema); } if (arg.CopyBodyHitsounds) { // Remove timingpoints in beatmapTo that are in a sliderbody/spinnerbody for both beatmapTo and BeatmapFrom foreach (var tp in from ho in beatmapTo.HitObjects from tp in ho.BodyHitsounds where beatmapFrom.HitObjects.Any(o => o.Time <tp.Offset && o.EndTime> tp.Offset) where !tp.Uninherited select tp) { beatmapTo.BeatmapTiming.Remove(tp); } // Get timingpointschanges for every timingpoint from beatmapFrom that is in a sliderbody/spinnerbody for both beatmapTo and BeatmapFrom timingPointsChanges.AddRange(from ho in beatmapFrom.HitObjects from tp in ho.BodyHitsounds where beatmapTo.HitObjects.Any(o => o.Time <tp.Offset && o.EndTime> tp.Offset) select new TimingPointsChange(tp.Copy(), sampleset: arg.CopySampleSets, index: arg.CopySampleSets, volume: arg.CopyVolumes)); } // Apply the timingpoint changes TimingPointsChange.ApplyChanges(beatmapTo.BeatmapTiming, timingPointsChanges); processedTimeline = tlTo; } if (arg.CopyStoryboardedSamples) { if (arg.CopyMode == 0) { beatmapTo.StoryboardSoundSamples.Clear(); } beatmapTo.GiveObjectsGreenlines(); processedTimeline.GiveTimingPoints(beatmapTo.BeatmapTiming); var mapDir = editorTo.GetParentFolder(); var firstSamples = HitsoundImporter.AnalyzeSamples(mapDir, true); var samplesTo = new HashSet <StoryboardSoundSample>(beatmapTo.StoryboardSoundSamples); var mode = (GameMode)beatmapTo.General["Mode"].IntValue; foreach (var sampleFrom in beatmapFrom.StoryboardSoundSamples) { if (arg.IgnoreHitsoundSatisfiedSamples) { var tloHere = processedTimeline.TimelineObjects.FindAll(o => Math.Abs(o.Time - sampleFrom.StartTime) <= arg.TemporalLeniency); var samplesHere = new HashSet <string>(); foreach (var tlo in tloHere) { foreach (var filename in tlo.GetPlayingFilenames(mode)) { var samplePath = Path.Combine(mapDir, filename); var fullPathExtLess = Path.Combine(Path.GetDirectoryName(samplePath), Path.GetFileNameWithoutExtension(samplePath)); if (firstSamples.Keys.Contains(fullPathExtLess)) { samplePath = firstSamples[fullPathExtLess]; } samplesHere.Add(samplePath); } } var sbSamplePath = Path.Combine(mapDir, sampleFrom.FilePath); var sbFullPathExtLess = Path.Combine(Path.GetDirectoryName(sbSamplePath), Path.GetFileNameWithoutExtension(sbSamplePath)); if (firstSamples.Keys.Contains(sbFullPathExtLess)) { sbSamplePath = firstSamples[sbFullPathExtLess]; } if (samplesHere.Contains(sbSamplePath)) { continue; } } // Add the StoryboardSoundSamples from beatmapFrom to beatmapTo if it doesn't already have the sample if (!samplesTo.Contains(sampleFrom)) { beatmapTo.StoryboardSoundSamples.Add(sampleFrom); } } // Sort the storyboarded samples beatmapTo.StoryboardSoundSamples.Sort(); } if (arg.MuteSliderends) { var timingPointsChanges = new List <TimingPointsChange>(); beatmapTo.GiveObjectsGreenlines(); processedTimeline.GiveTimingPoints(beatmapTo.BeatmapTiming); foreach (var tloTo in processedTimeline.TimelineObjects) { if (FilterMuteTlo(tloTo, beatmapTo, arg)) { // Set volume to 5%, remove all hitsounds, apply customindex and sampleset tloTo.SampleSet = arg.MutedSampleSet; tloTo.AdditionSet = 0; tloTo.Normal = false; tloTo.Whistle = false; tloTo.Finish = false; tloTo.Clap = false; tloTo.HitsoundsToOrigin(); // Add timingpointschange to copy timingpoint hitsounds var tp = tloTo.HitsoundTimingPoint.Copy(); tp.Offset = tloTo.Time; tp.SampleSet = arg.MutedSampleSet; tp.SampleIndex = arg.MutedIndex; tp.Volume = 5; timingPointsChanges.Add(new TimingPointsChange(tp, sampleset: true, index: doMutedIndex, volume: true)); } else { // Add timingpointschange to preserve index and volume and sampleset var tp = tloTo.HitsoundTimingPoint.Copy(); tp.Offset = tloTo.Time; timingPointsChanges.Add(new TimingPointsChange(tp, sampleset: true, index: doMutedIndex, volume: true)); } } // Apply the timingpoint changes TimingPointsChange.ApplyChanges(beatmapTo.BeatmapTiming, timingPointsChanges); } // Save the file editorTo.SaveFile(); // Export the sample schema if there are samples if (sampleSchema.Count > 0) { string exportFolder = MainWindow.ExportPath; DirectoryInfo di = new DirectoryInfo(exportFolder); foreach (FileInfo file in di.GetFiles()) { file.Delete(); } HitsoundExporter.ExportSampleSchema(sampleSchema, exportFolder); System.Diagnostics.Process.Start(exportFolder); } // Update progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(++mapsDone * 100 / paths.Length); } } return("Done!"); }
/// <summary> /// Places each hit object of the pattern beatmap into the other beatmap and applies timingpoint changes to copy timingpoint stuff aswell. /// The given pattern beatmap could be modified by this method if protectBeatmapPattern is false. /// </summary> /// <param name="patternBeatmap">The pattern beatmap to be placed into the beatmap.</param> /// <param name="beatmap">To beatmap to place the pattern in.</param> /// <param name="offset">An offset to move the pattern beatmap in time with.</param> /// <param name="protectBeatmapPattern">If true, copies the pattern beatmap to prevent the pattern beatmap from being modified by this method.</param> public void PlaceOsuPattern(Beatmap patternBeatmap, Beatmap beatmap, double offset = 0, bool protectBeatmapPattern = true) { if (protectBeatmapPattern) { // Copy so the original pattern doesnt get changed patternBeatmap = patternBeatmap.DeepCopy(); } // Do the offset if (Math.Abs(offset) > Precision.DOUBLE_EPSILON) { patternBeatmap.OffsetTime(offset); } // We adjust the pattern first so it alligns with the beatmap. // The right timing is applied and optional pre-processing is applied. // Sliderends and object timingpoints get recalculated. PreparePattern(patternBeatmap, beatmap, out var parts, out var timingPointsChanges); // Keep just the timing point changes which are inside the parts. // These timing point changes have everything that is necessary for inside the parts of the pattern. (even timing) timingPointsChanges = timingPointsChanges.Where(tpc => parts.Any(part => part.StartTime <= tpc.MyTP.Offset && part.EndTime >= tpc.MyTP.Offset)).ToList(); // Remove stuff if (PatternOverwriteMode != PatternOverwriteMode.NoOverwrite) { foreach (var part in parts) { RemovePartOfBeatmap(beatmap, part.StartTime - Padding, part.EndTime + Padding); } } // Add timingpoint changes for each hitobject to make sure they still have the wanted SV and hitsounds (especially near the edges of parts) // It is possible for the timingpoint of a hitobject at the start of a part to be outside of the part, so this fixes issues related to that timingPointsChanges.AddRange( beatmap.HitObjects.Where(ho => ho.TimingPoint != null) .Select(GetSvChange)); if (IncludeHitsounds) { timingPointsChanges.AddRange( beatmap.HitObjects.Where(ho => ho.HitsoundTimingPoint != null) .Select(GetHitsoundChange)); } // Apply the changes TimingPointsChange.ApplyChanges(beatmap.BeatmapTiming, timingPointsChanges); // Add the hitobjects of the pattern beatmap.HitObjects.AddRange(patternBeatmap.HitObjects); // Sort hitobjects beatmap.SortHitObjects(); if (FixColourHax) { beatmap.FixComboSkip(); } beatmap.GiveObjectsGreenlines(); beatmap.CalculateSliderEndTimes(); }
/// <summary> /// Places each hit object of the pattern beatmap into the other beatmap and applies timingpoint changes to copy timingpoint stuff aswell. /// The given pattern beatmap could be modified by this method if protectBeatmapPattern is false. /// </summary> /// <param name="patternBeatmap">The pattern beatmap to be placed into the beatmap.</param> /// <param name="beatmap">To beatmap to place the pattern in.</param> /// <param name="offset">An offset to move the pattern beatmap in time with.</param> /// <param name="protectBeatmapPattern">If true, copies the pattern beatmap to prevent the pattern beatmap from being modified by this method.</param> public void PlaceOsuPattern(Beatmap patternBeatmap, Beatmap beatmap, double offset = 0, bool protectBeatmapPattern = true) { if (protectBeatmapPattern) { // Copy so the original pattern doesnt get changed patternBeatmap = patternBeatmap.DeepCopy(); } if (offset != 0) { patternBeatmap.OffsetTime(offset); } // Do some kind of processing to fix timing etc // Set the global SV and BPM in the pattern beatmap so the object end times can be calculated for the partitioning patternBeatmap.BeatmapTiming.SliderMultiplier = beatmap.BeatmapTiming.SliderMultiplier; patternBeatmap.BeatmapTiming.TimingPoints.RemoveAll(tp => tp.Uninherited); patternBeatmap.BeatmapTiming.TimingPoints.AddRange(beatmap.BeatmapTiming.GetAllRedlines()); patternBeatmap.BeatmapTiming.Sort(); patternBeatmap.CalculateSliderEndTimes(); // Partition the pattern beatmap List <Tuple <double, double> > parts; if (PatternOverwriteMode == PatternOverwriteMode.PartitionedOverwrite) { parts = PartitionBeatmap(patternBeatmap); } else { parts = new List <Tuple <double, double> > { new Tuple <double, double>(patternBeatmap.GetHitObjectStartTime(), patternBeatmap.GetHitObjectEndTime()) }; } // Remove stuff if (PatternOverwriteMode != PatternOverwriteMode.NoOverwrite) { foreach (var part in parts) { RemovePartOfBeatmap(beatmap, part.Item1 - Padding, part.Item2 + Padding); } } // Add the hitobjects of the pattern beatmap.HitObjects.AddRange(patternBeatmap.HitObjects); // Add timingpoint changes for each timingpoint in a part in the pattern var timingPointsChanges = new List <TimingPointsChange>(); foreach (var part in parts) { timingPointsChanges.AddRange( patternBeatmap.BeatmapTiming.TimingPoints.Where(tp => tp.Offset >= part.Item1 - Padding && tp.Offset <= part.Item2 + Padding) .Select(tp => GetTimingPointsChange(tp, true, true))); } // Add timingpoint changes for each hitobject to make sure they still have the wanted SV and hitsounds (especially near the edges of parts) // It is possible for the timingpoint of a hitobject at the start of a part to be outside of the part, so this fixes issues related to that timingPointsChanges.AddRange( beatmap.HitObjects.Where(ho => ho.TimingPoint != null) .Select(ho => GetTimingPointsChange(ho, true, true))); // Apply the changes TimingPointsChange.ApplyChanges(beatmap.BeatmapTiming, timingPointsChanges); // Sort hitobjects later so the timingpoints changes from the new hitobjects have priority beatmap.SortHitObjects(); }