Ejemplo n.º 1
0
        private void ExportWav()
        {
            var filename = PlatformUtils.ShowSaveFileDialog("Export Wave File", "Wave Audio File (*.wav)|*.wav", ref Settings.LastExportFolder);

            if (filename != null)
            {
                var props            = dialog.GetPropertyPage((int)ExportFormat.Wav);
                var songName         = props.GetPropertyValue <string>(0);
                var sampleRate       = Convert.ToInt32(props.GetPropertyValue <string>(1));
                var loopCount        = props.GetPropertyValue <string>(2) != "Duration" ? props.GetPropertyValue <int>(3) : -1;
                var duration         = props.GetPropertyValue <string>(2) == "Duration" ? props.GetPropertyValue <int>(4) : -1;
                var selectedChannels = props.GetPropertyValue <bool[]>(5);

                var channelMask = 0;
                for (int i = 0; i < selectedChannels.Length; i++)
                {
                    if (selectedChannels[i])
                    {
                        channelMask |= (1 << i);
                    }
                }

                WaveFile.Save(project.GetSong(songName), filename, sampleRate, loopCount, duration, channelMask);
            }
        }
Ejemplo n.º 2
0
        private void ExportWavMp3()
        {
            var props  = dialog.GetPropertyPage((int)ExportFormat.WavMp3);
            var format = props.GetPropertyValue <string>(1);

            var filename = "";

            if (format == "MP3")
            {
                filename = lastExportFilename != null ? lastExportFilename : PlatformUtils.ShowSaveFileDialog("Export MP3 File", "MP3 Audio File (*.mp3)|*.mp3", ref Settings.LastExportFolder);
            }
            else
            {
                filename = lastExportFilename != null ? lastExportFilename : PlatformUtils.ShowSaveFileDialog("Export Wave File", "Wave Audio File (*.wav)|*.wav", ref Settings.LastExportFolder);
            }

            if (filename != null)
            {
                var songName         = props.GetPropertyValue <string>(0);
                var sampleRate       = Convert.ToInt32(props.GetPropertyValue <string>(2), CultureInfo.InvariantCulture);
                var bitRate          = Convert.ToInt32(props.GetPropertyValue <string>(3), CultureInfo.InvariantCulture);
                var loopCount        = props.GetPropertyValue <string>(4) != "Duration" ? props.GetPropertyValue <int>(5) : -1;
                var duration         = props.GetPropertyValue <string>(4) == "Duration" ? props.GetPropertyValue <int>(6) : -1;
                var separateFiles    = props.GetPropertyValue <bool>(7);
                var separateIntro    = props.GetPropertyValue <bool>(8);
                var selectedChannels = props.GetPropertyValue <bool[]>(9);
                var song             = project.GetSong(songName);

                var channelMask = 0;
                for (int i = 0; i < selectedChannels.Length; i++)
                {
                    if (selectedChannels[i])
                    {
                        channelMask |= (1 << i);
                    }
                }

                WavMp3ExportUtils.Save(song, filename, sampleRate, loopCount, duration, channelMask, separateFiles, separateIntro,
                                       (samples, fn) =>
                {
                    if (format == "MP3")
                    {
                        Mp3File.Save(samples, fn, sampleRate, bitRate);
                    }
                    else
                    {
                        WaveFile.Save(samples, fn, sampleRate);
                    }
                });

                lastExportFilename = filename;
            }
        }
Ejemplo n.º 3
0
        private void ExportWav()
        {
            var filename = PlatformUtils.ShowSaveFileDialog("Export Wave File", "Wave Audio File (*.wav)|*.wav");

            if (filename != null)
            {
                var props      = dialog.GetPropertyPage((int)ExportFormat.Wav);
                var songName   = props.GetPropertyValue <string>(0);
                var sampleRate = Convert.ToInt32(props.GetPropertyValue <string>(1));

                WaveFile.Save(project.GetSong(songName), filename, sampleRate);
            }
        }
Ejemplo n.º 4
0
        public void Serialize(ref Song song)
        {
            int songId = -1;

            Serialize(ref songId, true);
            song = project.GetSong(songId);
        }
