Exemplo n.º 1
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
        }
Exemplo n.º 2
0
        public Project Load(string filename, int songIndex, int duration, int patternLength, int startFrame, bool removeIntroSilence, bool reverseDpcm, bool preserveDpcmPad)
        {
            nsf = NsfOpen(filename);

            if (nsf == IntPtr.Zero)
            {
                Log.LogMessage(LogSeverity.Error, "Error opening NSF. File may be corrupted or may be a NSF2 using advanced features such as IRQ which are not supported at the moment.");
                return(null);
            }

            var trackCount = NsfGetTrackCount(nsf);

            if (songIndex < 0 || songIndex > trackCount)
            {
                return(null);
            }

            preserveDpcmPadding = preserveDpcmPad;

            var palSource = (NsfIsPal(nsf) & 1) == 1;
            var numFrames = duration * (palSource ? 50 : 60);

            project = new Project();

            project.Name      = Marshal.PtrToStringAnsi(NsfGetTitle(nsf));
            project.Author    = Marshal.PtrToStringAnsi(NsfGetArtist(nsf));
            project.Copyright = Marshal.PtrToStringAnsi(NsfGetCopyright(nsf));
            project.PalMode   = palSource;

            // Our expansion mask is the same as NSF.
            var expansionMask = NsfGetExpansion(nsf);

            // The 2 upper bits of the mask need to be zero, we dont support these.
            if (expansionMask != (expansionMask & ExpansionType.AllMask))
            {
                Log.LogMessage(LogSeverity.Error, "NSF uses unknown or unsupported expansion chips, aborting.");
                NsfClose(nsf);
                return(null);
            }

            var numN163Channels = (expansionMask & ExpansionType.N163Mask) != 0 ? GetNumNamcoChannels(filename, songIndex, numFrames) : 1;

            project.SetExpansionAudioMask(expansionMask, numN163Channels);

            var songName = Marshal.PtrToStringAnsi(NsfGetTrackName(nsf, songIndex));

            song          = project.CreateSong(string.IsNullOrEmpty(songName) ? $"Song {songIndex + 1}" : songName);
            channelStates = new ChannelState[song.Channels.Length];

            NsfSetTrack(nsf, songIndex);

            song.ChangeFamiStudioTempoGroove(new[] { 1 }, false);
            song.SetDefaultPatternLength(patternLength);

            for (int i = 0; i < song.Channels.Length; i++)
            {
                channelStates[i] = new ChannelState();
            }

            var foundFirstNote = !removeIntroSilence;

            var p = 0;
            var n = 0;
            var f = startFrame;

            for (int i = 0; i < numFrames; i++)
            {
                p = f / song.PatternLength;
                n = f % song.PatternLength;

                if (p >= Song.MaxLength - 1)
                {
                    break;
                }

                var playCalled     = 0;
                var waitFrameCount = 0;
                do
                {
                    playCalled = NsfRunFrame(nsf);

                    if (++waitFrameCount == 1000)
                    {
                        Log.LogMessage(LogSeverity.Error, "NSF did not call PLAY after 1000 frames, aborting.");
                        NsfClose(nsf);
                        return(null);
                    }
                }while (playCalled == 0);

                for (int c = 0; c < song.Channels.Length; c++)
                {
                    foundFirstNote |= UpdateChannel(p, n, song.Channels[c], channelStates[c]);
                }

                if (foundFirstNote)
                {
                    f++;
                }
                else
                {
                    // Reset everything until we find our first note.
                    project.DeleteAllInstruments();
                    project.DeleteAllSamples();
                    for (int c = 0; c < song.Channels.Length; c++)
                    {
                        channelStates[c] = new ChannelState();
                    }
                }
            }

            song.SetLength(p + 1);

            NsfClose(nsf);

            var factors = Utils.GetFactors(song.PatternLength, FamiStudioTempoUtils.MaxNoteLength);

            if (factors.Length > 0)
            {
                var noteLen = factors[0];

                // Look for a factor that generates a note length < 10 and gives a pattern length that is a multiple of 16.
                foreach (var factor in factors)
                {
                    if (factor <= 10)
                    {
                        noteLen = factor;
                        if (((song.PatternLength / noteLen) % 16) == 0)
                        {
                            break;
                        }
                    }
                }

                song.ChangeFamiStudioTempoGroove(new[] { noteLen }, false);
            }
            else
            {
                song.ChangeFamiStudioTempoGroove(new[] { 1 }, false);
            }

            song.SetSensibleBeatLength();
            song.ConvertToCompoundNotes();
            song.DeleteEmptyPatterns();
            song.UpdatePatternStartNotes();
            song.InvalidateCumulativePatternCache();
            project.DeleteUnusedInstruments();

            foreach (var sample in project.Samples)
            {
                sample.ReverseBits = reverseDpcm;
            }

            return(project);
        }