예제 #1
0
 public Sample(HitsoundLayer hl)
 {
     _sampleArgs = hl.SampleArgs.Copy();
     _priority   = hl.Priority;
     _sampleSet  = hl.SampleSet;
     _hitsound   = hl.Hitsound;
 }
예제 #2
0
        private static List <HitsoundLayer> ImportStoryboard(string path, bool volumes, bool removeDuplicates, Beatmap beatmap, string mapDir, string prefix = null)
        {
            var hitsoundLayers = new List <HitsoundLayer>();

            prefix = prefix ?? string.Empty;

            foreach (var sbSample in beatmap.StoryboardSoundSamples)
            {
                var    filepath   = sbSample.FilePath;
                string samplePath = Path.Combine(mapDir, filepath);
                var    filename   = Path.GetFileNameWithoutExtension(filepath);

                var volume = volumes ? sbSample.Volume : 1;

                SampleSet sampleSet = GetSamplesetFromFilename(filename);
                Hitsound  hitsound  = GetHitsoundFromFilename(filename);

                var importArgs = new LayerImportArgs(ImportType.Storyboard)
                {
                    Path = path, SamplePath = samplePath, Volume = volume, DiscriminateVolumes = volumes, RemoveDuplicates = removeDuplicates
                };

                // Find the hitsoundlayer with this path
                HitsoundLayer layer = hitsoundLayers.Find(o => o.ImportArgs == importArgs);

                if (layer != null)
                {
                    // Find hitsound layer with this path and add this time
                    layer.Times.Add(sbSample.StartTime);
                }
                else
                {
                    // Add new hitsound layer with this path
                    HitsoundLayer newLayer = new HitsoundLayer(prefix + filename,
                                                               sampleSet,
                                                               hitsound,
                                                               new SampleGeneratingArgs(samplePath)
                    {
                        Volume = volume
                    },
                                                               importArgs);

                    newLayer.Times.Add(sbSample.StartTime);

                    hitsoundLayers.Add(newLayer);
                }
            }

            if (removeDuplicates)
            {
                foreach (var hitsoundLayer in hitsoundLayers)
                {
                    hitsoundLayer.Times.Sort();
                    hitsoundLayer.RemoveDuplicates();
                }
            }

            return(hitsoundLayers);
        }
예제 #3
0
        public static HitsoundLayer ImportStack(string path, double x, double y)
        {
            HitsoundLayer layer = new HitsoundLayer
            {
                ImportArgs = { ImportType = ImportType.Stack, Path = path, X = x, Y = y },
                Times      = TimesFromStack(path, x, y)
            };

            return(layer);
        }
