public static bool ExportSample(SampleGeneratingArgs sampleGeneratingArgs, string name,
                                        string exportFolder, Dictionary <SampleGeneratingArgs, SampleSoundGenerator> loadedSamples = null)
        {
            SampleSoundGenerator sampleSoundGenerator;

            if (loadedSamples != null)
            {
                if (SampleImporter.ValidateSampleArgs(sampleGeneratingArgs, loadedSamples))
                {
                    sampleSoundGenerator = loadedSamples[sampleGeneratingArgs];
                }
                else
                {
                    return(false);
                }
            }
            else
            {
                try {
                    sampleSoundGenerator = SampleImporter.ImportSample(sampleGeneratingArgs);
                } catch (Exception ex) {
                    Console.WriteLine($@"{ex.Message} while importing sample {sampleGeneratingArgs}.");
                    return(false);
                }
            }

            // TODO: Allow mp3, ogg and aif export.
            string filename = name + ".wav";

            CreateWaveFile(Path.Combine(exportFolder, filename), sampleSoundGenerator.GetSampleProvider().ToWaveProvider16());

            return(true);
        }
Beispiel #2
0
 public Sample(HitsoundLayer hl)
 {
     _sampleArgs = hl.SampleArgs.Copy();
     _priority   = hl.Priority;
     _sampleSet  = hl.SampleSet;
     _hitsound   = hl.Hitsound;
 }
Beispiel #3
0
 public Sample(SampleSet sampleSet, Hitsound hitsound, SampleGeneratingArgs sampleArgs, int priority)
 {
     _sampleArgs = sampleArgs;
     _priority   = priority;
     _sampleSet  = sampleSet;
     _hitsound   = hitsound;
 }
Beispiel #4
0
 public Sample()
 {
     _sampleArgs = new SampleGeneratingArgs();
     _priority   = 0;
     _sampleSet  = SampleSet.Normal;
     _hitsound   = Hitsound.Normal;
 }
Beispiel #5
0
 public static bool ValidateSampleArgs(SampleGeneratingArgs args, Dictionary <SampleGeneratingArgs, SampleSoundGenerator> loadedSamples)
 {
     if (loadedSamples == null)
     {
         return(ValidateSampleArgs(args));
     }
     return(loadedSamples.ContainsKey(args) && loadedSamples[args] != null);
 }
Beispiel #6
0
        public static SampleSoundGenerator ImportFromVorbis(SampleGeneratingArgs args)
        {
            var generator = new SampleSoundGenerator(new VorbisWaveReader(args.Path))
            {
                VolumeCorrection = (float)args.Volume
            };

            return(generator);
        }
Beispiel #7
0
        private static bool IsCopyCompatible(SampleGeneratingArgs sampleGeneratingArgs, WaveFormatEncoding waveEncoding, SampleExportFormat exportFormat)
        {
            switch (exportFormat)
            {
            case SampleExportFormat.WaveIeeeFloat:
                return(waveEncoding == WaveFormatEncoding.IeeeFloat && sampleGeneratingArgs.GetExtension() == ".wav");

            case SampleExportFormat.WavePcm:
                return(waveEncoding == WaveFormatEncoding.Pcm && sampleGeneratingArgs.GetExtension() == ".wav");

            case SampleExportFormat.OggVorbis:
                return(sampleGeneratingArgs.GetExtension() == ".ogg");

            default:
                return(true);
            }
        }
Beispiel #8
0
        private static Zone ImportInstrument(Instrument i, SampleGeneratingArgs args)
        {
            Zone closest = null;
            int  bdist   = int.MaxValue;

            // an Instrument contains a set of zones that contain sample headers.
            foreach (var instrumentZone in i.Zones)
            {
                var sh = instrumentZone.SampleHeader();
                if (sh == null)
                {
                    continue;
                }

                // Requested key/velocity must also fit in the key/velocity range of the sample
                ushort keyRange = instrumentZone.KeyRange();
                byte   keyLow   = (byte)keyRange;
                byte   keyHigh  = (byte)(keyRange >> 8);
                if (!(args.Key >= keyLow && args.Key <= keyHigh) && args.Key != -1 && keyRange != 0)
                {
                    continue;
                }
                ushort velRange = instrumentZone.VelocityRange();
                byte   velLow   = (byte)keyRange;
                byte   velHigh  = (byte)(keyRange >> 8);
                if (!(args.Velocity >= velLow && args.Velocity <= velHigh) && args.Velocity != -1 && velRange != 0)
                {
                    continue;
                }

                // Get the closest key possible
                int dist = Math.Abs(args.Key - instrumentZone.Key());

                if (dist < bdist || args.Key == -1)
                {
                    closest = instrumentZone;
                    bdist   = dist;
                }
            }

            return(closest);
        }
        public static SampleSoundGenerator ImportSample(SampleGeneratingArgs args)
        {
            string path = args.Path;

            if (Path.GetExtension(path) == ".sf2")
            {
                SoundFont            sf2  = new SoundFont(path);
                SampleSoundGenerator wave = ImportFromSoundFont(args, sf2);
                GC.Collect();
                return(wave);
            }
            else if (Path.GetExtension(path) == ".ogg")
            {
                return(ImportFromVorbis(args));
            }
            else
            {
                return(ImportFromAudio(args));
            }
        }
