Пример #1
0
        private void DisplayHelp()
        {
            var version = Utils.SplitVersionNumber(PlatformUtils.ApplicationVersion, out _);

            InitializeConsole();
            Console.WriteLine($"FamiStudio {version} Command-Line Usage");
            Console.WriteLine($"");
            Console.WriteLine($"Usage:");
            Console.WriteLine($"  FamiStudio <input> <command> <output> [-options]");
            Console.WriteLine($"");
            Console.WriteLine($"Examples:");
            Console.WriteLine($"  FamiStudio music.fms wav-export music.wav -export-songs:2 -wav-export-rate:48000");
            Console.WriteLine($"  FamiStudio music.fms famitracker-txt-export music.txt -export-songs:0,1,2");
            Console.WriteLine($"  FamiStudio music.fms famitone2-asm-export music.s -famitone2-format:ca65");
            Console.WriteLine($"");
            Console.WriteLine($"Supported input formats:");
            Console.WriteLine($"  FamiStudio project (*.fms)");
            Console.WriteLine($"  FamiStudio text file (*.txt)");
            Console.WriteLine($"  FamiTracker 0.4.6 file (*.ftm)");
            Console.WriteLine($"  FamiTracker 0.4.6 text file (*.txt)");
            Console.WriteLine($"  Nintendo Sound Format (*.nsf, *.nsfe)");
            Console.WriteLine($"");
            Console.WriteLine($"Supported commands and corresponding output format(s):");
            Console.WriteLine($"  wav-export : Export to a WAV file (*.wav).");
            Console.WriteLine($"  mp3-export : Export to a WAV file (*.mp3).");
            Console.WriteLine($"  ogg-export : Export to a OGG file (*.ogg).");
            Console.WriteLine($"  nsf-export : Export to a NSF file (*.nsf).");
            Console.WriteLine($"  rom-export : Export to a NES ROM file (.nes).");
            Console.WriteLine($"  fds-export : Export to a FDS disk file (.fds).");
            Console.WriteLine($"  famitracker-txt-export : Export to a FamiTracker text file (*.txt).");
            Console.WriteLine($"  famistudio-txt-export : Export to a FamiStudio text file (*.txt).");
            Console.WriteLine($"  famistudio-asm-export : Export to FamiStudio sound engine music assembly file(s) (*.s, *.asm).");
            Console.WriteLine($"  famistudio-asm-sfx-export : Export to FamiStudio sound engine sound effect assembly file(s) (*.s, *.asm).");
            Console.WriteLine($"  famitone2-asm-export : Export to FamiTone2 music assembly file(s) (*.s, *.asm).");
            Console.WriteLine($"  famitone2-asm-sfx-export : Export to FamiTone2 sound effect assembly file(s) (*.s, *.asm).");
            Console.WriteLine($"");
            Console.WriteLine($"General options:");
            Console.WriteLine($"  -export-songs:<songs> : Comma-seperated zero-based indices of the songs to export (default:all).");
            Console.WriteLine($"");
            Console.WriteLine($"NSF import specific options");
            Console.WriteLine($"  -nsf-import-song:<song> : Zero-based index of the song to import (default:0).");
            Console.WriteLine($"  -nsf-import-duration:<duration> : Duration, in sec, to record from the NSF (default:120).");
            Console.WriteLine($"  -nsf-import-pattern-length:<length> : Pattern length to split the NSF into (default:256).");
            Console.WriteLine($"  -nsf-import-start-frame:<frame> : Frame to skips before starting the NSF capture (default:0).");
            Console.WriteLine($"  -nsf-import-reverse-dpcm : Reverse bits of DPCM samples (default:disabled).");
            Console.WriteLine($"  -nsf-import-preserve-padding : Preserve 1-byte of padding after DPCM samples (default:disabled).");
            Console.WriteLine($"");
            Console.WriteLine($"WAV export specific options");
            Console.WriteLine($"  -wav-export-rate:<rate> : Sample rate of the exported wave : 11025, 22050, 44100 or 48000 (default:44100).");
            Console.WriteLine($"  -wav-export-duration:<duration> : Duration in second, 0 plays song once and stop (default:0).");
            Console.WriteLine($"  -wav-export-loop:<count> : Number of times to play the song (default:1).");
            Console.WriteLine($"  -wav-export-channels:<mask> : Channel mask in hexadecimal, bit zero in channel 0 and so on (default:ff).");
            Console.WriteLine($"  -wav-export-separate-channels : Export each channels to separate file (default:off).");
            Console.WriteLine($"  -wav-export-separate-intro : Export the intro of the song seperately from the looping section (default:off).");
            Console.WriteLine($"");
            Console.WriteLine($"MP3 export specific options");
            Console.WriteLine($"  -mp3-export-rate:<rate> : Sample rate of the exported mp3 : 44100 or 48000 (default:44100).");
            Console.WriteLine($"  -mp3-export-bitrate:<rate> : Bitrate of the exported mp3 : 96, 112, 128, 160, 192, 224 or 256 (default:192).");
            Console.WriteLine($"  -mp3-export-duration:<duration> : Duration in second, 0 plays song once and stop (default:0).");
            Console.WriteLine($"  -mp3-export-loop:<count> : Number of times to play the song (default:1).");
            Console.WriteLine($"  -mp3-export-channels:<mask> : Channel mask in hexadecimal, bit zero in channel 0 and so on (default:ff).");
            Console.WriteLine($"  -mp3-export-separate-channels : Export each channels to separate file (default:off).");
            Console.WriteLine($"  -mp3-export-separate-intro : Export the intro of the song seperately from the looping section (default:off).");
            Console.WriteLine($"");
            Console.WriteLine($"OGG export specific options");
            Console.WriteLine($"  -ogg-export-rate:<rate> : Sample rate of the exported mp3 : 44100 or 48000 (default:44100).");
            Console.WriteLine($"  -ogg-export-bitrate:<rate> : Bitrate of the exported mp3 : 96, 112, 128, 160, 192, 224 or 256 (default:192).");
            Console.WriteLine($"  -ogg-export-duration:<duration> : Duration in second, 0 plays song once and stop (default:0).");
            Console.WriteLine($"  -ogg-export-loop:<count> : Number of times to play the song (default:1).");
            Console.WriteLine($"  -ogg-export-channels:<mask> : Channel mask in hexadecimal, bit zero in channel 0 and so on (default:ff).");
            Console.WriteLine($"  -ogg-export-separate-channels : Export each channels to separate file (default:off).");
            Console.WriteLine($"  -ogg-export-separate-intro : Export the intro of the song seperately from the looping section (default:off).");
            Console.WriteLine($"");
            Console.WriteLine($"NSF export specific options");
            Console.WriteLine($"  -nsf-export-mode:<mode> : Target machine: ntsc or pal (default:project mode).");
            Console.WriteLine($"");
            Console.WriteLine($"ROM export specific options");
            Console.WriteLine($"  -rom-export-mode:<mode> : Target machine: ntsc, pal or dual (default:project mode).");
            Console.WriteLine($"");
            Console.WriteLine($"FamiStudio text export specific options");
            Console.WriteLine($"  -famistudio-txt-cleanup : Cleanup unused data on export (default:disabled).");
            Console.WriteLine($"");
            Console.WriteLine($"FamiStudio sound engine export specific options");
            Console.WriteLine($"  -famistudio-asm-format:<format> : Assembly format to export to : nesasm, ca65 or asm6 (default:nesasm).");
            Console.WriteLine($"  -famistudio-asm-seperate-files : Export songs to individual files, output filename is the output path (default:disabled).");
            Console.WriteLine($"  -famistudio-asm-seperate-song-pattern:<pattern> : Name pattern to use when exporting songs to seperate files (default:{{project}}_{{song}}).");
            Console.WriteLine($"  -famistudio-asm-seperate-dmc-pattern:<pattern> : DMC filename pattern to use when exporting songs to seperate files (default:{{project}}).");
            Console.WriteLine($"  -famistudio-asm-generate-list : Generate song list include file along with music data (default:disabled).");
            Console.WriteLine($"  -famistudio-asm-sfx-mode:<mode> : Target machine for SFX : ntsc, pal or dual (default:project mode).");
            Console.WriteLine($"  -famistudio-asm-sfx-generate-list : Generate sfx list include file along with SFX data (default:disabled).");
            Console.WriteLine($"");
            Console.WriteLine($"FamiTone2 export specific options");
            Console.WriteLine($"  -famitone2-asm-format:<format> : Assembly format to export to : nesasm, ca65 or asm6 (default:nesasm).");
            Console.WriteLine($"  -famitone2-asm-seperate-files : Export songs to individual files, output filename is the output path (default:disabled).");
            Console.WriteLine($"  -famitone2-asm-seperate-song-pattern:<pattern> : Name pattern to use when exporting songs to seperate files (default:{{project}}_{{song}}).");
            Console.WriteLine($"  -famitone2-asm-seperate-dmc-pattern:<pattern> : DMC filename pattern to use when exporting songs to seperate files (default:{{project}}).");
            Console.WriteLine($"  -famitone2-asm-generate-list : Generate song list include file along with music data (default:disabled).");
            Console.WriteLine($"  -famitone2-asm-sfx-mode:<mode> : Target machine for SFX : ntsc, pal or dual (default:project mode).");
            Console.WriteLine($"  -famitone2-asm-sfx-generate-list : Generate sfx list include file along with SFX data (default:disabled).");
            Console.WriteLine($"");
            ShutdownConsole();
        }
