Ejemplo n.º 1
0
        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!");
        }
Ejemplo n.º 2
0
        private string Make_Hitsounds(HitsoundStudioVm arg, BackgroundWorker worker, DoWorkEventArgs _)
        {
            string result = string.Empty;

            if (arg.HitsoundExportModeSetting == HitsoundStudioVm.HitsoundExportMode.Standard)
            {
                // Convert the multiple layers into packages that have the samples from all the layers at one specific time
                List <SamplePackage> samplePackages = HitsoundConverter.ZipLayers(arg.HitsoundLayers, arg.DefaultSample);
                UpdateProgressBar(worker, 10);

                // Balance the volume between greenlines and samples
                HitsoundConverter.BalanceVolumes(samplePackages, 0, false);
                UpdateProgressBar(worker, 20);

                // Load the samples so validation can be done
                HashSet <SampleGeneratingArgs> allSampleArgs = new HashSet <SampleGeneratingArgs>();
                foreach (SamplePackage sp in samplePackages)
                {
                    allSampleArgs.UnionWith(sp.Samples.Select(o => o.SampleArgs));
                }

                var loadedSamples = SampleImporter.ImportSamples(allSampleArgs);
                UpdateProgressBar(worker, 30);

                // Convert the packages to hitsounds that fit on an osu standard map
                CompleteHitsounds completeHitsounds =
                    HitsoundConverter.GetCompleteHitsounds(samplePackages, loadedSamples);
                UpdateProgressBar(worker, 60);

                if (arg.ShowResults)
                {
                    // Count the number of samples
                    int samples = completeHitsounds.CustomIndices.SelectMany(ci => ci.Samples.Values)
                                  .Count(h => h.Any(SampleImporter.ValidateSampleArgs));

                    // Count the number of changes of custom index
                    int greenlines = 0;
                    int lastIndex  = -1;
                    foreach (var hit in completeHitsounds.Hitsounds.Where(hit => hit.CustomIndex != lastIndex))
                    {
                        lastIndex = hit.CustomIndex;
                        greenlines++;
                    }

                    result = $"Number of sample indices: {completeHitsounds.CustomIndices.Count}, " +
                             $"Number of samples: {samples}, Number of greenlines: {greenlines}";
                }

                if (arg.DeleteAllInExportFirst && (arg.ExportSamples || arg.ExportMap))
                {
                    // Delete all files in the export folder before filling it again
                    DirectoryInfo di = new DirectoryInfo(arg.ExportFolder);
                    foreach (FileInfo file in di.GetFiles())
                    {
                        file.Delete();
                    }
                }

                UpdateProgressBar(worker, 70);

                // Export the hitsound map and sound samples
                if (arg.ExportMap)
                {
                    HitsoundExporter.ExportHitsounds(completeHitsounds.Hitsounds, arg.BaseBeatmap, arg.ExportFolder);
                }

                UpdateProgressBar(worker, 80);

                if (arg.ExportSamples)
                {
                    HitsoundExporter.ExportCustomIndices(completeHitsounds.CustomIndices, arg.ExportFolder,
                                                         loadedSamples);
                }

                UpdateProgressBar(worker, 99);
            }
            else if (arg.HitsoundExportModeSetting == HitsoundStudioVm.HitsoundExportMode.Coinciding)
            {
                List <SamplePackage> samplePackages = HitsoundConverter.ZipLayers(arg.HitsoundLayers, arg.DefaultSample, 0, false);

                HitsoundConverter.BalanceVolumes(samplePackages, 0, false, true);
                UpdateProgressBar(worker, 20);

                Dictionary <SampleGeneratingArgs, SampleSoundGenerator> loadedSamples = null;
                Dictionary <SampleGeneratingArgs, string>  sampleNames     = null;
                Dictionary <SampleGeneratingArgs, Vector2> samplePositions = null;
                var hitsounds = HitsoundConverter.GetHitsounds(samplePackages, ref loadedSamples, ref sampleNames, ref samplePositions);

                // Load the samples so validation can be done
                UpdateProgressBar(worker, 50);

                if (arg.ShowResults)
                {
                    result = "Number of sample indices: 0, " +
                             $"Number of samples: {loadedSamples.Count}, Number of greenlines: 0";
                }

                if (arg.DeleteAllInExportFirst && (arg.ExportSamples || arg.ExportMap))
                {
                    // Delete all files in the export folder before filling it again
                    DirectoryInfo di = new DirectoryInfo(arg.ExportFolder);
                    foreach (FileInfo file in di.GetFiles())
                    {
                        file.Delete();
                    }
                }
                UpdateProgressBar(worker, 60);

                if (arg.ExportMap)
                {
                    HitsoundExporter.ExportHitsounds(hitsounds, arg.BaseBeatmap, arg.ExportFolder, false);
                }
                UpdateProgressBar(worker, 70);

                if (arg.ExportSamples)
                {
                    HitsoundExporter.ExportLoadedSamples(loadedSamples, arg.ExportFolder, sampleNames);
                }
            }
            else if (arg.HitsoundExportModeSetting == HitsoundStudioVm.HitsoundExportMode.Storyboard)
            {
                List <SamplePackage> samplePackages = HitsoundConverter.ZipLayers(arg.HitsoundLayers, arg.DefaultSample, 0, false);

                HitsoundConverter.BalanceVolumes(samplePackages, 0, false, true);
                UpdateProgressBar(worker, 20);

                Dictionary <SampleGeneratingArgs, SampleSoundGenerator> loadedSamples = null;
                Dictionary <SampleGeneratingArgs, string>  sampleNames     = null;
                Dictionary <SampleGeneratingArgs, Vector2> samplePositions = null;
                var hitsounds = HitsoundConverter.GetHitsounds(samplePackages, ref loadedSamples, ref sampleNames, ref samplePositions);

                // Load the samples so validation can be done
                UpdateProgressBar(worker, 50);

                if (arg.ShowResults)
                {
                    result = "Number of sample indices: 0, " +
                             $"Number of samples: {loadedSamples.Count}, Number of greenlines: 0";
                }

                if (arg.DeleteAllInExportFirst && (arg.ExportSamples || arg.ExportMap))
                {
                    // Delete all files in the export folder before filling it again
                    DirectoryInfo di = new DirectoryInfo(arg.ExportFolder);
                    foreach (FileInfo file in di.GetFiles())
                    {
                        file.Delete();
                    }
                }
                UpdateProgressBar(worker, 60);

                if (arg.ExportMap)
                {
                    HitsoundExporter.ExportHitsounds(hitsounds, arg.BaseBeatmap, arg.ExportFolder, false, true);
                }
                UpdateProgressBar(worker, 70);

                if (arg.ExportSamples)
                {
                    HitsoundExporter.ExportLoadedSamples(loadedSamples, arg.ExportFolder, sampleNames);
                }
            }

            // Open export folder
            if (arg.ExportSamples || arg.ExportMap)
            {
                System.Diagnostics.Process.Start(arg.ExportFolder);
            }

            // Collect garbage
            GC.Collect();

            UpdateProgressBar(worker, 100);

            return(result);
        }
        private void Make_Hitsounds(Arguments arg, BackgroundWorker worker, DoWorkEventArgs _)
        {
            if (arg.Debug)
            {
                // Convert the multiple layers into packages that have the samples from all the layers at one specific time
                List <SamplePackage> samplePackages = HitsoundConverter.ZipLayers(arg.HitsoundLayers, arg.DefaultSample);
                UpdateProgressBar(worker, 10);

                // Balance the volume between greenlines and samples
                HitsoundConverter.BalanceVolumes(samplePackages, new VolumeBalancingArgs(0, false));
                UpdateProgressBar(worker, 20);

                // Load the samples so validation can be done
                HashSet <SampleGeneratingArgs> allSampleArgs = new HashSet <SampleGeneratingArgs>();
                foreach (SamplePackage sp in samplePackages)
                {
                    allSampleArgs.UnionWith(sp.Samples.Select(o => o.SampleArgs));
                }
                var loadedSamples = SampleImporter.ImportSamples(allSampleArgs);
                UpdateProgressBar(worker, 40);

                // Convert the packages to hitsounds that fit on an osu standard map
                CompleteHitsounds completeHitsounds = HitsoundConverter.GetCompleteHitsounds(samplePackages, loadedSamples);
                UpdateProgressBar(worker, 60);

                int samples = completeHitsounds.CustomIndices.SelectMany(ci => ci.Samples.Values).Count(h => h.Any(SampleImporter.ValidateSampleArgs));
                UpdateProgressBar(worker, 80);

                int greenlines = 0;
                int lastIndex  = -1;
                foreach (var hit in completeHitsounds.Hitsounds.Where(hit => hit.CustomIndex != lastIndex))
                {
                    lastIndex = hit.CustomIndex;
                    greenlines++;
                }
                UpdateProgressBar(worker, 100);

                MessageBox.Show(
                    $"Number of sample indices: {completeHitsounds.CustomIndices.Count}, Number of samples: {samples}, Number of greenlines: {greenlines}");
            }
            else
            {
                // Convert the multiple layers into packages that have the samples from all the layers at one specific time
                List <SamplePackage> samplePackages = HitsoundConverter.ZipLayers(arg.HitsoundLayers, arg.DefaultSample);
                UpdateProgressBar(worker, 10);

                // Balance the volume between greenlines and samples
                HitsoundConverter.BalanceVolumes(samplePackages, new VolumeBalancingArgs(0, false));
                UpdateProgressBar(worker, 20);

                // Load the samples so validation can be done
                HashSet <SampleGeneratingArgs> allSampleArgs = new HashSet <SampleGeneratingArgs>();
                foreach (SamplePackage sp in samplePackages)
                {
                    allSampleArgs.UnionWith(sp.Samples.Select(o => o.SampleArgs));
                }
                var loadedSamples = SampleImporter.ImportSamples(allSampleArgs);
                UpdateProgressBar(worker, 30);

                // Convert the packages to hitsounds that fit on an osu standard map
                CompleteHitsounds completeHitsounds = HitsoundConverter.GetCompleteHitsounds(samplePackages, loadedSamples);
                UpdateProgressBar(worker, 60);

                // Delete all files in the export folder before filling it again
                DirectoryInfo di = new DirectoryInfo(arg.ExportFolder);
                foreach (FileInfo file in di.GetFiles())
                {
                    file.Delete();
                }
                UpdateProgressBar(worker, 80);

                // Export the hitsound .osu and sound samples
                HitsoundExporter.ExportCompleteHitsounds(arg.ExportFolder, arg.BaseBeatmap, completeHitsounds, loadedSamples);
                UpdateProgressBar(worker, 99);

                // Open export folder
                System.Diagnostics.Process.Start(arg.ExportFolder);
            }
            // Collect garbage
            GC.Collect();

            UpdateProgressBar(worker, 100);
        }
