public bool Save(Project originalProject, string filename, int[] songIds) { var project = originalProject.DeepClone(); project.RemoveAllSongsBut(songIds); var lines = new List <string>(); var versionString = Application.ProductVersion.Substring(0, Application.ProductVersion.LastIndexOf('.')); var projectLine = $"Project Version=\"{versionString}\" TempoMode=\"{Project.TempoModeNames[project.TempoMode]}\""; if (project.Name != "") { projectLine += $" Name=\"{project.Name}\""; } if (project.Author != "") { projectLine += $" Author=\"{project.Author}\""; } if (project.Copyright != "") { projectLine += $" Copyright=\"{project.Copyright}\""; } if (project.UsesExpansionAudio) { projectLine += $" Expansion=\"{Project.ExpansionShortNames[project.ExpansionAudio]}\""; } if (project.PalMode) { projectLine += $" PAL=\"{true}\""; } lines.Add(projectLine); // DPCM samples foreach (var sample in project.Samples) { lines.Add($"\tDPCMSample Name=\"{sample.Name}\" Data=\"{String.Join("", sample.Data.Select(x => $"{x:x2}"))}\""); } // DPCM mappings for (int i = 0; i < project.SamplesMapping.Length; i++) { var mapping = project.SamplesMapping[i]; if (mapping != null && mapping.Sample != null) { lines.Add($"\tDPCMMapping Note=\"{Note.GetFriendlyName(i + Note.DPCMNoteMin)}\" Sample=\"{mapping.Sample.Name}\" Pitch=\"{mapping.Pitch}\" Loop=\"{mapping.Loop}\""); } } // Instruments foreach (var instrument in project.Instruments) { var instrumentLine = $"\tInstrument Name=\"{instrument.Name}\""; if (instrument.IsExpansionInstrument) { instrumentLine += $" Expansion=\"{Project.ExpansionShortNames[project.ExpansionAudio]}\""; if (instrument.ExpansionType == Project.ExpansionFds) { instrumentLine += $" FdsWavePreset=\"{Envelope.PresetNames[instrument.FdsWavePreset]}\""; instrumentLine += $" FdsModPreset=\"{Envelope.PresetNames[instrument.FdsModPreset]}\""; if (instrument.FdsMasterVolume != 0) { instrumentLine += $" FdsMasterVolume=\"{instrument.FdsMasterVolume}\""; } if (instrument.FdsModSpeed != 0) { instrumentLine += $" FdsModSpeed=\"{instrument.FdsModSpeed}\""; } if (instrument.FdsModDepth != 0) { instrumentLine += $" FdsModDepth=\"{instrument.FdsModDepth}\""; } if (instrument.FdsModDelay != 0) { instrumentLine += $" FdsModDelay=\"{instrument.FdsModDelay}\""; } } else if (instrument.ExpansionType == Project.ExpansionN163) { instrumentLine += $" N163WavePreset=\"{Envelope.PresetNames[instrument.N163WavePreset]}\""; instrumentLine += $" N163WaveSize=\"{instrument.N163WaveSize}\""; instrumentLine += $" N163WavePos=\"{instrument.N163WavePos}\""; } else if (instrument.ExpansionType == Project.ExpansionVrc7) { instrumentLine += $" Vrc7Patch=\"{instrument.Vrc7Patch}\""; if (instrument.Vrc7Patch == 0) { for (int i = 0; i < 8; i++) { instrumentLine += $" Vrc7Reg{i}=\"{instrument.Vrc7PatchRegs[i]}\""; } } } } lines.Add(instrumentLine); for (int i = 0; i < Envelope.Count; i++) { var env = instrument.Envelopes[i]; if (env != null && !env.IsEmpty) { var envelopeLine = $"\t\tEnvelope Type=\"{Envelope.EnvelopeShortNames[i]}\" Length=\"{env.Length}\""; if (env.Loop >= 0) { envelopeLine += $" Loop=\"{env.Loop}\""; } if (env.Release >= 0) { envelopeLine += $" Release=\"{env.Release}\""; } if (env.Relative) { envelopeLine += $" Relative=\"{env.Relative}\""; } envelopeLine += $" Values=\"{String.Join(",", env.Values.Take(env.Length))}\""; lines.Add(envelopeLine); } } } // Songs foreach (var song in project.Songs) { var songStr = $"\tSong Name=\"{song.Name}\" Length=\"{song.Length}\" LoopPoint=\"{song.LoopPoint}\""; if (song.UsesFamiTrackerTempo) { songStr += $" PatternLength=\"{song.PatternLength}\" BarLength=\"{song.BarLength}\" FamiTrackerTempo=\"{song.FamitrackerTempo}\" FamiTrackerSpeed=\"{song.FamitrackerSpeed}\""; } else { songStr += $" PatternLength=\"{song.PatternLength / song.NoteLength}\" BarLength=\"{song.BarLength / song.NoteLength}\" NoteLength=\"{song.NoteLength}\""; } 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 Time=\"{i}\" Length=\"{patternLength}\""); } else { var noteLength = song.GetPatternNoteLength(i); var barLength = song.GetPatternBarLength(i); lines.Add($"\t\tPatternCustomSettings Time=\"{i}\" Length=\"{patternLength / noteLength}\" NoteLength=\"{noteLength}\" BarLength=\"{barLength / noteLength}\""); } } } foreach (var channel in song.Channels) { lines.Add($"\t\tChannel Type=\"{Channel.ChannelExportNames[channel.Type]}\""); foreach (var pattern in channel.Patterns) { lines.Add($"\t\t\tPattern Name=\"{pattern.Name}\""); foreach (var kv in pattern.Notes) { var note = kv.Value; if (!note.IsEmpty) { var noteLine = $"\t\t\t\tNote Time=\"{kv.Key}\""; if (note.IsValid) { noteLine += $" Value=\"{note.FriendlyName}\""; if (note.Instrument != null) { noteLine += $" Instrument=\"{note.Instrument.Name}\""; } } if (!note.HasAttack) { noteLine += $" Attack=\"{false.ToString()}\""; } if (note.HasVolume) { noteLine += $" Volume=\"{note.Volume}\""; } if (note.HasVibrato) { noteLine += $" VibratoSpeed=\"{note.VibratoSpeed}\" VibratoDepth=\"{note.VibratoDepth}\""; } if (note.HasSpeed) { noteLine += $" Speed=\"{note.Speed}\""; } if (note.HasFinePitch) { noteLine += $" FinePitch=\"{note.FinePitch}\""; } if (note.HasFdsModSpeed) { noteLine += $" FdsModSpeed=\"{note.FdsModSpeed}\""; } if (note.HasFdsModDepth) { noteLine += $" FdsModDepth=\"{note.FdsModDepth}\""; } if (note.IsMusical && note.IsSlideNote) { noteLine += $" SlideTarget=\"{Note.GetFriendlyName(note.SlideNoteTarget)}\""; } lines.Add(noteLine); } } } for (int p = 0; p < song.Length; p++) { var pattern = channel.PatternInstances[p]; if (pattern != null) { lines.Add($"\t\t\tPatternInstance Time=\"{p}\" Pattern=\"{pattern.Name}\""); } } } } File.WriteAllLines(filename, lines); return(true); }
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); }
private void ReadInstrument2A03(Instrument instrument, int instIdx, ref int idx) { ReadCommonEnvelopes(instrument, instIdx, ref idx, envelopes); for (int i = 0; i < OctaveRange; ++i) { for (int j = 0; j < 12; ++j) { var index = bytes[idx++]; var pitch = bytes[idx++]; if (blockVersion > 5) { idx++; // sample delta } if (index > 0 && pitch != 0) { var sample = samples[index - 1]; var note = i * 12 + j + 1; if (sample != null && sample.ProcessedData != null) { if (project.NoteSupportsDPCM(note)) { if (project.GetDPCMMapping(note) == null) { project.MapDPCMSample(note, sample, pitch & 0x0f, (pitch & 0x80) != 0); } else { Log.LogMessage(LogSeverity.Warning, $"Multiple instruments assigning DPCM samples to key {Note.GetFriendlyName(note)}. Only the first one will be assigned, others will be loaded, but unassigned."); } } else { Log.LogMessage(LogSeverity.Warning, $"DPCM sample assigned to key {Note.GetFriendlyName(note)}. FamiStudio only supports DPCM samples on keys {Note.GetFriendlyName(Note.DPCMNoteMin + 1)} to {Note.GetFriendlyName(Note.DPCMNoteMax)}."); } } } } } }