Пример #2
0
        public bool Save(Project originalProject, string filename, int[] songIds, bool deleteUnusedData)
        {
            var project = originalProject.DeepClone();

            project.DeleteAllSongsBut(songIds, deleteUnusedData);

            SetInvariantCulture();

            var lines = new List <string>();

            var versionString = Utils.SplitVersionNumber(PlatformUtils.ApplicationVersion, out _);
            var projectLine   = $"Project{GenerateAttribute("Version", versionString)}{GenerateAttribute("TempoMode", TempoType.Names[project.TempoMode])}";

            if (project.Name != "")
            {
                projectLine += GenerateAttribute("Name", project.Name);
            }
            if (project.Author != "")
            {
                projectLine += GenerateAttribute("Author", project.Author);
            }
            if (project.Copyright != "")
            {
                projectLine += GenerateAttribute("Copyright", project.Copyright);
            }
            if (project.PalMode)
            {
                projectLine += GenerateAttribute("PAL", true);
            }

            if (project.UsesAnyExpansionAudio)
            {
                var expansionStrings = new List <string>();

                for (int i = ExpansionType.Start; i <= ExpansionType.End; i++)
                {
                    if (project.UsesExpansionAudio(i))
                    {
                        expansionStrings.Add(ExpansionType.ShortNames[i]);
                    }
                }

                projectLine += GenerateAttribute("Expansions", string.Join(",", expansionStrings));

                if (project.UsesN163Expansion)
                {
                    projectLine += GenerateAttribute("NumN163Channels", project.ExpansionNumN163Channels);
                }
            }

            lines.Add(projectLine);

            // DPCM samples
            foreach (var sample in project.Samples)
            {
                // We don't include any DPCM sample source data or processing data. We simply write the final
                // processed data. Including giant WAV files or asking other importers to implement all the
                // processing options is unrealistic.
                if (sample.HasAnyProcessingOptions)
                {
                    if (sample.SourceDataIsWav)
                    {
                        Log.LogMessage(LogSeverity.Warning, $"Sample {sample.Name} has WAV data as source. Only the final processed DMC data will be exported.");
                    }
                    else
                    {
                        Log.LogMessage(LogSeverity.Warning, $"Sample {sample.Name} has processing option(s) enabled. Only the final processed DMC data will be exported.");
                    }
                }

                sample.PermanentlyApplyAllProcessing();

                Debug.Assert(!sample.HasAnyProcessingOptions);

                lines.Add($"\tDPCMSample{GenerateAttribute("Name", sample.Name)}{GenerateAttribute("Data", String.Join("", sample.ProcessedData.Select(x => $"{x:x2}")))}");
            }

            // DPCM mappings
            for (int i = 0; i < project.SamplesMapping.Length; i++)
            {
                var mapping = project.SamplesMapping[i];

                if (mapping != null)
                {
                    lines.Add($"\tDPCMMapping{GenerateAttribute("Note", Note.GetFriendlyName(i + Note.DPCMNoteMin))}{GenerateAttribute("Sample", mapping.Sample.Name)}{GenerateAttribute("Pitch", mapping.Pitch)}{GenerateAttribute("Loop", mapping.Loop)}");
                }
            }

            // Instruments
            foreach (var instrument in project.Instruments)
            {
                var instrumentLine = $"\tInstrument{GenerateAttribute("Name", instrument.Name)}";
                if (instrument.IsExpansionInstrument)
                {
                    instrumentLine += GenerateAttribute("Expansion", ExpansionType.ShortNames[instrument.Expansion]);

                    if (instrument.IsFdsInstrument)
                    {
                        instrumentLine += GenerateAttribute("FdsWavePreset", WavePresetType.Names[instrument.FdsWavePreset]);
                        instrumentLine += GenerateAttribute("FdsModPreset", WavePresetType.Names[instrument.FdsModPreset]);
                        if (instrument.FdsMasterVolume != 0)
                        {
                            instrumentLine += GenerateAttribute("FdsMasterVolume", instrument.FdsMasterVolume);
                        }
                        if (instrument.FdsModSpeed != 0)
                        {
                            instrumentLine += GenerateAttribute("FdsModSpeed", instrument.FdsModSpeed);
                        }
                        if (instrument.FdsModDepth != 0)
                        {
                            instrumentLine += GenerateAttribute("FdsModDepth", instrument.FdsModDepth);
                        }
                        if (instrument.FdsModDelay != 0)
                        {
                            instrumentLine += GenerateAttribute("FdsModDelay", instrument.FdsModDelay);
                        }
                    }
                    else if (instrument.IsN163Instrument)
                    {
                        instrumentLine += GenerateAttribute("N163WavePreset", WavePresetType.Names[instrument.N163WavePreset]);
                        instrumentLine += GenerateAttribute("N163WaveSize", instrument.N163WaveSize);
                        instrumentLine += GenerateAttribute("N163WavePos", instrument.N163WavePos);
                    }
                    else if (instrument.IsVrc6Instrument)
                    {
                        instrumentLine += GenerateAttribute("Vrc6SawMasterVolume", Vrc6SawMasterVolumeType.Names[instrument.Vrc6SawMasterVolume]);
                    }
                    else if (instrument.IsVrc7Instrument)
                    {
                        instrumentLine += GenerateAttribute("Vrc7Patch", instrument.Vrc7Patch);

                        if (instrument.Vrc7Patch == Vrc7InstrumentPatch.Custom)
                        {
                            for (int i = 0; i < 8; i++)
                            {
                                instrumentLine += GenerateAttribute($"Vrc7Reg{i}", instrument.Vrc7PatchRegs[i]);
                            }
                        }
                    }
                }
                lines.Add(instrumentLine);

                for (int i = 0; i < EnvelopeType.Count; i++)
                {
                    var env = instrument.Envelopes[i];
                    if (env != null && !env.IsEmpty(i))
                    {
                        var envelopeLine = $"\t\tEnvelope{GenerateAttribute("Type", EnvelopeType.ShortNames[i])}{GenerateAttribute("Length", env.Length)}";

                        if (env.Loop >= 0)
                        {
                            envelopeLine += GenerateAttribute("Loop", env.Loop);
                        }
                        if (env.Release >= 0)
                        {
                            envelopeLine += GenerateAttribute("Release", env.Release);
                        }
                        if (env.Relative)
                        {
                            envelopeLine += GenerateAttribute("Relative", env.Relative);
                        }

                        envelopeLine += GenerateAttribute("Values", String.Join(",", env.Values.Take(env.Length)));

                        lines.Add(envelopeLine);
                    }
                }
            }

            // Arpeggios
            foreach (var arpeggio in project.Arpeggios)
            {
                var env          = arpeggio.Envelope;
                var arpeggioLine = $"\tArpeggio{GenerateAttribute("Name", arpeggio.Name)}{GenerateAttribute("Length", env.Length)}";
                if (env.Loop >= 0)
                {
                    arpeggioLine += GenerateAttribute("Loop", env.Loop);
                }
                arpeggioLine += GenerateAttribute("Values", String.Join(",", env.Values.Take(env.Length)));
                lines.Add(arpeggioLine);
            }

            // Songs
            foreach (var song in project.Songs)
            {
                var songStr = $"\tSong{GenerateAttribute("Name", song.Name)}{GenerateAttribute("Length", song.Length)}{GenerateAttribute("LoopPoint", song.LoopPoint)}";

                if (song.UsesFamiTrackerTempo)
                {
                    songStr += $"{GenerateAttribute("PatternLength", song.PatternLength)}{GenerateAttribute("BeatLength", song.BeatLength)}{GenerateAttribute("FamiTrackerTempo", song.FamitrackerTempo)}{GenerateAttribute("FamiTrackerSpeed", song.FamitrackerSpeed)}";
                }
                else
                {
                    songStr += $"{GenerateAttribute("PatternLength", song.PatternLength / song.NoteLength)}{GenerateAttribute("BeatLength", song.BeatLength / song.NoteLength)}{GenerateAttribute("NoteLength", song.NoteLength)}{GenerateAttribute("Groove", string.Join("-", song.Groove))}{GenerateAttribute("GroovePaddingMode", GroovePaddingType.Names[song.GroovePaddingMode])}";
                }

                lines.Add(songStr);

                for (int i = 0; i < song.Length; i++)
                {
                    if (song.PatternHasCustomSettings(i))
                    {
                        var patternLength = song.GetPatternLength(i);

                        if (song.UsesFamiTrackerTempo)
                        {
                            lines.Add($"\t\tPatternCustomSettings{GenerateAttribute("Time", i)}{GenerateAttribute("Length", patternLength)}");
                        }
                        else
                        {
                            var noteLength        = song.GetPatternNoteLength(i);
                            var beatLength        = song.GetPatternBeatLength(i);
                            var groove            = song.GetPatternGroove(i);
                            var groovePaddingMode = song.GetPatternGroovePaddingMode(i);

                            lines.Add($"\t\tPatternCustomSettings{GenerateAttribute("Time", i)}{GenerateAttribute("Length", patternLength / noteLength)}{GenerateAttribute("NoteLength", noteLength)}{GenerateAttribute("Groove", string.Join("-", groove))}{GenerateAttribute("GroovePaddingMode", GroovePaddingType.Names[groovePaddingMode])}{GenerateAttribute("BeatLength", beatLength / noteLength)}");
                        }
                    }
                }

                foreach (var channel in song.Channels)
                {
                    lines.Add($"\t\tChannel{GenerateAttribute("Type", ChannelType.ShortNames[channel.Type])}");

                    foreach (var pattern in channel.Patterns)
                    {
                        lines.Add($"\t\t\tPattern{GenerateAttribute("Name", pattern.Name)}");

                        foreach (var kv in pattern.Notes)
                        {
                            var note = kv.Value;

                            if (!note.IsEmpty)
                            {
                                var noteLine = $"\t\t\t\tNote{GenerateAttribute("Time", kv.Key)}";

                                if (note.IsMusicalOrStop)
                                {
                                    noteLine += GenerateAttribute("Value", note.FriendlyName);

                                    if (note.IsMusical)
                                    {
                                        noteLine += GenerateAttribute("Duration", note.Duration);

                                        if (note.HasRelease)
                                        {
                                            noteLine += GenerateAttribute("Release", note.Release);
                                        }
                                        if (note.Instrument != null)
                                        {
                                            noteLine += GenerateAttribute("Instrument", note.Instrument.Name);
                                        }
                                        if (note.IsArpeggio)
                                        {
                                            noteLine += GenerateAttribute("Arpeggio", note.Arpeggio.Name);
                                        }
                                        if (note.IsSlideNote)
                                        {
                                            noteLine += GenerateAttribute("SlideTarget", Note.GetFriendlyName(note.SlideNoteTarget));
                                        }
                                    }
                                }

                                if (!note.HasAttack)
                                {
                                    noteLine += GenerateAttribute("Attack", false);
                                }
                                if (note.HasVolume)
                                {
                                    noteLine += GenerateAttribute("Volume", note.Volume);
                                }
                                if (note.HasVolumeSlide)
                                {
                                    noteLine += GenerateAttribute("VolumeSlideTarget", note.VolumeSlideTarget);
                                }
                                if (note.HasVibrato)
                                {
                                    noteLine += $"{GenerateAttribute("VibratoSpeed", note.VibratoSpeed)}{GenerateAttribute("VibratoDepth", note.VibratoDepth)}";
                                }
                                if (note.HasSpeed)
                                {
                                    noteLine += GenerateAttribute("Speed", note.Speed);
                                }
                                if (note.HasFinePitch)
                                {
                                    noteLine += GenerateAttribute("FinePitch", note.FinePitch);
                                }
                                if (note.HasFdsModSpeed)
                                {
                                    noteLine += GenerateAttribute("FdsModSpeed", note.FdsModSpeed);
                                }
                                if (note.HasFdsModDepth)
                                {
                                    noteLine += GenerateAttribute("FdsModDepth", note.FdsModDepth);
                                }
                                if (note.HasDutyCycle)
                                {
                                    noteLine += GenerateAttribute("DutyCycle", note.DutyCycle);
                                }
                                if (note.HasNoteDelay)
                                {
                                    noteLine += GenerateAttribute("NoteDelay", note.NoteDelay);
                                }
                                if (note.HasCutDelay)
                                {
                                    noteLine += GenerateAttribute("CutDelay", note.CutDelay);
                                }

                                lines.Add(noteLine);
                            }
                        }
                    }

                    for (int p = 0; p < song.Length; p++)
                    {
                        var pattern = channel.PatternInstances[p];

                        if (pattern != null)
                        {
                            lines.Add($"\t\t\tPatternInstance{GenerateAttribute("Time", p)}{GenerateAttribute("Pattern", pattern.Name)}");
                        }
                    }
                }
            }

            File.WriteAllLines(filename, lines);

            ResetCulture();
            return(true);
        }