Beispiel #10
0
        public static SampleSoundGenerator ImportSample(SampleGeneratingArgs args)
        {
            string path = args.Path;

            switch (Path.GetExtension(path))
            {
            case ".sf2": {
                SoundFont            sf2  = new SoundFont(path);
                SampleSoundGenerator wave = ImportFromSoundFont(args, sf2);
                GC.Collect();
                return(wave);
            }

            case ".ogg":
                return(ImportFromVorbis(args));

            default:
                return(ImportFromAudio(args));
            }
        }
Beispiel #11
0
        // TODO: format soundfont import to detect file versions and types of files.
        public static SampleSoundGenerator ImportFromSoundFont(SampleGeneratingArgs args, SoundFont sf2)
        {
            SampleSoundGenerator wave = null;

            foreach (var preset in sf2.Presets)
            {
                if (preset.PatchNumber != args.Patch && args.Patch != -1)
                {
                    continue;
                }
                if (preset.Bank != args.Bank && args.Bank != -1)
                {
                    continue;
                }

                wave = ImportPreset(sf2, preset, args);
                if (wave != null)
                {
                    break;
                }
            }

            return(wave ?? ImportInstruments(sf2, args));
        }
Beispiel #12
0
        private static SampleSoundGenerator GetSampleRemainder(SampleHeader sh, Zone izone, byte[] sample, SampleGeneratingArgs args)
        {
            // Indices in sf2 are numbers of samples, not byte length. So double them
            int start     = (int)sh.Start + izone.FullStartAddressOffset();
            int end       = (int)sh.End + izone.FullEndAddressOffset();
            int startLoop = (int)sh.StartLoop + izone.FullStartLoopAddressOffset();
            int endLoop   = (int)sh.EndLoop + izone.FullEndLoopAddressOffset();

            int length          = end - start;
            int loopLength      = endLoop - startLoop;
            int loopLengthBytes = loopLength * 2;

            int lengthFirstHalf      = startLoop - start;
            int lengthFirstHalfBytes = lengthFirstHalf * 2;

            int lengthSecondHalf      = end - endLoop;
            int lengthSecondHalfBytes = lengthSecondHalf * 2;

            double lengthInSeconds = args.Length != -1 ? (args.Length / 1000) : length / (double)sh.SampleRate;

            // Sample rate key correction
            int    keyCorrection = args.Key != -1 ? args.Key - izone.Key() : 0;
            double factor        = Math.Pow(2, keyCorrection / 12d);

            lengthInSeconds *= factor;

            int numberOfSamples = (int)Math.Ceiling(lengthInSeconds * sh.SampleRate);

            numberOfSamples += lengthSecondHalf;
            int numberOfLoopSamples = numberOfSamples - lengthFirstHalf - lengthSecondHalf;

            if (numberOfLoopSamples < loopLength)
            {
                return(GetSampleWithoutLoop(sh, izone, sample, args));
            }

            int numberOfBytes     = numberOfSamples * 2;
            int numberOfLoopBytes = numberOfLoopSamples * 2;

            byte[] buffer     = new byte[numberOfBytes];
            byte[] bufferLoop = new byte[numberOfLoopBytes];

            Array.Copy(sample, start * 2, buffer, 0, lengthFirstHalfBytes);
            for (int i = 0; i < (numberOfLoopSamples + loopLength - 1) / loopLength; i++)
            {
                Array.Copy(sample, startLoop * 2, bufferLoop, i * loopLengthBytes, Math.Min(loopLengthBytes, numberOfLoopBytes - i * loopLengthBytes));
            }
            bufferLoop.CopyTo(buffer, lengthFirstHalfBytes);
            Array.Copy(sample, start * 2, buffer, lengthFirstHalfBytes + numberOfLoopBytes, lengthSecondHalfBytes);

            return(new SampleSoundGenerator(BufferToWaveStream(buffer, (uint)(sh.SampleRate * factor))));
        }
