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));
                        }
                    }
                }
            }
        }
Beispiel #2
0
        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);
            }
        }
Beispiel #3
0
        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
        }
Beispiel #4
0
        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
        }