Ejemplo n.º 5
0
        private void FamiTone2MusicExport(string filename, bool famiStudio)
        {
            var kernel       = famiStudio ? FamiToneKernel.FamiStudio : FamiToneKernel.FamiTone2;
            var engineName   = famiStudio ? "famistudio" : "famitone2";
            var formatString = ParseOption($"{engineName}-asm-format", "nesasm");

            var format = AssemblyFormat.NESASM;

            switch (formatString)
            {
            case "ca65": format = AssemblyFormat.CA65; break;

            case "asm6": format = AssemblyFormat.ASM6; break;
            }

            var extension       = format == AssemblyFormat.CA65 ? ".s" : ".asm";
            var seperate        = HasOption($"{engineName}-asm-seperate-files");
            var generateInclude = HasOption($"{engineName}-generate-list");

            if (!seperate && !ValidateExtension(filename, extension))
            {
                return;
            }

            var exportSongIds = GetExportSongIds();

            if (exportSongIds != null)
            {
                if (seperate)
                {
                    var songNamePattern = ParseOption($"{engineName}-asm-seperate-song-pattern", "{project}_{song}");
                    var dpcmNamePattern = ParseOption($"{engineName}-asm-seperate-dmc-pattern", "{project}");

                    foreach (var songId in exportSongIds)
                    {
                        var song = project.GetSong(songId);
                        var formattedSongName = songNamePattern.Replace("{project}", project.Name).Replace("{song}", song.Name);
                        var formattedDpcmName = dpcmNamePattern.Replace("{project}", project.Name).Replace("{song}", song.Name);
                        var songFilename      = Path.Combine(filename, Utils.MakeNiceAsmName(formattedSongName) + extension);
                        var dpcmFilename      = Path.Combine(filename, Utils.MakeNiceAsmName(formattedDpcmName) + ".dmc");
                        var includeFilename   = generateInclude ? Path.ChangeExtension(songFilename, null) + "_songlist.inc" : null;

                        Log.LogMessage(LogSeverity.Info, $"Exporting song '{song.Name}' as separate assembly files.");

                        FamitoneMusicFile f = new FamitoneMusicFile(kernel, true);
                        f.Save(project, new int[] { songId }, format, true, songFilename, dpcmFilename, includeFilename, MachineType.Dual);
                    }
                }
                else
                {
                    var includeFilename = generateInclude ? Path.ChangeExtension(filename, null) + "_songlist.inc" : null;

                    Log.LogMessage(LogSeverity.Info, $"Exporting all songs to a single assembly file.");

                    FamitoneMusicFile f = new FamitoneMusicFile(kernel, true);
                    f.Save(project, exportSongIds, format, false, filename, Path.ChangeExtension(filename, ".dmc"), includeFilename, MachineType.Dual);
                }
            }
        }
Ejemplo n.º 6
0
        private void FamiTone2MusicExport(string filename)
        {
            var formatString = ParseOption("famitone2-format", "nesasm");

            var format = AssemblyFormat.NESASM;

            switch (formatString)
            {
            case "ca65": format = AssemblyFormat.CA65; break;

            case "asm6": format = AssemblyFormat.ASM6; break;
            }

            var extension = format == AssemblyFormat.CA65 ? ".s" : ".asm";
            var seperate  = HasOption("famitone2-seperate-files");

            if (!seperate && !ValidateExtension(filename, extension))
            {
                return;
            }

            var kernelString = ParseOption("famitone2-variant", "famitone2");
            var kernel       = FamitoneMusicFile.FamiToneKernel.FamiTone2;

            switch (formatString)
            {
            case "famitone2fs": kernel = FamitoneMusicFile.FamiToneKernel.FamiTone2FS; break;

            case "famistudio":  kernel = FamitoneMusicFile.FamiToneKernel.FamiStudio;  break;
            }

            var exportSongIds = GetExportSongIds();

            if (exportSongIds != null)
            {
                if (seperate)
                {
                    var songNamePattern = ParseOption("famitone2-seperate-song-pattern", "{project}_{song}");
                    var dpcmNamePattern = ParseOption("famitone2-seperate-dmc-pattern", "{project}");

                    foreach (var songId in exportSongIds)
                    {
                        var song = project.GetSong(songId);
                        var formattedSongName = songNamePattern.Replace("{project}", project.Name).Replace("{song}", song.Name);
                        var formattedDpcmName = dpcmNamePattern.Replace("{project}", project.Name).Replace("{song}", song.Name);
                        var songFilename      = Path.Combine(filename, Utils.MakeNiceAsmName(formattedSongName) + extension);
                        var dpcmFilename      = Path.Combine(filename, Utils.MakeNiceAsmName(formattedDpcmName) + ".dmc");

                        FamitoneMusicFile f = new FamitoneMusicFile(kernel);
                        f.Save(project, new int[] { songId }, format, true, songFilename, dpcmFilename, MachineType.Dual);
                    }
                }
                else
                {
                    FamitoneMusicFile f = new FamitoneMusicFile(kernel);
                    f.Save(project, exportSongIds, format, false, filename, Path.ChangeExtension(filename, ".dmc"), MachineType.Dual);
                }
            }
        }