예제 #4
0
        public static List <HitsoundLayer> ImportMidi(string path, double offset = 0, bool instruments = true, bool keysounds = true, bool lengths = true, double lengthRoughness = 1, bool velocities = true, double velocityRoughness = 1)
        {
            List <HitsoundLayer> hitsoundLayers = new List <HitsoundLayer>();

            var strictMode = false;
            var mf         = new MidiFile(path, strictMode);

            Console.WriteLine(
                $@"Format {mf.FileFormat}, " +
                $@"Tracks {mf.Tracks}, " +
                $@"Delta Ticks Per Quarter Note {mf.DeltaTicksPerQuarterNote}");

            List <TempoEvent> tempos = new List <TempoEvent>();

            foreach (var track in mf.Events)
            {
                tempos.AddRange(track.OfType <TempoEvent>());
            }
            tempos = tempos.OrderBy(o => o.AbsoluteTime).ToList();

            List <double> cumulativeTime = CalculateCumulativeTime(tempos, mf.DeltaTicksPerQuarterNote);

            Dictionary <int, int> channelBanks   = new Dictionary <int, int>();
            Dictionary <int, int> channelPatches = new Dictionary <int, int>();

            // Loop through every event of every track
            for (int track = 0; track < mf.Tracks; track++)
            {
                foreach (var midiEvent in mf.Events[track])
                {
                    if (midiEvent is PatchChangeEvent pc)
                    {
                        channelPatches[pc.Channel] = pc.Patch;
                    }
                    else if (midiEvent is ControlChangeEvent co)
                    {
                        if (co.Controller == MidiController.BankSelect)
                        {
                            channelBanks[co.Channel] = (co.ControllerValue * 128) + (channelBanks.ContainsKey(co.Channel) ? (byte)channelBanks[co.Channel] : 0);
                        }
                        else if (co.Controller == MidiController.BankSelectLsb)
                        {
                            channelBanks[co.Channel] = co.ControllerValue + (channelBanks.ContainsKey(co.Channel) ? channelBanks[co.Channel] >> 8 * 128 : 0);
                        }
                    }
                    else if (MidiEvent.IsNoteOn(midiEvent))
                    {
                        var on = midiEvent as NoteOnEvent;

                        double time   = CalculateTime(on.AbsoluteTime, tempos, cumulativeTime, mf.DeltaTicksPerQuarterNote);
                        double length = on.OffEvent != null
                            ? CalculateTime(on.OffEvent.AbsoluteTime,
                                            tempos,
                                            cumulativeTime,
                                            mf.DeltaTicksPerQuarterNote) -
                                        time
                            : -1;

                        length = RoundLength(length, lengthRoughness);

                        bool keys = keysounds || on.Channel == 10;

                        int bank = instruments
                            ? on.Channel == 10 ? 128 :
                                   channelBanks.ContainsKey(on.Channel) ? channelBanks[on.Channel] : 0
                            : -1;
                        int patch = instruments && channelPatches.ContainsKey(on.Channel)
                            ? channelPatches[on.Channel]
                            : -1;
                        int instrument = -1;
                        int key        = keys ? on.NoteNumber : -1;
                        length = lengths ? length : -1;
                        int velocity = velocities ? on.Velocity : -1;
                        velocity = (int)RoundVelocity(velocity, velocityRoughness);

                        string lengthString = Math.Round(length).ToString(CultureInfo.InvariantCulture);
                        string filename     = $"{bank}\\{patch}\\{instrument}\\{key}\\{lengthString}\\{velocity}.wav";

                        string instrumentName = on.Channel == 10 ? "Percussion" :
                                                patch >= 0 && patch <= 127 ? PatchChangeEvent.GetPatchName(patch) : "Undefined";
                        string keyName = on.NoteName;

                        string name = instrumentName;
                        if (keysounds)
                        {
                            name += "," + keyName;
                        }
                        if (lengths)
                        {
                            name += "," + lengthString;
                        }
                        if (velocities)
                        {
                            name += "," + velocity;
                        }


                        var sampleArgs = new SampleGeneratingArgs(filename, bank, patch, instrument, key, length, velocity);
                        var importArgs = new LayerImportArgs(ImportType.MIDI)
                        {
                            Path              = path,
                            Bank              = bank,
                            Patch             = patch,
                            Key               = key,
                            Length            = length,
                            LengthRoughness   = lengthRoughness,
                            Velocity          = velocity,
                            VelocityRoughness = velocityRoughness
                        };

                        // Find the hitsoundlayer with this path
                        HitsoundLayer layer = hitsoundLayers.Find(o => o.ImportArgs == importArgs);

                        if (layer != null)
                        {
                            // Find hitsound layer with this path and add this time
                            layer.Times.Add(time + offset);
                        }
                        else
                        {
                            // Add new hitsound layer with this path
                            HitsoundLayer newLayer = new HitsoundLayer(name, SampleSet.Normal, Hitsound.Normal, sampleArgs, importArgs);
                            newLayer.Times.Add(time + offset);

                            hitsoundLayers.Add(newLayer);
                        }
                    }
                }
            }
            // Stretch the velocities to reach 127
            int maxVelocity = hitsoundLayers.Max(o => o.SampleArgs.Velocity);

            foreach (var hsl in hitsoundLayers)
            {
                hsl.SampleArgs.Velocity = (int)Math.Round(hsl.SampleArgs.Velocity / (float)maxVelocity * 127);
            }

            // Sort the times
            hitsoundLayers.ForEach(o => o.Times = o.Times.OrderBy(t => t).ToList());

            // Sort layers by name
            hitsoundLayers = hitsoundLayers.OrderBy(o => o.Name).ToList();

            return(hitsoundLayers);
        }