Beispiel #13
0
        private static SampleSoundGenerator GetSampleWithoutLoop(SampleHeader sh, Zone izone, byte[] sample, SampleGeneratingArgs args)
        {
            // Indices in sf2 are numbers of samples, not byte length. So double them
            int start = (int)sh.Start + izone.FullStartAddressOffset();
            int end   = (int)sh.End + izone.FullEndAddressOffset();

            int length = end - start;

            double lengthInSeconds = args.Length != -1 ? (args.Length / 1000) + 0.4 : length / (double)sh.SampleRate;

            // Sample rate key correction
            int    keyCorrection = args.Key != -1 ? args.Key - izone.Key() : 0;
            double factor        = Math.Pow(2, keyCorrection / 12d);

            lengthInSeconds *= factor;

            lengthInSeconds = Math.Min(lengthInSeconds, length / (double)sh.SampleRate);

            int numberOfSamples = (int)Math.Ceiling(lengthInSeconds * sh.SampleRate);
            int numberOfBytes   = numberOfSamples * 2;

            byte[] buffer = new byte[numberOfBytes];
            Array.Copy(sample, start * 2, buffer, 0, numberOfBytes);

            var output = new SampleSoundGenerator(BufferToWaveStream(buffer, (uint)(sh.SampleRate * factor)));

            if (lengthInSeconds <= 0.4)
            {
                output.FadeStart  = lengthInSeconds * 0.7;
                output.FadeLength = lengthInSeconds * 0.2;
            }
            else
            {
                output.FadeStart  = lengthInSeconds - 0.4;
                output.FadeLength = 0.3;
            }

            return(output);
        }
Beispiel #14
0
 private static SampleSoundGenerator GetSampleWithLength(SampleHeader sh, Zone izone, int sampleMode, byte[] sample, SampleGeneratingArgs args)
 {
     if (sampleMode == 0 || sampleMode == 2)
     {
         // Don't loop
         return(GetSampleWithoutLoop(sh, izone, sample, args));
     }
     else if (sampleMode == 1)
     {
         // Loop continuously
         return(GetSampleContinuous(sh, izone, sample, args));
     }
     else
     {
         // Loops for the duration of key depression then proceed to play the remainder of the sample
         return(GetSampleRemainder(sh, izone, sample, args));
     }
 }
Beispiel #15
0
        private static SampleSoundGenerator GenerateSample(Zone izone, byte[] sample, SampleGeneratingArgs args)
        {
            // Read the sample mode to apply the correct lengthening algorithm
            // Add volume sample provider for the velocity argument

            var sh         = izone.SampleHeader();
            int sampleMode = izone.SampleModes();

            byte   key              = izone.Key();
            byte   velocity         = izone.Velocity();
            double volumeCorrection = args.Velocity != -1 ? (double)args.Velocity / velocity : 1d;

            var output = GetSampleWithLength(sh, izone, sampleMode, sample, args);

            // Key correction is 0 to not use the shitty pitch shifter
            output.KeyCorrection    = 0;
            output.VolumeCorrection = volumeCorrection;

            return(output);
        }
Beispiel #16
0
 public static bool ValidateSampleArgs(SampleGeneratingArgs args, bool validateSampleFile = true)
 {
     return(!validateSampleFile || ValidateSamplePath(args.Path));
 }
Beispiel #17
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);
        }
Beispiel #18
0
 public static bool ValidateSampleArgs(SampleGeneratingArgs args)
 {
     return(ValidateSampleArgs(args.Path));
 }
Beispiel #19
0
        private static SampleSoundGenerator ImportInstruments(SoundFont sf2, IEnumerable <Instrument> instruments, SampleGeneratingArgs args)
        {
            Zone closest = null;
            int  i       = 0;
            int  bdist   = int.MaxValue;

            foreach (var instrument in instruments)   // perccusion bank likely has more than one instrument here.
            {
                if (instrument == null)
                {
                    continue;
                }

                if (i++ != args.Instrument && args.Instrument != -1)
                {
                    continue;
                }

                var iZone = ImportInstrument(instrument, args);

                if (iZone == null)
                {
                    continue;
                }

                // Get closest instrument from the zones in the preset
                int dist = Math.Abs(args.Key - iZone.Key());

                if (dist < bdist || args.Key == -1)
                {
                    closest = iZone;
                    bdist   = dist;
                }
            }

            if (closest == null)
            {
                return(null);
            }

            //Console.WriteLine("closest: " + closest.Key());
            var wave = GenerateSample(closest, sf2.SampleData, args);

            return(wave);
        }