Ejemplo n.º 4
0
        private string Make_Hitsounds(HitsoundStudioVm arg, BackgroundWorker worker, DoWorkEventArgs _)
        {
            string result = string.Empty;

            bool validateSampleFile =
                !(arg.SingleSampleExportFormat == HitsoundExporter.SampleExportFormat.MidiChords ||
                  arg.MixedSampleExportFormat == HitsoundExporter.SampleExportFormat.MidiChords);

            var comparer = new SampleGeneratingArgsComparer(validateSampleFile);

            if (arg.HitsoundExportModeSetting == HitsoundStudioVm.HitsoundExportMode.Standard)
            {
                // Convert the multiple layers into packages that have the samples from all the layers at one specific time
                // Don't add default sample when exporting midi files because that's not a final export.
                List <SamplePackage> samplePackages = HitsoundConverter.ZipLayers(arg.HitsoundLayers, arg.DefaultSample, arg.ZipLayersLeniency, validateSampleFile);
                UpdateProgressBar(worker, 10);

                // Balance the volume between greenlines and samples
                HitsoundConverter.BalanceVolumes(samplePackages, 0, false);
                UpdateProgressBar(worker, 20);

                // Load the samples so validation can be done
                HashSet <SampleGeneratingArgs> allSampleArgs = new HashSet <SampleGeneratingArgs>(comparer);
                foreach (SamplePackage sp in samplePackages)
                {
                    allSampleArgs.UnionWith(sp.Samples.Select(o => o.SampleArgs));
                }

                var loadedSamples = SampleImporter.ImportSamples(allSampleArgs, comparer);
                UpdateProgressBar(worker, 30);

                // Convert the packages to hitsounds that fit on an osu standard map
                CompleteHitsounds completeHitsounds =
                    HitsoundConverter.GetCompleteHitsounds(samplePackages, loadedSamples,
                                                           arg.UsePreviousSampleSchema ? arg.PreviousSampleSchema.GetCustomIndices() : null,
                                                           arg.AllowGrowthPreviousSampleSchema, arg.FirstCustomIndex, validateSampleFile, comparer);
                UpdateProgressBar(worker, 60);

                // Save current sample schema
                if (!arg.UsePreviousSampleSchema)
                {
                    arg.PreviousSampleSchema = new SampleSchema(completeHitsounds.CustomIndices);
                }
                else if (arg.AllowGrowthPreviousSampleSchema)
                {
                    arg.PreviousSampleSchema.MergeWith(new SampleSchema(completeHitsounds.CustomIndices));
                }

                if (arg.ShowResults)
                {
                    // Count the number of samples
                    int samples = completeHitsounds.CustomIndices.SelectMany(ci => ci.Samples.Values)
                                  .Count(h => h.Any(o =>
                                                    SampleImporter.ValidateSampleArgs(o, loadedSamples, validateSampleFile)));

                    // Count the number of changes of custom index
                    int greenlines = 0;
                    int lastIndex  = -1;
                    foreach (var hit in completeHitsounds.Hitsounds.Where(hit => hit.CustomIndex != lastIndex))
                    {
                        lastIndex = hit.CustomIndex;
                        greenlines++;
                    }

                    result = $"Number of sample indices: {completeHitsounds.CustomIndices.Count}, " +
                             $"Number of samples: {samples}, Number of greenlines: {greenlines}";
                }

                if (arg.DeleteAllInExportFirst && (arg.ExportSamples || arg.ExportMap))
                {
                    // Delete all files in the export folder before filling it again
                    DirectoryInfo di = new DirectoryInfo(arg.ExportFolder);
                    foreach (FileInfo file in di.GetFiles())
                    {
                        file.Delete();
                    }
                }

                UpdateProgressBar(worker, 70);

                // Export the hitsound map and sound samples
                if (arg.ExportMap)
                {
                    HitsoundExporter.ExportHitsounds(completeHitsounds.Hitsounds,
                                                     arg.BaseBeatmap, arg.ExportFolder, arg.HitsoundDiffName, arg.HitsoundExportGameMode, true, false);
                }

                UpdateProgressBar(worker, 80);

                if (arg.ExportSamples)
                {
                    HitsoundExporter.ExportCustomIndices(completeHitsounds.CustomIndices, arg.ExportFolder,
                                                         loadedSamples, arg.SingleSampleExportFormat, arg.MixedSampleExportFormat, comparer);
                }

                UpdateProgressBar(worker, 99);
            }
            else if (arg.HitsoundExportModeSetting == HitsoundStudioVm.HitsoundExportMode.Coinciding)
            {
                List <SamplePackage> samplePackages = HitsoundConverter.ZipLayers(arg.HitsoundLayers, arg.DefaultSample, 0, false);

                HitsoundConverter.BalanceVolumes(samplePackages, 0, false, true);
                UpdateProgressBar(worker, 20);

                Dictionary <SampleGeneratingArgs, SampleSoundGenerator> loadedSamples = null;
                Dictionary <SampleGeneratingArgs, string>  sampleNames     = arg.UsePreviousSampleSchema ? arg.PreviousSampleSchema?.GetSampleNames(comparer) : null;
                Dictionary <SampleGeneratingArgs, Vector2> samplePositions = null;
                var hitsounds = HitsoundConverter.GetHitsounds(samplePackages, ref loadedSamples, ref sampleNames, ref samplePositions,
                                                               arg.HitsoundExportGameMode == GameMode.Mania, arg.AddCoincidingRegularHitsounds, arg.AllowGrowthPreviousSampleSchema,
                                                               validateSampleFile, comparer);

                // Save current sample schema
                if (!arg.UsePreviousSampleSchema || arg.PreviousSampleSchema == null)
                {
                    arg.PreviousSampleSchema = new SampleSchema(sampleNames);
                }
                else if (arg.AllowGrowthPreviousSampleSchema)
                {
                    arg.PreviousSampleSchema.MergeWith(new SampleSchema(sampleNames));
                }

                // Load the samples so validation can be done
                UpdateProgressBar(worker, 50);

                if (arg.ShowResults)
                {
                    result = "Number of sample indices: 0, " +
                             $"Number of samples: {loadedSamples.Count}, Number of greenlines: 0";
                }

                if (arg.DeleteAllInExportFirst && (arg.ExportSamples || arg.ExportMap))
                {
                    // Delete all files in the export folder before filling it again
                    DirectoryInfo di = new DirectoryInfo(arg.ExportFolder);
                    foreach (FileInfo file in di.GetFiles())
                    {
                        file.Delete();
                    }
                }
                UpdateProgressBar(worker, 60);

                if (arg.ExportMap)
                {
                    HitsoundExporter.ExportHitsounds(hitsounds,
                                                     arg.BaseBeatmap, arg.ExportFolder, arg.HitsoundDiffName, arg.HitsoundExportGameMode, false, false);
                }
                UpdateProgressBar(worker, 70);

                if (arg.ExportSamples)
                {
                    HitsoundExporter.ExportLoadedSamples(loadedSamples, arg.ExportFolder, sampleNames, arg.SingleSampleExportFormat, comparer);
                }
            }
            else if (arg.HitsoundExportModeSetting == HitsoundStudioVm.HitsoundExportMode.Storyboard)
            {
                List <SamplePackage> samplePackages = HitsoundConverter.ZipLayers(arg.HitsoundLayers, arg.DefaultSample, 0, false);

                HitsoundConverter.BalanceVolumes(samplePackages, 0, false, true);
                UpdateProgressBar(worker, 20);

                Dictionary <SampleGeneratingArgs, SampleSoundGenerator> loadedSamples = null;
                Dictionary <SampleGeneratingArgs, string>  sampleNames     = arg.UsePreviousSampleSchema ? arg.PreviousSampleSchema?.GetSampleNames(comparer) : null;
                Dictionary <SampleGeneratingArgs, Vector2> samplePositions = null;
                var hitsounds = HitsoundConverter.GetHitsounds(samplePackages, ref loadedSamples, ref sampleNames, ref samplePositions,
                                                               false, false, arg.AllowGrowthPreviousSampleSchema, validateSampleFile, comparer);

                // Save current sample schema
                if (!arg.UsePreviousSampleSchema || arg.PreviousSampleSchema == null)
                {
                    arg.PreviousSampleSchema = new SampleSchema(sampleNames);
                }
                else if (arg.AllowGrowthPreviousSampleSchema)
                {
                    arg.PreviousSampleSchema.MergeWith(new SampleSchema(sampleNames));
                }

                // Load the samples so validation can be done
                UpdateProgressBar(worker, 50);

                if (arg.ShowResults)
                {
                    result = "Number of sample indices: 0, " +
                             $"Number of samples: {loadedSamples.Count}, Number of greenlines: 0";
                }

                if (arg.DeleteAllInExportFirst && (arg.ExportSamples || arg.ExportMap))
                {
                    // Delete all files in the export folder before filling it again
                    DirectoryInfo di = new DirectoryInfo(arg.ExportFolder);
                    foreach (FileInfo file in di.GetFiles())
                    {
                        file.Delete();
                    }
                }
                UpdateProgressBar(worker, 60);

                if (arg.ExportMap)
                {
                    HitsoundExporter.ExportHitsounds(hitsounds,
                                                     arg.BaseBeatmap, arg.ExportFolder, arg.HitsoundDiffName, arg.HitsoundExportGameMode, false, true);
                }
                UpdateProgressBar(worker, 70);

                if (arg.ExportSamples)
                {
                    HitsoundExporter.ExportLoadedSamples(loadedSamples, arg.ExportFolder, sampleNames, arg.SingleSampleExportFormat, comparer);
                }
            }

            // Open export folder
            if (arg.ExportSamples || arg.ExportMap)
            {
                System.Diagnostics.Process.Start("explorer.exe", arg.ExportFolder);
            }

            // Collect garbage
            GC.Collect();

            UpdateProgressBar(worker, 100);

            return(result);
        }