예제 #5
0
        /// <summary>
        /// Extract every used sample in a beatmap and return them as hitsound layers.
        /// </summary>
        /// <param name="path">The path to the beatmap.</param>
        /// <param name="volumes">Taking the volumes from the map and making different layers for different volumes.</param>
        /// <param name="detectDuplicateSamples">Detect duplicate samples and optimise hitsound layer count with that.</param>
        /// <param name="removeDuplicates">Removes duplicate sounds at the same millisecond.</param>
        /// <param name="includeStoryboard">Also imports storyboarded samples.</param>
        /// <returns>The hitsound layers</returns>
        public static List <HitsoundLayer> ImportHitsounds(string path, bool volumes, bool detectDuplicateSamples, bool removeDuplicates, bool includeStoryboard)
        {
            var      editor   = EditorReaderStuff.GetNewestVersionOrNot(path);
            Beatmap  beatmap  = editor.Beatmap;
            Timeline timeline = beatmap.GetTimeline();

            GameMode mode   = (GameMode)beatmap.General["Mode"].IntValue;
            string   mapDir = editor.GetParentFolder();
            Dictionary <string, string> firstSamples = AnalyzeSamples(mapDir, false, detectDuplicateSamples);

            List <HitsoundLayer> hitsoundLayers = new List <HitsoundLayer>();

            foreach (TimelineObject tlo in timeline.TimelineObjects)
            {
                if (!tlo.HasHitsound)
                {
                    continue;
                }

                double volume = volumes ? tlo.FenoSampleVolume / 100 : 1;

                List <string> samples = tlo.GetPlayingFilenames(mode);

                foreach (string filename in samples)
                {
                    bool isFilename = tlo.UsesFilename;

                    SampleSet sampleSet = isFilename ? tlo.FenoSampleSet : GetSamplesetFromFilename(filename);
                    Hitsound  hitsound  = isFilename ? tlo.GetHitsound() : GetHitsoundFromFilename(filename);

                    string samplePath      = Path.Combine(mapDir, filename);
                    string fullPathExtLess = Path.Combine(
                        Path.GetDirectoryName(samplePath) ?? throw new InvalidOperationException(),
                        Path.GetFileNameWithoutExtension(samplePath));

                    // Get the first occurence of this sound to not get duplicated
                    if (firstSamples.Keys.Contains(fullPathExtLess))
                    {
                        samplePath = firstSamples[fullPathExtLess];
                    }
                    else
                    {
                        // Sample doesn't exist
                        if (!isFilename)
                        {
                            samplePath = Path.Combine(
                                Path.GetDirectoryName(samplePath) ?? throw new InvalidOperationException(),
                                $"{sampleSet.ToString().ToLower()}-hit{hitsound.ToString().ToLower()}-1.wav");
                        }
                    }

                    string extLessFilename = Path.GetFileNameWithoutExtension(samplePath);
                    var    importArgs      = new LayerImportArgs(ImportType.Hitsounds)
                    {
                        Path   = path, SamplePath = samplePath,
                        Volume = volume, DetectDuplicateSamples = detectDuplicateSamples, DiscriminateVolumes = volumes, RemoveDuplicates = removeDuplicates
                    };

                    // Find the hitsoundlayer with this path
                    HitsoundLayer layer = hitsoundLayers.Find(o => o.ImportArgs == importArgs);

                    if (layer != null)
                    {
                        // Find hitsound layer with this path and add this time
                        layer.Times.Add(tlo.Time);
                    }
                    else
                    {
                        // Add new hitsound layer with this path
                        HitsoundLayer newLayer = new HitsoundLayer(extLessFilename,
                                                                   sampleSet,
                                                                   hitsound,
                                                                   new SampleGeneratingArgs(samplePath)
                        {
                            Volume = volume
                        },
                                                                   importArgs);
                        newLayer.Times.Add(tlo.Time);

                        hitsoundLayers.Add(newLayer);
                    }
                }
            }

            if (includeStoryboard)
            {
                hitsoundLayers.AddRange(ImportStoryboard(path, volumes, removeDuplicates, beatmap, mapDir, "SB: "));
            }

            // Sort layers by name
            hitsoundLayers = hitsoundLayers.OrderBy(o => o.Name).ToList();

            if (removeDuplicates)
            {
                foreach (var hitsoundLayer in hitsoundLayers)
                {
                    hitsoundLayer.Times.Sort();
                    hitsoundLayer.RemoveDuplicates();
                }
            }

            return(hitsoundLayers);
        }