Beispiel #20
0
 private static SampleSoundGenerator ImportInstruments(SoundFont sf2, SampleGeneratingArgs args)
 {
     return(ImportInstruments(sf2, sf2.Instruments, args));
 }
Beispiel #21
0
 private static SampleSoundGenerator ImportPreset(SoundFont sf2, Preset preset, SampleGeneratingArgs args)
 {
     /*
      *  == Aproximate Pesdo Code of importing a preset from sf2 ==
      *  -    Get all preset zones from soundfont (sf2)
      *  -    get the instrument and double check if it's not null (if it is, "continue" the method)
      *  -    is the instrument grabbed the same as what args is asking for? (if not, "continue" method)
      *  -    get each zone within instrument and create wav file from information
      *      -   get Sample Header of instrument zone and double check if null (if so, "continue")
      *      -   get the Key range of spesified zone and create high and low keys 8 bit's in difference.
      *          -   is the key not the same as what the args asked for? (yes, then "continue")
      *      -   Get the velocity range of spesified zone and create high and low keys 8 bits' in difference.
      *          -   is the velocity not the same as what the args asked for? (yes, then "continue")
      *      -   Find the closest key from instrument zone to the key spesified from args.
      *          - is the closest key lower than the maximum integer value or, is the key of args just not spesified?
      *              - if so, set the closest zone (initial = null) to the current zone,
      *              - if so, set the bdist (initial = maximum integer value) to the closest key.
      *      -   Is there a zone found from above?
      *          - If so, create a wave from zone information using the SampleData.
      */
     return(ImportInstruments(sf2, preset.Zones.Select(z => z.Instrument()), args));
 }