Ejemplo n.º 7
0
        private void ExportWav()
        {
            var sfd = new SaveFileDialog()
            {
                Filter = "Wave Audio File (*.wav)|*.wav",
                Title  = "Export Wave File"
            };

            if (sfd.ShowDialog() == DialogResult.OK)
            {
                var props      = formatProps[(int)ExportFormat.Wav];
                var songName   = props.GetPropertyValue <string>(0);
                var sampleRate = Convert.ToInt32(props.GetPropertyValue <string>(1));

                WaveFile.Save(project.GetSong(songName), sfd.FileName, sampleRate);
            }
        }
Ejemplo n.º 8
0
        public bool Save(Project project, int[] songIds, AssemblyFormat format, MachineType mode, string filename)
        {
            SetupFormat(format);

            var modeStrings = new List <string>();

            if (mode == MachineType.NTSC || mode == MachineType.Dual)
            {
                modeStrings.Add("ntsc");
            }
            if (mode == MachineType.PAL || mode == MachineType.Dual)
            {
                modeStrings.Add("pal");
            }

            var lines = new List <string>();

            lines.Add($";this file for FamiTone2 libary generated by FamiStudio\n");
            lines.Add($"sounds:");

            lines.Add($"\t{dw} {ll}{modeStrings[0]}");
            lines.Add($"\t{dw} {ll}{modeStrings[1 % modeStrings.Count]}");

            foreach (var str in modeStrings)
            {
                lines.Add($"{ll}{str}:");
                foreach (var songId in songIds)
                {
                    var song = project.GetSong(songId);
                    lines.Add($"\t{dw} {ll}sfx_{str}_{Utils.MakeNiceAsmName(song.Name)}");
                }
                lines.Add("");
            }

            foreach (var str in modeStrings)
            {
                foreach (var songId in songIds)
                {
                    var song = project.GetSong(songId);

                    var regPlayer = new RegisterPlayer();
                    var writes    = regPlayer.GetRegisterValues(song, str == "pal");

                    var lastChangeFrame   = 0;
                    var lastZeroVolumeIdx = -1;
                    var volumeAllZero     = true;
                    var volume            = new int[4];
                    var regs   = new int[32];
                    var effect = new List <byte>();

                    for (int i = 0; i < regs.Length; i++)
                    {
                        regs[i] = -1;
                    }

                    regs[0x00] = 0x30;
                    regs[0x04] = 0x30;
                    regs[0x08] = 0x00;
                    regs[0x0c] = 0x30;

                    foreach (var reg in writes)
                    {
                        if (reg.Register == NesApu.APU_PL1_VOL ||
                            reg.Register == NesApu.APU_PL1_LO ||
                            reg.Register == NesApu.APU_PL1_HI ||
                            reg.Register == NesApu.APU_PL2_VOL ||
                            reg.Register == NesApu.APU_PL2_LO ||
                            reg.Register == NesApu.APU_PL2_HI ||
                            reg.Register == NesApu.APU_TRI_LINEAR ||
                            reg.Register == NesApu.APU_TRI_LO ||
                            reg.Register == NesApu.APU_TRI_HI ||
                            reg.Register == NesApu.APU_NOISE_VOL ||
                            reg.Register == NesApu.APU_NOISE_LO)
                        {
                            if (regs[reg.Register - 0x4000] != reg.Value)
                            {
                                if (reg.FrameNumber != lastChangeFrame)
                                {
                                    int numEmptyFrames = reg.FrameNumber - lastChangeFrame;

                                    while (numEmptyFrames >= 0)
                                    {
                                        effect.Add((byte)(Math.Min(numEmptyFrames, 127)));
                                        numEmptyFrames -= 127;
                                    }
                                }

                                switch (reg.Register)
                                {
                                case 0x4000: volume[0] = reg.Value & 0x0f; break;

                                case 0x4004: volume[1] = reg.Value & 0x0f; break;

                                case 0x4008: volume[2] = reg.Value & 0x7f; break;

                                case 0x400c: volume[3] = reg.Value & 0x0f; break;
                                }

                                if (!volumeAllZero)
                                {
                                    if (volume[0] == 0 && volume[1] == 0 && volume[2] == 0 && volume[3] == 0)
                                    {
                                        volumeAllZero     = true;
                                        lastZeroVolumeIdx = effect.Count();
                                    }
                                }
                                else
                                {
                                    if (volume[0] != 0 || volume[1] != 0 || volume[2] != 0 || volume[3] != 0)
                                    {
                                        volumeAllZero = false;
                                    }
                                }

                                effect.Add(RegisterMap[reg.Register - 0x4000]);
                                effect.Add((byte)reg.Value);

                                regs[reg.Register - 0x4000] = reg.Value;

                                lastChangeFrame = reg.FrameNumber;
                            }
                        }
                    }

                    if (!volumeAllZero)
                    {
                        int numEmptyFrames = writes[writes.Length - 1].FrameNumber - lastChangeFrame;

                        while (numEmptyFrames > 0)
                        {
                            effect.Add((byte)(Math.Min(numEmptyFrames, 127)));
                            numEmptyFrames -= 127;
                        }
                    }
                    else
                    {
                        effect.RemoveRange(lastZeroVolumeIdx, effect.Count - lastZeroVolumeIdx);
                    }

                    // TODO: Error management!
                    if (effect.Count > 255)
                    {
                        effect.RemoveRange(lastZeroVolumeIdx, effect.Count - 255);
                    }

                    effect.Add(0);

                    lines.Add($"{ll}sfx_{str}_{Utils.MakeNiceAsmName(song.Name)}:");

                    for (int i = 0; i < (effect.Count + 15) / 16; i++)
                    {
                        lines.Add($"\t{db} {string.Join(",", effect.Skip(i * 16).Take(Math.Min(16, effect.Count - i * 16)).Select(x => $"${x:x2}"))}");
                    }
                }
            }

            File.WriteAllLines(filename, lines.ToArray());

            return(true);
        }
