private void ComputeChannelsScroll(VideoFrameMetadata[] frames, int channelMask, int numVisibleNotes) { var numFrames = frames.Length; var numChannels = frames[0].channelNotes.Length; for (int c = 0; c < numChannels; c++) { if ((channelMask & (1 << c)) == 0) { continue; } // Go through all the frames and split them in segments. // A segment is a section of the song where all the notes fit in the view. var segments = new List <ScrollSegment>(); var currentSegment = (ScrollSegment)null; var minOverallNote = int.MaxValue; var maxOverallNote = int.MinValue; for (int f = 0; f < numFrames; f++) { var frame = frames[f]; var note = frame.channelNotes[c]; if (frame.scroll == null) { frame.scroll = new float[numChannels]; } if (note.IsMusical) { if (currentSegment == null) { currentSegment = new ScrollSegment(); segments.Add(currentSegment); } // If its the start of a new pattern and we've been not moving for ~10 sec, let's start a new segment. bool forceNewSegment = frame.playNote == 0 && (f - currentSegment.startFrame) > 600; var minNoteValue = note.Value - 1; var maxNoteValue = note.Value + 1; // Only consider slides if they arent too large. if (note.IsSlideNote && Math.Abs(note.SlideNoteTarget - note.Value) < numVisibleNotes / 2) { minNoteValue = Math.Min(note.Value, note.SlideNoteTarget) - 1; maxNoteValue = Math.Max(note.Value, note.SlideNoteTarget) + 1; } // Only consider arpeggios if they are not too big. if (note.IsArpeggio && note.Arpeggio.GetChordMinMaxOffset(out var minArp, out var maxArp) && maxArp - minArp < numVisibleNotes / 2) { minNoteValue = note.Value + minArp; maxNoteValue = note.Value + maxArp; } minOverallNote = Math.Min(minOverallNote, minNoteValue); maxOverallNote = Math.Max(maxOverallNote, maxNoteValue); var newMinNote = Math.Min(currentSegment.minNote, minNoteValue); var newMaxNote = Math.Max(currentSegment.maxNote, maxNoteValue); // If we cant fit the next note in the view, start a new segment. if (forceNewSegment || newMaxNote - newMinNote + 1 > numVisibleNotes) { currentSegment.endFrame = f; currentSegment = new ScrollSegment(); currentSegment.startFrame = f; segments.Add(currentSegment); currentSegment.minNote = minNoteValue; currentSegment.maxNote = maxNoteValue; } else { currentSegment.minNote = newMinNote; currentSegment.maxNote = newMaxNote; } } } // Not a single notes in this channel... if (currentSegment == null) { currentSegment = new ScrollSegment(); currentSegment.minNote = Note.FromFriendlyName("C4"); currentSegment.maxNote = currentSegment.minNote; segments.Add(currentSegment); } currentSegment.endFrame = numFrames; // Remove very small segments, these make the camera move too fast, looks bad. var shortestAllowedSegment = SegmentTransitionNumFrames * 2; bool removed = false; do { var sortedSegment = new List <ScrollSegment>(segments); sortedSegment.Sort((s1, s2) => s1.NumFrames.CompareTo(s2.NumFrames)); if (sortedSegment[0].NumFrames >= shortestAllowedSegment) { break; } for (int s = 0; s < sortedSegment.Count; s++) { var seg = sortedSegment[s]; if (seg.NumFrames >= shortestAllowedSegment) { break; } var thisSegmentIndex = segments.IndexOf(seg); // Segment is too short, see if we can merge with previous/next one. var mergeSegmentIndex = -1; var mergeSegmentLength = -1; if (thisSegmentIndex > 0) { mergeSegmentIndex = thisSegmentIndex - 1; mergeSegmentLength = segments[thisSegmentIndex - 1].NumFrames; } if (thisSegmentIndex != segments.Count - 1 && segments[thisSegmentIndex + 1].NumFrames > mergeSegmentLength) { mergeSegmentIndex = thisSegmentIndex + 1; mergeSegmentLength = segments[thisSegmentIndex + 1].NumFrames; } if (mergeSegmentIndex >= 0) { // Merge. var mergeSeg = segments[mergeSegmentIndex]; mergeSeg.startFrame = Math.Min(mergeSeg.startFrame, seg.startFrame); mergeSeg.endFrame = Math.Max(mergeSeg.endFrame, seg.endFrame); segments.RemoveAt(thisSegmentIndex); removed = true; break; } } }while (removed); // Build the actually scrolling data. var minScroll = (float)Math.Ceiling(Note.MusicalNoteMin + numVisibleNotes * 0.5f); var maxScroll = (float)Math.Floor(Note.MusicalNoteMax - numVisibleNotes * 0.5f); Debug.Assert(maxScroll >= minScroll); foreach (var segment in segments) { segment.scroll = Utils.Clamp(segment.minNote + (segment.maxNote - segment.minNote) * 0.5f, minScroll, maxScroll); } for (var s = 0; s < segments.Count; s++) { var segment0 = segments[s + 0]; var segment1 = s == segments.Count - 1 ? null : segments[s + 1]; for (int f = segment0.startFrame; f < segment0.endFrame - (segment1 == null ? 0 : SegmentTransitionNumFrames); f++) { frames[f].scroll[c] = segment0.scroll; } if (segment1 != null) { // Smooth transition to next segment. for (int f = segment0.endFrame - SegmentTransitionNumFrames, a = 0; f < segment0.endFrame; f++, a++) { var lerp = a / (float)SegmentTransitionNumFrames; frames[f].scroll[c] = Utils.Lerp(segment0.scroll, segment1.scroll, Utils.SmootherStep(lerp)); } } } } }
public Project Load(string filename) { try { var lines = File.ReadAllLines(filename); var parameters = new Dictionary <string, string>(); var project = (Project)null; var instrument = (Instrument)null; var song = (Song)null; var channel = (Channel)null; var pattern = (Pattern)null; foreach (var line in lines) { var cmd = SplitLine(line.Trim(), ref parameters); switch (cmd) { case "Project": { project = new Project(); if (parameters.TryGetValue("Name", out var name)) { project.Name = name; } if (parameters.TryGetValue("Author", out var author)) { project.Author = author; } if (parameters.TryGetValue("Copyright", out var copyright)) { project.Copyright = copyright; } if (parameters.TryGetValue("Expansion", out var expansion)) { project.SetExpansionAudio(Array.IndexOf(Project.ExpansionShortNames, expansion)); } if (parameters.TryGetValue("TempoMode", out var tempoMode)) { project.TempoMode = Array.IndexOf(Project.TempoModeNames, tempoMode); } if (parameters.TryGetValue("PAL", out var pal)) { project.PalMode = bool.Parse(pal); } break; } case "DPCMSample": { var str = parameters["Data"]; var data = new byte[str.Length / 2]; for (int i = 0; i < data.Length; i++) { data[i] = Convert.ToByte(str.Substring(i * 2, 2), 16); } project.CreateDPCMSample(parameters["Name"], data); break; } case "DPCMMapping": { var pitch = 15; var loop = false; if (parameters.TryGetValue("Pitch", out var pitchStr)) { pitch = int.Parse(pitchStr); } if (parameters.TryGetValue("Loop", out var loopStr)) { loop = bool.Parse(loopStr); } project.MapDPCMSample(Note.FromFriendlyName(parameters["Note"]), project.GetSample(parameters["Sample"]), pitch, loop); break; } case "Instrument": { instrument = project.CreateInstrument(parameters.TryGetValue("Expansion", out _) ? project.ExpansionAudio : Project.ExpansionNone, parameters["Name"]); if (instrument.ExpansionType == Project.ExpansionFds) { if (parameters.TryGetValue("FdsWavePreset", out var wavPresetStr)) { instrument.FdsWavePreset = (byte)Array.IndexOf(Envelope.PresetNames, wavPresetStr); } if (parameters.TryGetValue("FdsModPreset", out var modPresetStr)) { instrument.FdsWavePreset = (byte)Array.IndexOf(Envelope.PresetNames, modPresetStr); } if (parameters.TryGetValue("FdsMasterVolume", out var masterVolumeStr)) { instrument.FdsMasterVolume = byte.Parse(masterVolumeStr); } if (parameters.TryGetValue("FdsModSpeed", out var fdsModSpeedStr)) { instrument.FdsModSpeed = ushort.Parse(fdsModSpeedStr); } if (parameters.TryGetValue("FdsModDepth", out var fdsModDepthStr)) { instrument.FdsModDepth = byte.Parse(fdsModDepthStr); } if (parameters.TryGetValue("FdsModDelay", out var fdsModDelayStr)) { instrument.FdsModDelay = byte.Parse(fdsModDelayStr); } } else if (instrument.ExpansionType == Project.ExpansionN163) { if (parameters.TryGetValue("N163WavePreset", out var wavPresetStr)) { instrument.N163WavePreset = (byte)Array.IndexOf(Envelope.PresetNames, wavPresetStr); } if (parameters.TryGetValue("N163WaveSize", out var n163WavSizeStr)) { instrument.N163WaveSize = byte.Parse(n163WavSizeStr); } if (parameters.TryGetValue("N163WavePos", out var n163WavPosStr)) { instrument.N163WavePos = byte.Parse(n163WavPosStr); } } else if (instrument.ExpansionType == Project.ExpansionVrc7) { if (parameters.TryGetValue("Vrc7Patch", out var vrc7PatchStr)) { instrument.Vrc7Patch = byte.Parse(vrc7PatchStr); } if (instrument.Vrc7Patch == 0) { for (int i = 0; i < 8; i++) { if (parameters.TryGetValue($"Vrc7Reg{i}", out var regStr)) { instrument.Vrc7PatchRegs[i] = byte.Parse(regStr); } } } } break; } case "Envelope": { var env = instrument.Envelopes[Array.IndexOf(Envelope.EnvelopeShortNames, parameters["Type"])]; if (env != null) { if (env.CanResize) { env.Length = int.Parse(parameters["Length"]); } if (parameters.TryGetValue("Loop", out var loopStr)) { env.Loop = int.Parse(loopStr); } if (parameters.TryGetValue("Release", out var releaseStr)) { env.Release = int.Parse(releaseStr); } if (parameters.TryGetValue("Relative", out var relativeStr)) { env.Relative = bool.Parse(relativeStr); } var values = parameters["Values"].Split(','); for (int j = 0; j < values.Length; j++) { env.Values[j] = sbyte.Parse(values[j]); } } break; } case "Song": { song = project.CreateSong(parameters["Name"]); song.SetLength(int.Parse(parameters["Length"])); song.SetBarLength(int.Parse(parameters["BarLength"])); song.SetLoopPoint(int.Parse(parameters["LoopPoint"])); if (song.UsesFamiTrackerTempo) { song.SetDefaultPatternLength(int.Parse(parameters["PatternLength"])); song.FamitrackerTempo = int.Parse(parameters["FamiTrackerTempo"]); song.FamitrackerSpeed = int.Parse(parameters["FamiTrackerSpeed"]); } else { var noteLength = int.Parse(parameters["NoteLength"]); song.ResizeNotes(noteLength, false); song.SetBarLength(int.Parse(parameters["BarLength"]) * noteLength); song.SetDefaultPatternLength(int.Parse(parameters["PatternLength"]) * noteLength); } break; } case "PatternCustomSettings": { if (project.UsesFamiTrackerTempo) { song.SetPatternCustomSettings(int.Parse(parameters["Time"]), int.Parse(parameters["Length"])); } else { var patternLength = int.Parse(parameters["Length"]); var noteLength = int.Parse(parameters["NoteLength"]); var barLength = int.Parse(parameters["BarLength"]); song.SetPatternCustomSettings(int.Parse(parameters["Time"]), patternLength * noteLength, noteLength, barLength * noteLength); } break; } case "Channel": { var channelType = Array.IndexOf(Channel.ChannelExportNames, parameters["Type"]); channel = song.GetChannelByType(channelType); break; } case "Pattern": { pattern = channel.CreatePattern(parameters["Name"]); break; } case "Note": { var time = int.Parse(parameters["Time"]); var note = pattern.GetOrCreateNoteAt(time); if (parameters.TryGetValue("Value", out var valueStr)) { note.Value = (byte)Note.FromFriendlyName(valueStr); } if (parameters.TryGetValue("Instrument", out var instStr)) { note.Instrument = project.GetInstrument(instStr); } if (parameters.TryGetValue("Attack", out var attackStr)) { note.HasAttack = bool.Parse(attackStr); } if (parameters.TryGetValue("Volume", out var volumeStr)) { note.Volume = byte.Parse(volumeStr); } if (parameters.TryGetValue("VibratoSpeed", out var vibSpeedStr)) { note.VibratoSpeed = byte.Parse(vibSpeedStr); } if (parameters.TryGetValue("VibratoDepth", out var vibDepthStr)) { note.VibratoDepth = byte.Parse(vibDepthStr); } if (parameters.TryGetValue("FinePitch", out var finePitchStr)) { note.FinePitch = sbyte.Parse(finePitchStr); } if (parameters.TryGetValue("SlideTarget", out var slideStr)) { note.SlideNoteTarget = (byte)Note.FromFriendlyName(valueStr); } if (parameters.TryGetValue("FdsModSpeed", out var modSpeedStr)) { note.FdsModSpeed = ushort.Parse(modSpeedStr); } if (parameters.TryGetValue("FdsModDepth", out var modDepthStr)) { note.FdsModDepth = byte.Parse(modDepthStr); } break; } case "PatternInstance": { var time = int.Parse(parameters["Time"]); channel.PatternInstances[time] = channel.GetPattern(parameters["Pattern"]); break; } } } return(project); } catch { return(null); } }
public Project Load(string filename) { #if !DEBUG try #endif { var lines = File.ReadAllLines(filename); var parameters = new Dictionary <string, string>(); var project = (Project)null; var instrument = (Instrument)null; var arpeggio = (Arpeggio)null; var song = (Song)null; var channel = (Channel)null; var pattern = (Pattern)null; SetInvariantCulture(); foreach (var line in lines) { var cmd = SplitLine(line.Trim(), ref parameters); switch (cmd) { case "Project": { project = new Project(); parameters.TryGetValue("Version", out var version); if (parameters.TryGetValue("Name", out var name)) { project.Name = name; } if (parameters.TryGetValue("Author", out var author)) { project.Author = author; } if (parameters.TryGetValue("Copyright", out var copyright)) { project.Copyright = copyright; } if (parameters.TryGetValue("TempoMode", out var tempoMode)) { project.TempoMode = TempoType.GetValueForName(tempoMode); } if (parameters.TryGetValue("PAL", out var pal)) { project.PalMode = bool.Parse(pal); } if (parameters.TryGetValue("Expansions", out var expansions)) { var expansionMask = 0; var expansionStrings = expansions.Split(','); foreach (var s in expansionStrings) { var exp = ExpansionType.GetValueForShortName(s.Trim()); expansionMask |= ExpansionType.GetMaskFromValue(exp); } var numN163Channels = 1; if ((expansionMask & ExpansionType.N163Mask) != 0 && parameters.TryGetValue("NumN163Channels", out var numN163ChannelsStr)) { numN163Channels = int.Parse(numN163ChannelsStr); } project.SetExpansionAudioMask(expansionMask, numN163Channels); } if (!version.StartsWith("3.2")) { Log.LogMessage(LogSeverity.Error, "File was created with an incompatible version of FamiStudio. The text format is only compatible with the current version."); return(null); } break; } case "DPCMSample": { var str = parameters["Data"]; var data = new byte[str.Length / 2]; for (int i = 0; i < data.Length; i++) { data[i] = Convert.ToByte(str.Substring(i * 2, 2), 16); } var sample = project.CreateDPCMSampleFromDmcData(parameters["Name"], data); break; } case "DPCMMapping": { var pitch = 15; var loop = false; if (parameters.TryGetValue("Pitch", out var pitchStr)) { pitch = int.Parse(pitchStr); } if (parameters.TryGetValue("Loop", out var loopStr)) { loop = bool.Parse(loopStr); } project.MapDPCMSample(Note.FromFriendlyName(parameters["Note"]), project.GetSample(parameters["Sample"]), pitch, loop); break; } case "Instrument": { var instrumentExp = ExpansionType.None; if (parameters.TryGetValue("Expansion", out var instrumentExpStr)) { instrumentExp = ExpansionType.GetValueForShortName(instrumentExpStr); } instrument = project.CreateInstrument(instrumentExp, parameters["Name"]); if (instrument.IsFdsInstrument) { if (parameters.TryGetValue("FdsWavePreset", out var wavPresetStr)) { instrument.FdsWavePreset = (byte)WavePresetType.GetValueForName(wavPresetStr); } if (parameters.TryGetValue("FdsModPreset", out var modPresetStr)) { instrument.FdsWavePreset = (byte)WavePresetType.GetValueForName(modPresetStr); } if (parameters.TryGetValue("FdsMasterVolume", out var masterVolumeStr)) { instrument.FdsMasterVolume = byte.Parse(masterVolumeStr); } if (parameters.TryGetValue("FdsModSpeed", out var fdsModSpeedStr)) { instrument.FdsModSpeed = ushort.Parse(fdsModSpeedStr); } if (parameters.TryGetValue("FdsModDepth", out var fdsModDepthStr)) { instrument.FdsModDepth = byte.Parse(fdsModDepthStr); } if (parameters.TryGetValue("FdsModDelay", out var fdsModDelayStr)) { instrument.FdsModDelay = byte.Parse(fdsModDelayStr); } } else if (instrument.IsN163Instrument) { if (parameters.TryGetValue("N163WavePreset", out var wavPresetStr)) { instrument.N163WavePreset = (byte)WavePresetType.GetValueForName(wavPresetStr); } if (parameters.TryGetValue("N163WaveSize", out var n163WavSizeStr)) { instrument.N163WaveSize = byte.Parse(n163WavSizeStr); } if (parameters.TryGetValue("N163WavePos", out var n163WavPosStr)) { instrument.N163WavePos = byte.Parse(n163WavPosStr); } } else if (instrument.IsVrc6Instrument) { if (parameters.TryGetValue("Vrc6SawMasterVolume", out var vrc6SawVolumeStr)) { instrument.Vrc6SawMasterVolume = (byte)Vrc6SawMasterVolumeType.GetValueForName(vrc6SawVolumeStr); } } else if (instrument.IsVrc7Instrument) { if (parameters.TryGetValue("Vrc7Patch", out var vrc7PatchStr)) { instrument.Vrc7Patch = byte.Parse(vrc7PatchStr); } if (instrument.Vrc7Patch == Vrc7InstrumentPatch.Custom) { for (int i = 0; i < 8; i++) { if (parameters.TryGetValue($"Vrc7Reg{i}", out var regStr)) { instrument.Vrc7PatchRegs[i] = byte.Parse(regStr); } } } } break; } case "Arpeggio": { arpeggio = project.CreateArpeggio(parameters["Name"]); arpeggio.Envelope.Length = int.Parse(parameters["Length"]); if (parameters.TryGetValue("Loop", out var loopStr)) { arpeggio.Envelope.Loop = int.Parse(loopStr); } var values = parameters["Values"].Split(','); for (int j = 0; j < values.Length; j++) { arpeggio.Envelope.Values[j] = sbyte.Parse(values[j]); } break; } case "Envelope": { var env = instrument.Envelopes[EnvelopeType.GetValueForShortName(parameters["Type"])]; if (env != null) { if (env.CanResize) { env.Length = int.Parse(parameters["Length"]); } if (parameters.TryGetValue("Loop", out var loopStr)) { env.Loop = int.Parse(loopStr); } if (parameters.TryGetValue("Release", out var releaseStr)) { env.Release = int.Parse(releaseStr); } if (parameters.TryGetValue("Relative", out var relativeStr)) { env.Relative = bool.Parse(relativeStr); } var values = parameters["Values"].Split(','); for (int j = 0; j < values.Length; j++) { env.Values[j] = sbyte.Parse(values[j]); } } break; } case "Song": { song = project.CreateSong(parameters["Name"]); song.SetLength(int.Parse(parameters["Length"])); song.SetBeatLength(int.Parse(parameters["BeatLength"])); song.SetLoopPoint(int.Parse(parameters["LoopPoint"])); if (song.UsesFamiTrackerTempo) { song.SetDefaultPatternLength(int.Parse(parameters["PatternLength"])); song.FamitrackerTempo = int.Parse(parameters["FamiTrackerTempo"]); song.FamitrackerSpeed = int.Parse(parameters["FamiTrackerSpeed"]); } else { var noteLength = int.Parse(parameters["NoteLength"]); var groove = parameters["Groove"].Split('-').Select(Int32.Parse).ToArray(); var groovePaddingMode = GroovePaddingType.GetValueForName(parameters["GroovePaddingMode"]); if (!FamiStudioTempoUtils.ValidateGroove(groove) || Utils.Min(groove) != noteLength) { Log.LogMessage(LogSeverity.Error, "Invalid tempo settings."); return(null); } song.ChangeFamiStudioTempoGroove(groove, false); song.SetBeatLength(song.BeatLength * noteLength); song.SetDefaultPatternLength(int.Parse(parameters["PatternLength"]) * noteLength); song.SetGroovePaddingMode(groovePaddingMode); } break; } case "PatternCustomSettings": { if (project.UsesFamiTrackerTempo) { var beatLength = song.BeatLength; if (parameters.TryGetValue("BeatLength", out var beatLengthStr)) { beatLength = int.Parse(beatLengthStr); } song.SetPatternCustomSettings(int.Parse(parameters["Time"]), int.Parse(parameters["Length"]), beatLength); } else { var patternLength = int.Parse(parameters["Length"]); var noteLength = int.Parse(parameters["NoteLength"]); var beatLength = int.Parse(parameters["BeatLength"]); var groove = parameters["Groove"].Split('-').Select(Int32.Parse).ToArray(); var groovePaddingMode = GroovePaddingType.GetValueForName(parameters["GroovePaddingMode"]); if (!FamiStudioTempoUtils.ValidateGroove(groove) || Utils.Min(groove) != noteLength) { Log.LogMessage(LogSeverity.Error, "Invalid tempo settings."); return(null); } song.SetPatternCustomSettings(int.Parse(parameters["Time"]), patternLength * noteLength, beatLength * noteLength, groove, groovePaddingMode); } break; } case "Channel": { var channelType = ChannelType.GetValueForShortName(parameters["Type"]); channel = song.GetChannelByType(channelType); break; } case "Pattern": { pattern = channel.CreatePattern(parameters["Name"]); break; } case "Note": { var time = int.Parse(parameters["Time"]); var note = pattern.GetOrCreateNoteAt(time); if (parameters.TryGetValue("Value", out var valueStr)) { note.Value = (byte)Note.FromFriendlyName(valueStr); } if (note.IsMusical && parameters.TryGetValue("Duration", out var durationStr)) { note.Duration = int.Parse(durationStr); } else if (note.IsStop) { note.Duration = 1; } if (note.IsMusical && parameters.TryGetValue("Release", out var releaseStr)) { note.Release = int.Parse(releaseStr); } if (note.IsMusical && parameters.TryGetValue("Instrument", out var instStr) && channel.SupportsInstrument(project.GetInstrument(instStr))) { note.Instrument = project.GetInstrument(instStr); } if (note.IsMusical && parameters.TryGetValue("Arpeggio", out var arpStr) && channel.SupportsArpeggios) { note.Arpeggio = project.GetArpeggio(arpStr); } if (note.IsMusical && parameters.TryGetValue("SlideTarget", out var slideStr) && channel.SupportsSlideNotes) { note.SlideNoteTarget = (byte)Note.FromFriendlyName(slideStr); } if (note.IsMusical && parameters.TryGetValue("Attack", out var attackStr)) { note.HasAttack = bool.Parse(attackStr); } if (parameters.TryGetValue("Volume", out var volumeStr) && channel.SupportsEffect(Note.EffectVolume)) { note.Volume = byte.Parse(volumeStr); } if (parameters.TryGetValue("VolumeSlideTarget", out var volumeSlideStr) && channel.SupportsEffect(Note.EffectVolumeSlide)) { note.VolumeSlideTarget = byte.Parse(volumeSlideStr); } if (parameters.TryGetValue("VibratoSpeed", out var vibSpeedStr) && channel.SupportsEffect(Note.EffectVibratoSpeed)) { note.VibratoSpeed = byte.Parse(vibSpeedStr); } if (parameters.TryGetValue("VibratoDepth", out var vibDepthStr) && channel.SupportsEffect(Note.EffectVibratoDepth)) { note.VibratoDepth = byte.Parse(vibDepthStr); } if (parameters.TryGetValue("Speed", out var speedStr) && channel.SupportsEffect(Note.EffectSpeed)) { note.Speed = byte.Parse(speedStr); } if (parameters.TryGetValue("FinePitch", out var finePitchStr) && channel.SupportsEffect(Note.EffectFinePitch)) { note.FinePitch = sbyte.Parse(finePitchStr); } if (parameters.TryGetValue("FdsModSpeed", out var modSpeedStr) && channel.SupportsEffect(Note.EffectFdsModSpeed)) { note.FdsModSpeed = ushort.Parse(modSpeedStr); } if (parameters.TryGetValue("FdsModDepth", out var modDepthStr) && channel.SupportsEffect(Note.EffectFdsModDepth)) { note.FdsModDepth = byte.Parse(modDepthStr); } if (parameters.TryGetValue("DutyCycle", out var dutyCycleStr) && channel.SupportsEffect(Note.EffectDutyCycle)) { note.DutyCycle = byte.Parse(dutyCycleStr); } if (parameters.TryGetValue("NoteDelay", out var noteDelayStr) && channel.SupportsEffect(Note.EffectNoteDelay)) { note.NoteDelay = byte.Parse(noteDelayStr); } if (parameters.TryGetValue("CutDelay", out var cutDelayStr) && channel.SupportsEffect(Note.EffectCutDelay)) { note.CutDelay = byte.Parse(cutDelayStr); } break; } case "PatternInstance": { var time = int.Parse(parameters["Time"]); channel.PatternInstances[time] = channel.GetPattern(parameters["Pattern"]); break; } } } project.SortEverything(false); ResetCulture(); return(project); } #if !DEBUG catch (Exception e) { Log.LogMessage(LogSeverity.Error, "Please contact the developer on GitHub!"); Log.LogMessage(LogSeverity.Error, e.Message); Log.LogMessage(LogSeverity.Error, e.StackTrace); ResetCulture(); return(null); } #endif }
public Project Load(string filename) { #if !DEBUG try #endif { var lines = File.ReadAllLines(filename); var parameters = new Dictionary <string, string>(); var project = (Project)null; var instrument = (Instrument)null; var arpeggio = (Arpeggio)null; var song = (Song)null; var channel = (Channel)null; var pattern = (Pattern)null; var version = "0.0.0"; var isVersion230OrNewer = false; var isVersion240OrNewer = false; var beatLengthAttributeName = "BeatLength"; foreach (var line in lines) { var cmd = SplitLine(line.Trim(), ref parameters); switch (cmd) { case "Project": { project = new Project(); parameters.TryGetValue("Version", out version); if (parameters.TryGetValue("Name", out var name)) { project.Name = name; } if (parameters.TryGetValue("Author", out var author)) { project.Author = author; } if (parameters.TryGetValue("Copyright", out var copyright)) { project.Copyright = copyright; } if (parameters.TryGetValue("Expansion", out var expansion)) { project.SetExpansionAudio(ExpansionType.GetValueForShortName(expansion)); } if (parameters.TryGetValue("TempoMode", out var tempoMode)) { project.TempoMode = TempoType.GetValueForName(tempoMode); } if (parameters.TryGetValue("PAL", out var pal)) { project.PalMode = bool.Parse(pal); } isVersion230OrNewer = string.CompareOrdinal(version, "2.3.0") >= 0; isVersion240OrNewer = string.CompareOrdinal(version, "2.4.0") >= 0; if (!isVersion230OrNewer) { beatLengthAttributeName = "BarLength"; } break; } case "DPCMSample": { var str = parameters["Data"]; var data = new byte[str.Length / 2]; for (int i = 0; i < data.Length; i++) { data[i] = Convert.ToByte(str.Substring(i * 2, 2), 16); } var sample = project.CreateDPCMSampleFromDmcData(parameters["Name"], data); // Any DPCM processing options are no longer supported in 2.4.0 and newer. We just import/export the processed DMC data. if (!isVersion240OrNewer && parameters.TryGetValue("ReverseBits", out var reverseBitStr)) { sample.ReverseBits = bool.Parse(reverseBitStr); sample.Process(); } break; } case "DPCMMapping": { var pitch = 15; var loop = false; if (parameters.TryGetValue("Pitch", out var pitchStr)) { pitch = int.Parse(pitchStr); } if (parameters.TryGetValue("Loop", out var loopStr)) { loop = bool.Parse(loopStr); } project.MapDPCMSample(Note.FromFriendlyName(parameters["Note"]), project.GetSample(parameters["Sample"]), pitch, loop); break; } case "Instrument": { instrument = project.CreateInstrument(parameters.TryGetValue("Expansion", out _) ? project.ExpansionAudio : ExpansionType.None, parameters["Name"]); if (instrument.ExpansionType == ExpansionType.Fds) { if (parameters.TryGetValue("FdsWavePreset", out var wavPresetStr)) { instrument.FdsWavePreset = (byte)WavePresetType.GetValueForName(wavPresetStr); } if (parameters.TryGetValue("FdsModPreset", out var modPresetStr)) { instrument.FdsWavePreset = (byte)WavePresetType.GetValueForName(modPresetStr); } if (parameters.TryGetValue("FdsMasterVolume", out var masterVolumeStr)) { instrument.FdsMasterVolume = byte.Parse(masterVolumeStr); } if (parameters.TryGetValue("FdsModSpeed", out var fdsModSpeedStr)) { instrument.FdsModSpeed = ushort.Parse(fdsModSpeedStr); } if (parameters.TryGetValue("FdsModDepth", out var fdsModDepthStr)) { instrument.FdsModDepth = byte.Parse(fdsModDepthStr); } if (parameters.TryGetValue("FdsModDelay", out var fdsModDelayStr)) { instrument.FdsModDelay = byte.Parse(fdsModDelayStr); } } else if (instrument.ExpansionType == ExpansionType.N163) { if (parameters.TryGetValue("N163WavePreset", out var wavPresetStr)) { instrument.N163WavePreset = (byte)WavePresetType.GetValueForName(wavPresetStr); } if (parameters.TryGetValue("N163WaveSize", out var n163WavSizeStr)) { instrument.N163WaveSize = byte.Parse(n163WavSizeStr); } if (parameters.TryGetValue("N163WavePos", out var n163WavPosStr)) { instrument.N163WavePos = byte.Parse(n163WavPosStr); } } else if (instrument.ExpansionType == ExpansionType.Vrc7) { if (parameters.TryGetValue("Vrc7Patch", out var vrc7PatchStr)) { instrument.Vrc7Patch = byte.Parse(vrc7PatchStr); } if (instrument.Vrc7Patch == Vrc7InstrumentPatch.Custom) { for (int i = 0; i < 8; i++) { if (parameters.TryGetValue($"Vrc7Reg{i}", out var regStr)) { instrument.Vrc7PatchRegs[i] = byte.Parse(regStr); } } } } break; } case "Arpeggio": { arpeggio = project.CreateArpeggio(parameters["Name"]); arpeggio.Envelope.Length = int.Parse(parameters["Length"]); if (parameters.TryGetValue("Loop", out var loopStr)) { arpeggio.Envelope.Loop = int.Parse(loopStr); } var values = parameters["Values"].Split(','); for (int j = 0; j < values.Length; j++) { arpeggio.Envelope.Values[j] = sbyte.Parse(values[j]); } break; } case "Envelope": { var env = instrument.Envelopes[EnvelopeType.GetValueForShortName(parameters["Type"])]; if (env != null) { if (env.CanResize) { env.Length = int.Parse(parameters["Length"]); } if (parameters.TryGetValue("Loop", out var loopStr)) { env.Loop = int.Parse(loopStr); } if (parameters.TryGetValue("Release", out var releaseStr)) { env.Release = int.Parse(releaseStr); } if (parameters.TryGetValue("Relative", out var relativeStr)) { env.Relative = bool.Parse(relativeStr); } var values = parameters["Values"].Split(','); for (int j = 0; j < values.Length; j++) { env.Values[j] = sbyte.Parse(values[j]); } } break; } case "Song": { song = project.CreateSong(parameters["Name"]); song.SetLength(int.Parse(parameters["Length"])); song.SetBeatLength(int.Parse(parameters[beatLengthAttributeName])); song.SetLoopPoint(int.Parse(parameters["LoopPoint"])); if (song.UsesFamiTrackerTempo) { song.SetDefaultPatternLength(int.Parse(parameters["PatternLength"])); song.FamitrackerTempo = int.Parse(parameters["FamiTrackerTempo"]); song.FamitrackerSpeed = int.Parse(parameters["FamiTrackerSpeed"]); } else { var noteLength = int.Parse(parameters["NoteLength"]); song.ResizeNotes(noteLength, false); song.SetBeatLength(song.BeatLength * noteLength); song.SetDefaultPatternLength(int.Parse(parameters["PatternLength"]) * noteLength); } break; } case "PatternCustomSettings": { if (project.UsesFamiTrackerTempo) { var beatLength = song.BeatLength; if (parameters.TryGetValue(beatLengthAttributeName, out var beatLengthStr)) { beatLength = int.Parse(beatLengthStr); } song.SetPatternCustomSettings(int.Parse(parameters["Time"]), int.Parse(parameters["Length"]), beatLength); } else { var patternLength = int.Parse(parameters["Length"]); var noteLength = int.Parse(parameters["NoteLength"]); var beatLength = int.Parse(parameters[beatLengthAttributeName]); song.SetPatternCustomSettings(int.Parse(parameters["Time"]), patternLength * noteLength, beatLength * noteLength, noteLength); } break; } case "Channel": { var channelType = ChannelType.GetValueForShortName(parameters["Type"]); channel = song.GetChannelByType(channelType); break; } case "Pattern": { pattern = channel.CreatePattern(parameters["Name"]); break; } case "Note": { var time = int.Parse(parameters["Time"]); var note = pattern.GetOrCreateNoteAt(time); if (parameters.TryGetValue("Value", out var valueStr)) { note.Value = (byte)Note.FromFriendlyName(valueStr); } if (parameters.TryGetValue("Instrument", out var instStr) && channel.SupportsInstrument(project.GetInstrument(instStr))) { note.Instrument = project.GetInstrument(instStr); } if (parameters.TryGetValue("Arpeggio", out var arpStr) && channel.SupportsArpeggios) { note.Arpeggio = project.GetArpeggio(arpStr); } if (parameters.TryGetValue("SlideTarget", out var slideStr) && channel.SupportsSlideNotes) { note.SlideNoteTarget = (byte)Note.FromFriendlyName(slideStr); } if (parameters.TryGetValue("Attack", out var attackStr)) { note.HasAttack = bool.Parse(attackStr); } if (parameters.TryGetValue("Volume", out var volumeStr) && channel.SupportsEffect(Note.EffectVolume)) { note.Volume = byte.Parse(volumeStr); } if (parameters.TryGetValue("VibratoSpeed", out var vibSpeedStr) && channel.SupportsEffect(Note.EffectVibratoSpeed)) { note.VibratoSpeed = byte.Parse(vibSpeedStr); } if (parameters.TryGetValue("VibratoDepth", out var vibDepthStr) && channel.SupportsEffect(Note.EffectVibratoDepth)) { note.VibratoDepth = byte.Parse(vibDepthStr); } if (parameters.TryGetValue("Speed", out var speedStr) && channel.SupportsEffect(Note.EffectSpeed)) { note.Speed = byte.Parse(speedStr); } if (parameters.TryGetValue("FinePitch", out var finePitchStr) && channel.SupportsEffect(Note.EffectFinePitch)) { note.FinePitch = sbyte.Parse(finePitchStr); } if (parameters.TryGetValue("FdsModSpeed", out var modSpeedStr) && channel.SupportsEffect(Note.EffectFdsModSpeed)) { note.FdsModSpeed = ushort.Parse(modSpeedStr); } if (parameters.TryGetValue("FdsModDepth", out var modDepthStr) && channel.SupportsEffect(Note.EffectFdsModDepth)) { note.FdsModDepth = byte.Parse(modDepthStr); } if (parameters.TryGetValue("DutyCycle", out var dutyCycleStr) && channel.SupportsEffect(Note.EffectDutyCycle)) { note.DutyCycle = byte.Parse(dutyCycleStr); } if (parameters.TryGetValue("NoteDelay", out var noteDelayStr) && channel.SupportsEffect(Note.EffectNoteDelay)) { note.NoteDelay = byte.Parse(noteDelayStr); } if (parameters.TryGetValue("CutDelay", out var cutDelayStr) && channel.SupportsEffect(Note.EffectCutDelay)) { note.CutDelay = byte.Parse(cutDelayStr); } break; } case "PatternInstance": { var time = int.Parse(parameters["Time"]); channel.PatternInstances[time] = channel.GetPattern(parameters["Pattern"]); break; } } } project.SortEverything(); return(project); } #if !DEBUG catch (Exception e) { Log.LogMessage(LogSeverity.Error, "Please contact the developer on GitHub!"); Log.LogMessage(LogSeverity.Error, e.Message); Log.LogMessage(LogSeverity.Error, e.StackTrace); return(null); } #endif }