Beispiel #22
0
        public static bool ExportSample(SampleGeneratingArgs sampleGeneratingArgs, string name,
                                        string exportFolder, Dictionary <SampleGeneratingArgs, SampleSoundGenerator> loadedSamples = null,
                                        SampleExportFormat format = SampleExportFormat.Default)
        {
            // Export as midi file with single note
            if (format == SampleExportFormat.MidiChords)
            {
                MidiExporter.SaveToFile(Path.Combine(exportFolder, name + ".mid"), new[] { sampleGeneratingArgs });
                return(true);
            }

            if (sampleGeneratingArgs.CanCopyPaste && format == SampleExportFormat.Default)
            {
                var dest = Path.Combine(exportFolder, name + sampleGeneratingArgs.GetExtension());
                return(CopySample(sampleGeneratingArgs.Path, dest));
            }

            SampleSoundGenerator sampleSoundGenerator;

            if (loadedSamples != null)
            {
                if (SampleImporter.ValidateSampleArgs(sampleGeneratingArgs, loadedSamples))
                {
                    sampleSoundGenerator = loadedSamples[sampleGeneratingArgs];
                }
                else
                {
                    return(false);
                }
            }
            else
            {
                try {
                    sampleSoundGenerator = SampleImporter.ImportSample(sampleGeneratingArgs);
                } catch (Exception ex) {
                    Console.WriteLine($@"{ex.Message} while importing sample {sampleGeneratingArgs}.");
                    return(false);
                }
            }

            var sourceWaveEncoding = sampleSoundGenerator.Wave.WaveFormat.Encoding;

            // Either if it is the blank sample or the source file is literally what the user wants to be exported
            if (sampleSoundGenerator.BlankSample && sampleGeneratingArgs.GetExtension().ToLower() == ".wav" ||
                sampleGeneratingArgs.CanCopyPaste && IsCopyCompatible(sampleGeneratingArgs, sourceWaveEncoding, format))
            {
                var dest = Path.Combine(exportFolder, name + sampleGeneratingArgs.GetExtension());
                return(CopySample(sampleGeneratingArgs.Path, dest));
            }

            var sampleProvider = sampleSoundGenerator.GetSampleProvider();

            if ((format == SampleExportFormat.WavePcm || format == SampleExportFormat.OggVorbis) && sourceWaveEncoding == WaveFormatEncoding.IeeeFloat)
            {
                // When the source is IEEE float and the export format is PCM or Vorbis, then clipping is possible, so we add a limiter
                sampleProvider = new SoftLimiter(sampleProvider);
            }

            switch (format)
            {
            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:
                var resampled = new WdlResamplingSampleProvider(sampleProvider,
                                                                VorbisFileWriter.GetSupportedSampleRate(sampleProvider.WaveFormat.SampleRate));
                VorbisFileWriter.CreateVorbisFile(Path.Combine(exportFolder, name + ".ogg"), resampled.ToWaveProvider());
                break;

            default:
                switch (sourceWaveEncoding)
                {
                case WaveFormatEncoding.IeeeFloat:
                    CreateWaveFile(Path.Combine(exportFolder, name + ".wav"), sampleProvider.ToWaveProvider());
                    break;

                case WaveFormatEncoding.Pcm:
                    CreateWaveFile(Path.Combine(exportFolder, name + ".wav"), sampleProvider.ToWaveProvider16());
                    break;

                default:
                    CreateWaveFile(Path.Combine(exportFolder, name + ".wav"), sampleProvider.ToWaveProvider());
                    break;
                }

                break;
            }

            return(true);
        }
        public static void AddNewSampleName(Dictionary <SampleGeneratingArgs, string> sampleNames, SampleGeneratingArgs sample,
                                            Dictionary <SampleGeneratingArgs, SampleSoundGenerator> loadedSamples)
        {
            if (!SampleImporter.ValidateSampleArgs(sample, loadedSamples))
            {
                sampleNames[sample] = string.Empty;
                return;
            }

            var baseName = sample.GetFilename();
            var name     = baseName;
            int i        = 1;

            while (sampleNames.ContainsValue(name))
            {
                name = baseName + "-" + ++i;
            }

            sampleNames[sample] = name;
        }
        public static bool ExportSample(SampleGeneratingArgs sampleGeneratingArgs, string name,
                                        string exportFolder, Dictionary <SampleGeneratingArgs, SampleSoundGenerator> loadedSamples = null,
                                        SampleExportFormat format = SampleExportFormat.Default)
        {
            if (sampleGeneratingArgs.CanCopyPaste && format == SampleExportFormat.Default)
            {
                var dest = Path.Combine(exportFolder, name + sampleGeneratingArgs.GetExtension());
                return(CopySample(sampleGeneratingArgs.Path, dest));
            }

            SampleSoundGenerator sampleSoundGenerator;

            if (loadedSamples != null)
            {
                if (SampleImporter.ValidateSampleArgs(sampleGeneratingArgs, loadedSamples))
                {
                    sampleSoundGenerator = loadedSamples[sampleGeneratingArgs];
                }
                else
                {
                    return(false);
                }
            }
            else
            {
                try {
                    sampleSoundGenerator = SampleImporter.ImportSample(sampleGeneratingArgs);
                } catch (Exception ex) {
                    Console.WriteLine($@"{ex.Message} while importing sample {sampleGeneratingArgs}.");
                    return(false);
                }
            }

            var sourceEncoding = sampleSoundGenerator.Wave.WaveFormat.Encoding;

            // Either if it is the blank sample or the source file is literally what the user wants to be exported
            if (sampleSoundGenerator.BlankSample && sampleGeneratingArgs.GetExtension() == ".wav" ||
                sampleGeneratingArgs.CanCopyPaste && IsFormatEncodingCompatible(sourceEncoding, format))
            {
                var dest = Path.Combine(exportFolder, name + sampleGeneratingArgs.GetExtension());
                return(CopySample(sampleGeneratingArgs.Path, dest));
            }

            var sampleProvider = sampleSoundGenerator.GetSampleProvider();

            if ((format == SampleExportFormat.WavePcm || format == SampleExportFormat.OggVorbis) && sourceEncoding == WaveFormatEncoding.IeeeFloat)
            {
                // When the source is IEEE float and the export format is PCM or Vorbis, then clipping is possible, so we add a limiter
                sampleProvider = new SoftLimiter(sampleProvider);
            }

            switch (format)
            {
            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:
                switch (sourceEncoding)
                {
                case WaveFormatEncoding.IeeeFloat:
                    CreateWaveFile(Path.Combine(exportFolder, name + ".wav"), sampleProvider.ToWaveProvider());
                    break;

                case WaveFormatEncoding.Pcm:
                    CreateWaveFile(Path.Combine(exportFolder, name + ".wav"), sampleProvider.ToWaveProvider16());
                    break;

                case WaveFormatEncoding.Vorbis1:
                case WaveFormatEncoding.Vorbis2:
                case WaveFormatEncoding.Vorbis3:
                case WaveFormatEncoding.Vorbis1P:
                case WaveFormatEncoding.Vorbis2P:
                case WaveFormatEncoding.Vorbis3P:
                    // Vorbis files default to being exported as 16-bit PCM wave files because that's lossless
                    CreateWaveFile(Path.Combine(exportFolder, name + ".wav"), sampleProvider.ToWaveProvider16());
                    break;

                default:
                    CreateWaveFile(Path.Combine(exportFolder, name + ".wav"), sampleProvider.ToWaveProvider());
                    break;
                }

                break;
            }

            return(true);
        }