Ejemplo n.º 9
0
        public bool Save(Project project, int[] songIds, int format, int machine, int kernel, string filename, string includeFilename)
        {
            SetupFormat(format);

            var modeStrings = new List <string>();

            if (machine == MachineType.NTSC || machine == MachineType.Dual)
            {
                modeStrings.Add("ntsc");
            }
            if (machine == MachineType.PAL || machine == MachineType.Dual)
            {
                modeStrings.Add("pal");
            }

            var lines = new List <string>();

            lines.Add($";this file for FamiTone2 libary generated by FamiStudio\n");
            lines.Add($"sounds:");

            lines.Add($"\t{dw} {ll}{modeStrings[0]}");
            lines.Add($"\t{dw} {ll}{modeStrings[1 % modeStrings.Count]}");

            foreach (var str in modeStrings)
            {
                lines.Add($"{ll}{str}:");
                foreach (var songId in songIds)
                {
                    var song = project.GetSong(songId);
                    lines.Add($"\t{dw} {ll}sfx_{str}_{Utils.MakeNiceAsmName(song.Name)}");
                }
                lines.Add("");
            }

            foreach (var str in modeStrings)
            {
                foreach (var songId in songIds)
                {
                    var song   = project.GetSong(songId);
                    var writes = GetRegisterWrites(song, str == "pal");

                    var lastChangeFrame   = 0;
                    var lastZeroVolumeIdx = -1;
                    var volumeAllZero     = true;
                    var volume            = new int[4];
                    var regs              = new int[32];
                    var effect            = new List <byte>();
                    var lastByteIsOperand = false;

                    for (int i = 0; i < regs.Length; i++)
                    {
                        regs[i] = -1;
                    }

                    regs[0x00] = 0x30;
                    regs[0x04] = 0x30;
                    regs[0x08] = 0x80;
                    regs[0x0c] = 0x30;

                    foreach (var reg in writes)
                    {
                        if (reg.Register == NesApu.APU_PL1_VOL ||
                            reg.Register == NesApu.APU_PL1_LO ||
                            reg.Register == NesApu.APU_PL1_HI ||
                            reg.Register == NesApu.APU_PL2_VOL ||
                            reg.Register == NesApu.APU_PL2_LO ||
                            reg.Register == NesApu.APU_PL2_HI ||
                            reg.Register == NesApu.APU_TRI_LINEAR ||
                            reg.Register == NesApu.APU_TRI_LO ||
                            reg.Register == NesApu.APU_TRI_HI ||
                            reg.Register == NesApu.APU_NOISE_VOL ||
                            reg.Register == NesApu.APU_NOISE_LO)
                        {
                            if (regs[reg.Register - 0x4000] != reg.Value)
                            {
                                if (reg.FrameNumber != lastChangeFrame)
                                {
                                    int numEmptyFrames = reg.FrameNumber - lastChangeFrame;

                                    while (numEmptyFrames >= 0)
                                    {
                                        effect.Add((byte)(Math.Min(numEmptyFrames, 127)));
                                        numEmptyFrames -= 127;
                                    }
                                }

                                switch (reg.Register)
                                {
                                case 0x4000: volume[0] = reg.Value & 0x0f; break;

                                case 0x4004: volume[1] = reg.Value & 0x0f; break;

                                case 0x4008: volume[2] = reg.Value & 0x7f; break;

                                case 0x400c: volume[3] = reg.Value & 0x0f; break;
                                }

                                if (!volumeAllZero)
                                {
                                    if (volume[0] == 0 && volume[1] == 0 && volume[2] == 0 && volume[3] == 0)
                                    {
                                        volumeAllZero     = true;
                                        lastZeroVolumeIdx = effect.Count();
                                    }
                                }
                                else
                                {
                                    if (volume[0] != 0 || volume[1] != 0 || volume[2] != 0 || volume[3] != 0)
                                    {
                                        volumeAllZero = false;
                                    }
                                }

                                effect.Add(RegisterMap[reg.Register - 0x4000]);
                                effect.Add((byte)reg.Value);

                                if (effect.Count == 255)
                                {
                                    lastByteIsOperand = true;
                                }

                                regs[reg.Register - 0x4000] = reg.Value;

                                lastChangeFrame = reg.FrameNumber;
                            }
                        }
                    }

                    if (!volumeAllZero)
                    {
                        int numEmptyFrames = writes[writes.Length - 1].FrameNumber - lastChangeFrame;

                        while (numEmptyFrames > 0)
                        {
                            effect.Add((byte)(Math.Min(numEmptyFrames, 127)));
                            numEmptyFrames -= 127;
                        }
                    }
                    else if (lastZeroVolumeIdx >= 0)
                    {
                        effect.RemoveRange(lastZeroVolumeIdx, effect.Count - lastZeroVolumeIdx);
                    }

                    if (kernel == FamiToneKernel.FamiTone2 && effect.Count > 255)
                    {
                        Log.LogMessage(LogSeverity.Warning, $"Effect ({song.Name}) was longer than 256 bytes ({effect.Count}) and was truncated.");
                        var removeStart = lastByteIsOperand ? 254 : 255;
                        effect.RemoveRange(removeStart, effect.Count - removeStart);
                    }

                    effect.Add(0);

                    lines.Add($"{ll}sfx_{str}_{Utils.MakeNiceAsmName(song.Name)}:");

                    for (int i = 0; i < (effect.Count + 15) / 16; i++)
                    {
                        lines.Add($"\t{db} {string.Join(",", effect.Skip(i * 16).Take(Math.Min(16, effect.Count - i * 16)).Select(x => $"${x:x2}"))}");
                    }

                    Log.LogMessage(LogSeverity.Info, $"Effect ({song.Name}): {effect.Count} bytes.");
                }
            }

            if (format == AssemblyFormat.CA65)
            {
                lines.Add("");
                lines.Add(".export sounds");
            }

            File.WriteAllLines(filename, lines.ToArray());

            if (includeFilename != null)
            {
                var includeLines = new List <string>();

                for (int songIdx = 0; songIdx < songIds.Length; songIdx++)
                {
                    var songId = songIds[songIdx];
                    var song   = project.GetSong(songId);
                    includeLines.Add($"sfx_{Utils.MakeNiceAsmName(song.Name)} = {songIdx}");
                }
                includeLines.Add($"sfx_max = {songIds.Length}");

                // For CA65, also include song names.
                if (format == AssemblyFormat.CA65)
                {
                    includeLines.Add("");
                    includeLines.Add(".if SFX_STRINGS");
                    includeLines.Add("sfx_strings:");

                    foreach (var songId in songIds)
                    {
                        var song = project.GetSong(songId);
                        includeLines.Add($".asciiz \"{song.Name}\"");
                    }

                    includeLines.Add(".endif");
                }

                File.WriteAllLines(includeFilename, includeLines.ToArray());
            }

            return(true);
        }
