/// <summary> /// /// </summary> public CustomIndex(SampleGeneratingArgsComparer comparer = null) { Index = -1; Comparer = comparer ?? new SampleGeneratingArgsComparer(); Samples = new Dictionary <string, HashSet <SampleGeneratingArgs> >(); foreach (string key in AllKeys) { Samples[key] = new HashSet <SampleGeneratingArgs>(Comparer); } }
public static List <CustomIndex> GetCustomIndices(List <SamplePackage> packages, Dictionary <SampleGeneratingArgs, SampleSoundGenerator> loadedSamples = null, bool validateSampleFile = true, SampleGeneratingArgsComparer comparer = null) { var indices = packages.Select(o => o.GetCustomIndex(comparer)).ToList(); indices.ForEach(o => o.CleanInvalids(loadedSamples, validateSampleFile)); return(indices); }
public static void ExportSampleSchema(SampleSchema sampleSchema, string exportFolder, Dictionary <SampleGeneratingArgs, SampleSoundGenerator> loadedSamples = null, SampleExportFormat format = SampleExportFormat.Default, SampleExportFormat mixedFormat = SampleExportFormat.Default, SampleGeneratingArgsComparer comparer = null) { foreach (var kvp in sampleSchema) { ExportMixedSample(kvp.Value, kvp.Key, exportFolder, loadedSamples, format, mixedFormat, comparer); } }
public CustomIndex GetCustomIndex(SampleGeneratingArgsComparer comparer = null) { if (comparer == null) { comparer = new SampleGeneratingArgsComparer(); } SampleSet sampleSet = GetSampleSet(); SampleSet additions = GetAdditions(); HashSet <SampleGeneratingArgs> normals = new HashSet <SampleGeneratingArgs>(Samples.Where(o => o.Hitsound == Hitsound.Normal).Select(o => o.SampleArgs), comparer); HashSet <SampleGeneratingArgs> whistles = new HashSet <SampleGeneratingArgs>(Samples.Where(o => o.Hitsound == Hitsound.Whistle).Select(o => o.SampleArgs), comparer); HashSet <SampleGeneratingArgs> finishes = new HashSet <SampleGeneratingArgs>(Samples.Where(o => o.Hitsound == Hitsound.Finish).Select(o => o.SampleArgs), comparer); HashSet <SampleGeneratingArgs> claps = new HashSet <SampleGeneratingArgs>(Samples.Where(o => o.Hitsound == Hitsound.Clap).Select(o => o.SampleArgs), comparer); CustomIndex ci = new CustomIndex(comparer); if (sampleSet == SampleSet.Normal) { ci.Samples["normal-hitnormal"] = normals; } else if (sampleSet == SampleSet.Drum) { ci.Samples["drum-hitnormal"] = normals; } else { ci.Samples["soft-hitnormal"] = normals; } if (additions == SampleSet.Normal) { ci.Samples["normal-hitwhistle"] = whistles; ci.Samples["normal-hitfinish"] = finishes; ci.Samples["normal-hitclap"] = claps; } else if (additions == SampleSet.Drum) { ci.Samples["drum-hitwhistle"] = whistles; ci.Samples["drum-hitfinish"] = finishes; ci.Samples["drum-hitclap"] = claps; } else { ci.Samples["soft-hitwhistle"] = whistles; ci.Samples["soft-hitfinish"] = finishes; ci.Samples["soft-hitclap"] = claps; } return(ci); }
/// <summary> /// Generates a dictionary which maps <see cref="SampleGeneratingArgs"/> to their corresponding filename which makes that sample sound. /// Only maps the <see cref="SampleGeneratingArgs"/> which are non-mixed. /// </summary> /// <returns></returns> public Dictionary <SampleGeneratingArgs, string> GetSampleNames(SampleGeneratingArgsComparer comparer = null) { var sampleNames = new Dictionary <SampleGeneratingArgs, string>(comparer ?? new SampleGeneratingArgsComparer()); foreach (var kvp in this.Where(kvp => kvp.Value.Count == 1)) { if (!sampleNames.ContainsKey(kvp.Value[0])) { sampleNames.Add(kvp.Value[0], kvp.Key); } } return(sampleNames); }
public List <CustomIndex> GetCustomIndices(SampleGeneratingArgsComparer comparer = null) { if (comparer == null) { comparer = new SampleGeneratingArgsComparer(); } var customIndices = new Dictionary <int, CustomIndex>(); foreach (var kvp in this) { var name = Path.GetFileNameWithoutExtension(kvp.Key); if (name == null) { continue; } var match = Regex.Match(name, "^(normal|soft|drum)-hit(normal|whistle|finish|clap)"); if (!match.Success) { continue; } var hitsound = match.Value; var remainder = name.Substring(match.Index + match.Length); int index = 1; if (!string.IsNullOrEmpty(remainder)) { if (!FileFormatHelper.TryParseInt(remainder, out index)) { continue; } } if (customIndices.ContainsKey(index)) { customIndices[index].Samples[hitsound] = new HashSet <SampleGeneratingArgs>(kvp.Value); } else { var ci = new CustomIndex(index, comparer); customIndices.Add(index, ci); ci.Samples[hitsound] = new HashSet <SampleGeneratingArgs>(kvp.Value, comparer); } } return(customIndices.Values.ToList()); }
/// <summary> /// Exports all samples for a collection of custom indices. /// </summary> /// <param name="customIndices"></param> /// <param name="exportFolder"></param> /// <param name="loadedSamples"></param> /// <param name="format"></param> /// <param name="mixedFormat"></param> /// <param name="comparer"></param> public static void ExportCustomIndices(List <CustomIndex> customIndices, string exportFolder, Dictionary <SampleGeneratingArgs, SampleSoundGenerator> loadedSamples = null, SampleExportFormat format = SampleExportFormat.Default, SampleExportFormat mixedFormat = SampleExportFormat.Default, SampleGeneratingArgsComparer comparer = null) { foreach (CustomIndex ci in customIndices) { foreach (KeyValuePair <string, HashSet <SampleGeneratingArgs> > kvp in ci.Samples) { if (kvp.Value.Count == 0) { continue; } string filename = ci.Index == 1 ? kvp.Key : kvp.Key + ci.Index; ExportMixedSample(kvp.Value, filename, exportFolder, loadedSamples, format, mixedFormat, comparer); } } }
public static CompleteHitsounds GetCompleteHitsounds(List <SamplePackage> packages, Dictionary <SampleGeneratingArgs, SampleSoundGenerator> loadedSamples = null, List <CustomIndex> customIndices = null, bool allowGrowth = false, int firstCustomIndex = 1, bool validateSampleFile = true, SampleGeneratingArgsComparer comparer = null) { if (customIndices == null) { customIndices = OptimizeCustomIndices(GetCustomIndices(packages, loadedSamples, validateSampleFile, comparer)); GiveCustomIndicesIndices(customIndices, false, firstCustomIndex); } else if (allowGrowth) { customIndices = OptimizeCustomIndices(customIndices.Concat(GetCustomIndices(packages, loadedSamples, validateSampleFile, comparer)).ToList()); GiveCustomIndicesIndices(customIndices, true, firstCustomIndex); } var hitsounds = GetHitsounds(packages, customIndices, loadedSamples, validateSampleFile, comparer); return(new CompleteHitsounds(hitsounds, customIndices)); }
/// <summary> /// Generates 1-to-1 <see cref="HitsoundEvent"/> of out <see cref="SamplePackage"/> using provided custom indices. /// </summary> /// <param name="samplePackages">The SamplePackages to get hitsounds out of</param> /// <param name="customIndices">The CustomIndices that fit all the packages</param> /// <param name="loadedSamples">Loaded samples for the validation of samples files from the sample packages.</param> /// <param name="validateSampleFile">Whether to validate sample files from the sample packages.</param> /// <param name="comparer">Comparer for <see cref="SampleGeneratingArgs"/></param> /// <returns></returns> public static List <HitsoundEvent> GetHitsounds(List <SamplePackage> samplePackages, List <CustomIndex> customIndices, Dictionary <SampleGeneratingArgs, SampleSoundGenerator> loadedSamples = null, bool validateSampleFile = true, SampleGeneratingArgsComparer comparer = null) { List <HitsoundEvent> hitsounds = new List <HitsoundEvent>(samplePackages.Count); List <CustomIndex> packageCustomIndices = GetCustomIndices(samplePackages, loadedSamples, validateSampleFile, comparer); int index = 0; while (index < packageCustomIndices.Count) { // Find CustomIndex that fits the most packages from here CustomIndex bestCustomIndex = null; int bestFits = 0; foreach (CustomIndex ci in customIndices) { int fits = NumSupportedPackages(packageCustomIndices, index, ci); if (fits <= bestFits) { continue; } bestCustomIndex = ci; bestFits = fits; } if (bestFits == 0) { throw new Exception("Custom indices can't fit the sample packages.\n" + "Maybe you are using an incompatible previous sample schema and growth is disabled."); } // Add all the fitted packages as hitsounds for (int i = 0; i < bestFits; i++) { if (bestCustomIndex != null) { hitsounds.Add(samplePackages[index + i].GetHitsound(bestCustomIndex.Index)); } } index += bestFits; } return(hitsounds); }
public static List <HitsoundEvent> GetHitsounds(List <SamplePackage> samplePackages, ref Dictionary <SampleGeneratingArgs, SampleSoundGenerator> loadedSamples, ref Dictionary <SampleGeneratingArgs, string> names, ref Dictionary <SampleGeneratingArgs, Vector2> positions, bool maniaPositions = false, bool includeRegularHitsounds = true, bool allowNamingGrowth = false, bool validateSampleFile = true, SampleGeneratingArgsComparer comparer = null) { if (comparer == null) { comparer = new SampleGeneratingArgsComparer(); } HashSet <SampleGeneratingArgs> allSampleArgs = new HashSet <SampleGeneratingArgs>(comparer); foreach (SamplePackage sp in samplePackages) { allSampleArgs.UnionWith(sp.Samples.Select(o => o.SampleArgs)); } if (loadedSamples == null) { loadedSamples = SampleImporter.ImportSamples(allSampleArgs, comparer); } if (names == null) { names = HitsoundExporter.GenerateSampleNames(allSampleArgs, loadedSamples, validateSampleFile, comparer); } if (positions == null) { positions = maniaPositions ? HitsoundExporter.GenerateManiaHitsoundPositions(allSampleArgs, comparer) : HitsoundExporter.GenerateHitsoundPositions(allSampleArgs, comparer); } var hitsounds = new List <HitsoundEvent>(); foreach (var p in samplePackages) { foreach (var s in p.Samples) { string filename; if (names.ContainsKey(s.SampleArgs)) { filename = names[s.SampleArgs]; } else { // Validate the sample because we expect only valid samples to be present in the sample schema if (SampleImporter.ValidateSampleArgs(s.SampleArgs, loadedSamples, validateSampleFile)) { if (allowNamingGrowth) { HitsoundExporter.AddNewSampleName(names, s.SampleArgs, loadedSamples); filename = names[s.SampleArgs]; } else { throw new Exception($"Given sample schema doesn't support sample ({s.SampleArgs}) and growth is disabled."); } } else { filename = string.Empty; } } if (includeRegularHitsounds) { hitsounds.Add(new HitsoundEvent(p.Time, positions[s.SampleArgs], s.OutsideVolume, filename, s.SampleSet, s.SampleSet, 0, s.Whistle, s.Finish, s.Clap)); } else { hitsounds.Add(new HitsoundEvent(p.Time, positions[s.SampleArgs], s.OutsideVolume, filename, SampleSet.Auto, SampleSet.Auto, 0, false, false, false)); } } } return(hitsounds); }
/// <summary> /// Imports all samples specified by <see cref="SampleGeneratingArgs"/> and returns a dictionary which maps the <see cref="SampleGeneratingArgs"/> /// to their <see cref="SampleSoundGenerator"/>. If a sample couldn't be imported then it has a null instead. /// </summary> /// <param name="argsList"></param> /// <returns></returns> public static Dictionary <SampleGeneratingArgs, SampleSoundGenerator> ImportSamples(IEnumerable <SampleGeneratingArgs> argsList, SampleGeneratingArgsComparer comparer = null) { if (comparer == null) { comparer = new SampleGeneratingArgsComparer(); } var samples = new Dictionary <SampleGeneratingArgs, SampleSoundGenerator>(comparer); var separatedByPath = new Dictionary <string, HashSet <SampleGeneratingArgs> >(); foreach (var args in argsList) { if (separatedByPath.TryGetValue(args.Path, out HashSet <SampleGeneratingArgs> value)) { value.Add(args); } else { separatedByPath.Add(args.Path, new HashSet <SampleGeneratingArgs>(comparer) { args }); } } foreach (var pair in separatedByPath) { var path = pair.Key; if (!ValidateSamplePath(path)) { foreach (var args in pair.Value) { samples.Add(args, null); } continue; } try { switch (Path.GetExtension(path)) { case ".sf2": { var sf2 = new SoundFont(path); foreach (var args in pair.Value) { var sample = ImportFromSoundFont(args, sf2); samples.Add(args, sample); } break; } case ".ogg": { foreach (var args in pair.Value) { samples.Add(args, ImportFromVorbis(args)); } break; } default: { foreach (var args in pair.Value) { samples.Add(args, ImportFromAudio(args)); } break; } } } catch (Exception ex) { Console.WriteLine(ex.Message); foreach (var args in pair.Value) { samples.Add(args, null); } } GC.Collect(); } return(samples); }
public static void ExportLoadedSamples(Dictionary <SampleGeneratingArgs, SampleSoundGenerator> loadedSamples, string exportFolder, Dictionary <SampleGeneratingArgs, string> names = null, SampleExportFormat format = SampleExportFormat.Default, SampleGeneratingArgsComparer comparer = null) { if (names == null) { names = GenerateSampleNames(loadedSamples.Keys, loadedSamples, format != SampleExportFormat.MidiChords, comparer); } foreach (var sample in loadedSamples.Keys.Where(sample => SampleImporter.ValidateSampleArgs(sample, loadedSamples, format != SampleExportFormat.MidiChords))) { ExportSample(sample, names[sample], exportFolder, loadedSamples, format); } }
public static Dictionary <SampleGeneratingArgs, Vector2> GenerateManiaHitsoundPositions(IEnumerable <SampleGeneratingArgs> samples, SampleGeneratingArgsComparer comparer = null) { var sampleArray = samples.ToArray(); var sampleCount = sampleArray.Length; // One key per unique sample but clamped between 1 and 18 int numKeys = MathHelper.Clamp(sampleCount, 1, 18); var positions = new Dictionary <SampleGeneratingArgs, Vector2>(comparer ?? new SampleGeneratingArgsComparer()); double x = 256d / numKeys; foreach (var sample in sampleArray) { positions.Add(sample, new Vector2(Math.Round(x), 192)); x += 512d / numKeys; if (x > 512) { x = 256d / numKeys; } } return(positions); }
public static Dictionary <SampleGeneratingArgs, Vector2> GenerateHitsoundPositions(IEnumerable <SampleGeneratingArgs> samples, SampleGeneratingArgsComparer comparer = null) { var sampleArray = samples.ToArray(); var sampleCount = sampleArray.Length; // Find the biggest spacing that will still fit all the samples int spacingX = 128; int spacingY = 128; bool reduceX = false; while ((int)(512d / spacingX + 1) * (int)(384d / spacingY + 1) < sampleCount && spacingX > 1) { reduceX = !reduceX; if (reduceX) { spacingX /= 2; } else { spacingY /= 2; } } var positions = new Dictionary <SampleGeneratingArgs, Vector2>(comparer ?? new SampleGeneratingArgsComparer()); int x = 0; int y = 0; foreach (var sample in sampleArray) { positions.Add(sample, new Vector2(x, y)); x += spacingX; if (x > 512) { x = 0; y += spacingY; if (y > 384) { y = 0; } } } return(positions); }
public static Dictionary <SampleGeneratingArgs, string> GenerateSampleNames(IEnumerable <SampleGeneratingArgs> samples, Dictionary <SampleGeneratingArgs, SampleSoundGenerator> loadedSamples, bool validateSampleFile = true, SampleGeneratingArgsComparer comparer = null) { var usedNames = new HashSet <string>(); var sampleNames = new Dictionary <SampleGeneratingArgs, string>(comparer ?? new SampleGeneratingArgsComparer()); foreach (var sample in samples) { if (!SampleImporter.ValidateSampleArgs(sample, loadedSamples, validateSampleFile)) { sampleNames[sample] = string.Empty; continue; } var baseName = sample.GetFilename(); var name = baseName; int i = 1; while (usedNames.Contains(name)) { name = baseName + "-" + ++i; } usedNames.Add(name); sampleNames[sample] = name; } return(sampleNames); }
public static void ExportMixedSample(IEnumerable <SampleGeneratingArgs> sampleGeneratingArgses, string name, string exportFolder, Dictionary <SampleGeneratingArgs, SampleSoundGenerator> loadedSamples = null, SampleExportFormat format = SampleExportFormat.Default, SampleExportFormat mixedFormat = SampleExportFormat.Default, SampleGeneratingArgsComparer comparer = null) { // Export as midi file with single chord if (format == SampleExportFormat.MidiChords) { MidiExporter.SaveToFile(Path.Combine(exportFolder, name + ".mid"), sampleGeneratingArgses.ToArray()); return; } // Try loading all the valid samples var validLoadedSamples = new Dictionary <SampleGeneratingArgs, SampleSoundGenerator>(comparer ?? new SampleGeneratingArgsComparer()); if (loadedSamples != null) { foreach (var args in sampleGeneratingArgses) { if (!SampleImporter.ValidateSampleArgs(args, loadedSamples)) { continue; } var sample = loadedSamples[args]; validLoadedSamples.Add(args, sample); } } else { // Import each sample individually foreach (SampleGeneratingArgs args in sampleGeneratingArgses) { try { var sample = SampleImporter.ImportSample(args); validLoadedSamples.Add(args, sample); } catch (Exception ex) { Console.WriteLine($@"{ex.Message} while importing sample {args}."); } } } if (validLoadedSamples.Count == 0) { return; } // If all the valid samples are blank samples, then also export only a single blank sample if (validLoadedSamples.Count == 1 || validLoadedSamples.All(o => o.Value.BlankSample)) { // It has only one valid sample, so we can just export it with the single sample export ExportSample(validLoadedSamples.Keys.First(), name, exportFolder, loadedSamples, format); } else if (validLoadedSamples.Count > 1) { // Synchronize the sample rate and channels for all samples and get the sample providers int maxSampleRate = validLoadedSamples.Values.Max(o => o.Wave.WaveFormat.SampleRate); int maxChannels = validLoadedSamples.Values.Max(o => o.Wave.WaveFormat.Channels); // Resample to a supported sample rate when exporting in vorbis format if (mixedFormat == SampleExportFormat.OggVorbis) { maxSampleRate = VorbisFileWriter.GetSupportedSampleRate(maxSampleRate); } IEnumerable <ISampleProvider> sameFormatSamples = validLoadedSamples.Select(o => (ISampleProvider) new WdlResamplingSampleProvider(SampleImporter.SetChannels(o.Value.GetSampleProvider(), maxChannels), maxSampleRate)); ISampleProvider sampleProvider = new MixingSampleProvider(sameFormatSamples); // If the input is Ieee float or you are mixing multiple samples, then clipping is possible, // so you can either export as IEEE float or use a compressor and export as 16-bit PCM (half filesize) or Vorbis (even smaller filesize) // If the input is only The Blank Sample then it should export The Blank Sample if (mixedFormat == SampleExportFormat.WavePcm || mixedFormat == SampleExportFormat.OggVorbis) { // When the sample is mixed and the export format is PCM or Vorbis, then clipping is possible, so we add a limiter sampleProvider = new SoftLimiter(sampleProvider); } switch (mixedFormat) { case SampleExportFormat.WaveIeeeFloat: CreateWaveFile(Path.Combine(exportFolder, name + ".wav"), sampleProvider.ToWaveProvider()); break; case SampleExportFormat.WavePcm: CreateWaveFile(Path.Combine(exportFolder, name + ".wav"), sampleProvider.ToWaveProvider16()); break; case SampleExportFormat.OggVorbis: VorbisFileWriter.CreateVorbisFile(Path.Combine(exportFolder, name + ".ogg"), sampleProvider.ToWaveProvider()); break; default: CreateWaveFile(Path.Combine(exportFolder, name + ".wav"), sampleProvider.ToWaveProvider()); break; } } }