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(); }
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); }