Ejemplo n.º 10
0
        private void ExportWavMp3()
        {
            var props  = dialog.GetPropertyPage((int)ExportFormat.WavMp3);
            var format = props.GetPropertyValue <string>(1);

            var filename = "";

            if (format == "MP3")
            {
                filename = PlatformUtils.ShowSaveFileDialog("Export MP3 File", "MP3 Audio File (*.mp3)|*.mp3", ref Settings.LastExportFolder);
            }
            else
            {
                filename = PlatformUtils.ShowSaveFileDialog("Export Wave File", "Wave Audio File (*.wav)|*.wav", ref Settings.LastExportFolder);
            }

            if (filename != null)
            {
                var songName         = props.GetPropertyValue <string>(0);
                var sampleRate       = Convert.ToInt32(props.GetPropertyValue <string>(2));
                var bitRate          = Convert.ToInt32(props.GetPropertyValue <string>(3));
                var loopCount        = props.GetPropertyValue <string>(4) != "Duration" ? props.GetPropertyValue <int>(5) : -1;
                var duration         = props.GetPropertyValue <string>(4) == "Duration" ? props.GetPropertyValue <int>(6) : -1;
                var separateFiles    = props.GetPropertyValue <bool>(7);
                var selectedChannels = props.GetPropertyValue <bool[]>(8);
                var song             = project.GetSong(songName);

                if (separateFiles)
                {
                    for (int i = 0; i < selectedChannels.Length; i++)
                    {
                        if (selectedChannels[i])
                        {
                            var channelFilename = Utils.AddFileSuffix(filename, "_" + song.Channels[i].ExportName);

                            if (format == "MP3")
                            {
                                Mp3File.Save(song, channelFilename, sampleRate, bitRate, loopCount, duration, 1 << i);
                            }
                            else
                            {
                                WaveFile.Save(song, channelFilename, sampleRate, loopCount, duration, 1 << i);
                            }
                        }
                    }
                }
                else
                {
                    var channelMask = 0;
                    for (int i = 0; i < selectedChannels.Length; i++)
                    {
                        if (selectedChannels[i])
                        {
                            channelMask |= (1 << i);
                        }
                    }

                    if (format == "MP3")
                    {
                        Mp3File.Save(song, filename, sampleRate, bitRate, loopCount, duration, channelMask);
                    }
                    else
                    {
                        WaveFile.Save(song, filename, sampleRate, loopCount, duration, channelMask);
                    }
                }
            }
        }
