public Sample(HitsoundLayer hl) { _sampleArgs = hl.SampleArgs.Copy(); _priority = hl.Priority; _sampleSet = hl.SampleSet; _hitsound = hl.Hitsound; }
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); }
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); }
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); }
/// <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); }