Ejemplo n.º 11
0
        private void ExportWavMp3()
        {
            var props  = dialog.GetPropertyPage((int)ExportFormat.WavMp3);
            var format = props.GetSelectedIndex(1);

            Action <string> ExportWavMp3Action = (filename) =>
            {
                if (filename != null)
                {
                    var songName      = props.GetPropertyValue <string>(0);
                    var sampleRate    = Convert.ToInt32(props.GetPropertyValue <string>(2), CultureInfo.InvariantCulture);
                    var bitrate       = Convert.ToInt32(props.GetPropertyValue <string>(3), CultureInfo.InvariantCulture);
                    var loopCount     = props.GetPropertyValue <string>(4) != "Duration" ? props.GetPropertyValue <int>(5) : -1;
                    var duration      = props.GetPropertyValue <string>(4) == "Duration" ? props.GetPropertyValue <int>(6) : -1;
                    var separateFiles = props.GetPropertyValue <bool>(7);
                    var separateIntro = props.GetPropertyValue <bool>(8);
                    var stereo        = props.GetPropertyValue <bool>(9) && !separateFiles;
                    var song          = project.GetSong(songName);

                    var channelCount = project.GetActiveChannelCount();
                    var channelMask  = 0;
                    var pan          = (float[])null;

                    if (PlatformUtils.IsDesktop)
                    {
                        pan = new float[channelCount];

                        for (int i = 0; i < channelCount; i++)
                        {
                            if (props.GetPropertyValue <bool>(10, i, 0))
                            {
                                channelMask |= (1 << i);
                            }

                            pan[i] = props.GetPropertyValue <int>(10, i, 2) / 100.0f;
                        }
                    }
                    else
                    {
                        var selectedChannels = props.GetPropertyValue <bool[]>(10);
                        for (int i = 0; i < channelCount; i++)
                        {
                            if (selectedChannels[i])
                            {
                                channelMask |= (1 << i);
                            }
                        }
                    }

                    AudioExportUtils.Save(song, filename, sampleRate, loopCount, duration, channelMask, separateFiles, separateIntro, stereo, pan,
                                          (samples, samplesChannels, fn) =>
                    {
                        switch (format)
                        {
                        case AudioFormatType.Mp3:
                            Mp3File.Save(samples, fn, sampleRate, bitrate, samplesChannels);
                            break;

                        case AudioFormatType.Wav:
                            WaveFile.Save(samples, fn, sampleRate, samplesChannels);
                            break;

                        case AudioFormatType.Vorbis:
                            VorbisFile.Save(samples, fn, sampleRate, bitrate, samplesChannels);
                            break;
                        }
                    });

                    lastExportFilename = filename;
                }
            };

            if (PlatformUtils.IsMobile)
            {
                var songName = props.GetPropertyValue <string>(0);
                PlatformUtils.StartMobileSaveFileOperationAsync(AudioFormatType.MimeTypes[format], $"{songName}", (f) =>
                {
                    ExportWavMp3Action(f);
                    PlatformUtils.FinishMobileSaveFileOperationAsync(true, () => { PlatformUtils.ShowToast("Audio Export Successful!"); });
                });
            }
            else
            {
                var filename = (string)null;

                if (lastExportFilename != null)
                {
                    filename = lastExportFilename;
                }
                else
                {
                    filename = PlatformUtils.ShowSaveFileDialog(
                        $"Export {AudioFormatType.Names[format]} File",
                        $"{AudioFormatType.Names[format]} Audio File (*.{AudioFormatType.Extensions[format]})|*.{AudioFormatType.Extensions[format]}",
                        ref Settings.LastExportFolder);
                }

                ExportWavMp3Action(filename);
            }
        }