コード例 #1
0
        private void TruncateLongPatterns(Song song)
        {
            if (song.PatternLength > 256)
            {
                song.SetDefaultPatternLength(256);
            }

            // FamiTracker can only shorten patterns using skips.
            // We allow patterns to be longer than the default, so we will truncate those.
            for (int i = 0; i < song.Length; i++)
            {
                if (song.GetPatternLength(i) > song.PatternLength)
                {
                    song.ClearPatternCustomSettings(i);
                }
            }

            song.DeleteNotesPastMaxInstanceLength();
        }
コード例 #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 == null)
            {
                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;

            switch (NsfGetExpansion(nsf))
            {
            case EXTSOUND_VRC6: project.SetExpansionAudio(ExpansionType.Vrc6); break;

            case EXTSOUND_VRC7: project.SetExpansionAudio(ExpansionType.Vrc7); break;

            case EXTSOUND_FDS:  project.SetExpansionAudio(ExpansionType.Fds);  break;

            case EXTSOUND_MMC5: project.SetExpansionAudio(ExpansionType.Mmc5); break;

            case EXTSOUND_N163: project.SetExpansionAudio(ExpansionType.N163, GetNumNamcoChannels(filename, songIndex, numFrames)); break;

            case EXTSOUND_S5B:  project.SetExpansionAudio(ExpansionType.S5B);  break;

            case 0: break;

            default:
                NsfClose(nsf);     // Unsupported expansion combination.
                return(null);
            }

            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.ResizeNotes(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;
                do
                {
                    playCalled = NsfRunFrame(nsf);
                }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.DeleteAllInstrument();
                    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, Song.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.ResizeNotes(noteLen, false);
            }
            else
            {
                song.ResizeNotes(1, false);
            }

            song.SetSensibleBeatLength();
            song.DeleteEmptyPatterns();
            song.UpdatePatternStartNotes();
            project.DeleteUnusedInstruments();
            project.UpdateAllLastValidNotesAndVolume();

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

            return(project);
        }
コード例 #3
0
        public void ApplyAsync(bool custom, Action callback)
        {
            if (song.UsesFamiTrackerTempo)
            {
                if (patternIdx == -1)
                {
                    if (famitrackerTempoPropIdx >= 0)
                    {
                        song.FamitrackerTempo = props.GetPropertyValue <int>(famitrackerTempoPropIdx);
                        song.FamitrackerSpeed = props.GetPropertyValue <int>(famitrackerSpeedPropIdx);
                    }

                    song.SetBeatLength(props.GetPropertyValue <int>(notesPerBeatPropIdx));
                    song.SetDefaultPatternLength(props.GetPropertyValue <int>(notesPerPatternPropIdx));
                }
                else
                {
                    for (int i = minPatternIdx; i <= maxPatternIdx; i++)
                    {
                        var beatLength    = props.GetPropertyValue <int>(notesPerBeatPropIdx);
                        var patternLength = props.GetPropertyValue <int>(notesPerPatternPropIdx);

                        if (custom)
                        {
                            song.SetPatternCustomSettings(i, patternLength, beatLength);
                        }
                        else
                        {
                            song.ClearPatternCustomSettings(i);
                        }
                    }
                }

                FinishApply(callback);
            }
            else
            {
                var tempoIndex = Array.IndexOf(tempoStrings, props.GetPropertyValue <string>(famistudioBpmPropIdx));
                var tempoInfo  = tempoList[tempoIndex];

                var beatLength    = props.GetPropertyValue <int>(notesPerBeatPropIdx);
                var patternLength = props.GetPropertyValue <int>(notesPerPatternPropIdx);
                var noteLength    = Utils.Min(tempoInfo.groove);

                var grooveIndex   = Array.IndexOf(grooveStrings, props.GetPropertyValue <string>(groovePropIdx));
                var groovePadMode = GroovePaddingType.GetValueForName(props.GetPropertyValue <string>(groovePadPropIdx));
                var grooveList    = FamiStudioTempoUtils.GetAvailableGrooves(tempoInfo.groove);
                var groove        = grooveList[grooveIndex];

                props.UpdateIntegerRange(notesPerPatternPropIdx, 1, Pattern.MaxLength / noteLength);
                props.SetLabelText(framesPerNotePropIdx, noteLength.ToString());

                if (patternIdx == -1)
                {
                    ShowConvertTempoDialogAsync(noteLength != originalNoteLength, (c) =>
                    {
                        song.ChangeFamiStudioTempoGroove(groove, c);
                        song.SetBeatLength(beatLength * song.NoteLength);
                        song.SetDefaultPatternLength(patternLength * song.NoteLength);
                        song.SetGroovePaddingMode(groovePadMode);

                        FinishApply(callback);
                    });
                }
                else
                {
                    var actualNoteLength    = song.NoteLength;
                    var actualPatternLength = song.PatternLength;
                    var actualBeatLength    = song.BeatLength;

                    if (custom)
                    {
                        actualNoteLength    = noteLength;
                        actualBeatLength    = beatLength * noteLength;
                        actualPatternLength = patternLength * noteLength;
                    }

                    var patternsToResize = new List <int>();
                    for (int i = minPatternIdx; i <= maxPatternIdx; i++)
                    {
                        if (actualNoteLength != song.GetPatternNoteLength(patternIdx))
                        {
                            patternsToResize.Add(i);
                        }
                    }

                    ShowConvertTempoDialogAsync(patternsToResize.Count > 0, (c) =>
                    {
                        if (c)
                        {
                            foreach (var p in patternsToResize)
                            {
                                song.ResizePatternNotes(p, actualNoteLength);
                            }
                        }

                        for (int i = minPatternIdx; i <= maxPatternIdx; i++)
                        {
                            if (custom)
                            {
                                song.SetPatternCustomSettings(i, actualPatternLength, actualBeatLength, groove, groovePadMode);
                            }
                            else
                            {
                                song.ClearPatternCustomSettings(i);
                            }
                        }

                        FinishApply(callback);
                    });
                }
            }
        }
コード例 #4
0
ファイル: NsfFile.cs プロジェクト: zarp-dev/FamiStudio
        public Project Load(string filename, int songIndex, int duration, int patternLength, int startFrame, bool removeIntroSilence)
        {
            nsf = NsfOpen(filename);

            if (nsf == null)
            {
                return(null);
            }

            var numFrames = duration * (NsfIsPal(nsf) != 0 ? 50 : 60);

            project = new Project();

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

            switch (NsfGetExpansion(nsf))
            {
            case EXTSOUND_VRC6: project.SetExpansionAudio(Project.ExpansionVrc6); break;

            case EXTSOUND_VRC7: project.SetExpansionAudio(Project.ExpansionVrc7); break;

            case EXTSOUND_FDS:  project.SetExpansionAudio(Project.ExpansionFds);  break;

            case EXTSOUND_MMC5: project.SetExpansionAudio(Project.ExpansionMmc5); break;

            case EXTSOUND_N163: project.SetExpansionAudio(Project.ExpansionN163, GetNumNamcoChannels(filename, songIndex, numFrames)); break;

            case EXTSOUND_S5B:  project.SetExpansionAudio(Project.ExpansionS5B);  break;

            case 0: break;

            default:
                NsfClose(nsf);     // Unsupported expansion combination.
                return(null);
            }

            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.ResizeNotes(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;
                do
                {
                    playCalled = NsfRunFrame(nsf);
                }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.DeleteAllInstrument();
                    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, Song.MaxNoteLength);

            if (factors.Length > 0 && factors[0] <= Song.MaxNoteLength)
            {
                song.ResizeNotes(factors[0], false);
            }
            else
            {
                song.ResizeNotes(1, false);
            }

            song.SetSensibleBarLength();
            song.DeleteEmptyPatterns();
            song.UpdatePatternStartNotes();
            project.DeleteUnusedInstruments();
            project.UpdateAllLastValidNotesAndVolume();

            return(project);
        }
コード例 #5
0
        public Project Load(string filename)
        {
            var envelopes   = new Dictionary <int, Envelope> [Project.ExpansionCount, Envelope.Count];
            var instruments = new Dictionary <int, Instrument>();
            var dpcms       = new Dictionary <int, DPCMSample>();
            var columns     = new int[5] {
                1, 1, 1, 1, 1
            };

            for (int i = 0; i < envelopes.GetLength(0); i++)
            {
                for (int j = 0; j < envelopes.GetLength(1); j++)
                {
                    envelopes[i, j] = new Dictionary <int, Envelope>();
                }
            }

            project           = new Project();
            project.TempoMode = Project.TempoFamiTracker;

            DPCMSample currentDpcm  = null;
            int        dpcmWriteIdx = 0;
            Song       song         = null;
            string     patternName  = "";

            var lines = File.ReadAllLines(filename);

            for (int i = 0; i < lines.Length; i++)
            {
                var line = lines[i].Trim();

                if (line.StartsWith("TITLE"))
                {
                    project.Name = line.Substring(5).Trim(' ', '"');
                }
                else if (line.StartsWith("AUTHOR"))
                {
                    project.Author = line.Substring(6).Trim(' ', '"');
                }
                else if (line.StartsWith("COPYRIGHT"))
                {
                    project.Copyright = line.Substring(9).Trim(' ', '"');
                }
                else if (line.StartsWith("EXPANSION"))
                {
                    var exp          = int.Parse(line.Substring(9));
                    var convertedExp = ConvertExpansionAudio(exp);

                    if (convertedExp < 0)
                    {
                        return(null);
                    }

                    project.SetExpansionAudio(convertedExp);
                }
                else if (line.StartsWith("N163CHANNELS"))
                {
                    var numExpChannels = int.Parse(line.Substring(12).Trim(' ', '"'));
                    project.SetExpansionAudio(Project.ExpansionN163, numExpChannels);
                }
                else if (line.StartsWith("MACRO"))
                {
                    var expansion = line.StartsWith("MACROVRC6") ? Project.ExpansionVrc6  :
                                    line.StartsWith("MACRON163") ? Project.ExpansionN163 : Project.ExpansionNone;

                    var halves = line.Substring(line.IndexOf(' ')).Split(':');
                    var param  = halves[0].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    var curve  = halves[1].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                    var type = int.Parse(param[0]);
                    var idx  = int.Parse(param[1]);
                    var loop = int.Parse(param[2]);
                    var rel  = int.Parse(param[3]);

                    var famistudioType = EnvelopeTypeLookup[type];

                    if (famistudioType < Envelope.Count)
                    {
                        var env = new Envelope(famistudioType);
                        env.Length = curve.Length;

                        // FamiTracker allows envelope with release with no loop. We dont allow that.
                        if (env.CanRelease && loop == -1 && rel != -1)
                        {
                            loop = rel;
                        }

                        env.Loop     = loop;
                        env.Release  = env.CanRelease && rel != -1 ? rel + 1 : -1;
                        env.Relative = famistudioType == Envelope.Pitch;

                        for (int j = 0; j < curve.Length; j++)
                        {
                            env.Values[j] = sbyte.Parse(curve[j]);
                        }

                        envelopes[expansion, famistudioType][idx] = env;
                    }
                }
                else if (line.StartsWith("DPCMDEF"))
                {
                    var param = SplitStringKeepQuotes(line.Substring(7));
                    currentDpcm = CreateUniquelyNamedSample(param[2], new byte[int.Parse(param[1])]);
                    dpcms[int.Parse(param[0])] = currentDpcm;
                    dpcmWriteIdx = 0;
                }
                else if (line.StartsWith("DPCM"))
                {
                    if (currentDpcm != null) // Can happen if more than 16KB of samples
                    {
                        var param = line.Substring(6).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                        foreach (var s in param)
                        {
                            currentDpcm.Data[dpcmWriteIdx++] = Convert.ToByte(s, 16);
                        }
                    }
                }
                else if (line.StartsWith("KEYDPCM"))
                {
                    var param = line.Substring(7).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    if (param[0] == "0")
                    {
                        int octave   = int.Parse(param[1]);
                        int semitone = int.Parse(param[2]);
                        int note     = octave * 12 + semitone + 1;

                        if (project.NoteSupportsDPCM(note))
                        {
                            int dpcm  = int.Parse(param[3]);
                            int pitch = int.Parse(param[4]);
                            int loop  = int.Parse(param[5]);

                            project.MapDPCMSample(note, dpcms[dpcm], pitch, loop != 0);
                        }
                    }
                }
                else if (line.StartsWith("INST2A03") || line.StartsWith("INSTVRC6") || line.StartsWith("INSTN163"))
                {
                    var expansion = line.StartsWith("INSTVRC6") ? Project.ExpansionVrc6  :
                                    line.StartsWith("INSTN163") ? Project.ExpansionN163 : Project.ExpansionNone;

                    var param = SplitStringKeepQuotes(line.Substring(line.IndexOf(' ')));

                    int idx = int.Parse(param[0]);
                    int vol = int.Parse(param[1]);
                    int arp = int.Parse(param[2]);
                    int pit = int.Parse(param[3]);
                    int dut = int.Parse(param[5]);

                    var instrument = CreateUniquelyNamedInstrument(expansion, param[param.Length - 1]);

                    if (expansion == Project.ExpansionN163)
                    {
                        instrument.N163WavePreset = Envelope.WavePresetCustom;
                        instrument.N163WaveSize   = byte.Parse(param[6]);
                        instrument.N163WavePos    = byte.Parse(param[7]);
                    }

                    if (vol >= 0 && instrument.IsEnvelopeActive(Envelope.Volume))
                    {
                        instrument.Envelopes[Envelope.Volume] = envelopes[expansion, Envelope.Volume][vol].ShallowClone();
                    }
                    if (arp >= 0 && instrument.IsEnvelopeActive(Envelope.Arpeggio))
                    {
                        instrument.Envelopes[Envelope.Arpeggio] = envelopes[expansion, Envelope.Arpeggio][arp].ShallowClone();
                    }
                    if (pit >= 0 && instrument.IsEnvelopeActive(Envelope.Pitch))
                    {
                        instrument.Envelopes[Envelope.Pitch] = envelopes[expansion, Envelope.Pitch][pit].ShallowClone();
                    }
                    if (dut >= 0 && instrument.IsEnvelopeActive(Envelope.DutyCycle))
                    {
                        instrument.Envelopes[Envelope.DutyCycle] = envelopes[expansion, Envelope.DutyCycle][dut].ShallowClone();
                    }

                    instruments[idx] = instrument;
                }
                else if (line.StartsWith("INSTVRC7"))
                {
                    var param = SplitStringKeepQuotes(line.Substring(line.IndexOf(' ')));

                    int idx        = int.Parse(param[0]);
                    var instrument = CreateUniquelyNamedInstrument(Project.ExpansionVrc7, param[param.Length - 1]);

                    instrument.Vrc7Patch = byte.Parse(param[1]);
                    if (instrument.Vrc7Patch == 0)
                    {
                        for (int j = 0; j < 8; j++)
                        {
                            instrument.Vrc7PatchRegs[j] = Convert.ToByte(param[2 + j], 16);
                        }
                    }

                    instruments[idx] = instrument;
                }
                else if (line.StartsWith("INSTFDS"))
                {
                    var param = SplitStringKeepQuotes(line.Substring(line.IndexOf(' ')));

                    int idx       = int.Parse(param[0]);
                    int modEnable = int.Parse(param[1]);
                    int modSpeed  = int.Parse(param[2]);
                    int modDepth  = int.Parse(param[3]);
                    int modDelay  = int.Parse(param[4]);

                    if (modEnable == 0)
                    {
                        modSpeed = 0;
                        modDepth = 0;
                        modDelay = 0;
                    }

                    instruments[idx]               = CreateUniquelyNamedInstrument(Project.ExpansionFds, param[5]);
                    instruments[idx].FdsModSpeed   = (ushort)modSpeed;
                    instruments[idx].FdsModDepth   = (byte)modDepth;
                    instruments[idx].FdsModDelay   = (byte)modDelay;
                    instruments[idx].FdsWavePreset = Envelope.WavePresetCustom;
                    instruments[idx].FdsModPreset  = Envelope.WavePresetCustom;
                }
                else if (line.StartsWith("FDSMACRO"))
                {
                    var halves = line.Substring(line.IndexOf(' ')).Split(':');
                    var param  = halves[0].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    var curve  = halves[1].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                    var inst = int.Parse(param[0]);
                    var type = int.Parse(param[1]);
                    var loop = int.Parse(param[2]);
                    var rel  = int.Parse(param[3]);

                    var famistudioType = EnvelopeTypeLookup[type];

                    var env = instruments[inst].Envelopes[famistudioType];

                    env.Length = curve.Length;

                    // FamiTracker allows envelope with release with no loop. We dont allow that.
                    if (env.CanRelease && loop == -1 && rel != -1)
                    {
                        loop = rel;
                    }

                    env.Loop     = loop;
                    env.Release  = env.CanRelease && rel != -1 ? rel + 1 : -1;
                    env.Relative = famistudioType == Envelope.Pitch;

                    for (int j = 0; j < curve.Length; j++)
                    {
                        env.Values[j] = sbyte.Parse(curve[j]);
                    }
                }
                else if (line.StartsWith("FDSMOD") || line.StartsWith("FDSWAVE"))
                {
                    var mod    = line.StartsWith("FDSMOD");
                    var halves = line.Substring(line.IndexOf(' ')).Split(':');
                    var param  = halves[0].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    var curve  = halves[1].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                    var inst = int.Parse(param[0]);
                    var env  = instruments[inst].Envelopes[mod ? Envelope.FdsModulation : Envelope.FdsWaveform];
                    for (int j = 0; j < curve.Length; j++)
                    {
                        env.Values[j] = sbyte.Parse(curve[j]);
                    }
                    if (mod)
                    {
                        env.ConvertFdsModulationToAbsolute();
                    }
                }
                else if (line.StartsWith("N163WAVE"))
                {
                    var param = line.Substring(8).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                    var instIdx = int.Parse(param[0]);
                    var waveIdx = int.Parse(param[1]);

                    // TODO: We could create different instruments for each wave.
                    if (waveIdx == 0)
                    {
                        var env = instruments[instIdx].Envelopes[Envelope.N163Waveform];

                        for (int j = 3; j < param.Length; j++)
                        {
                            env.Values[j - 3] = sbyte.Parse(param[j]);
                        }
                    }
                }
                else if (line.StartsWith("TRACK"))
                {
                    var param = SplitStringKeepQuotes(line.Substring(5));

                    song = project.CreateSong(param[3]);
                    song.SetLength(0);
                    song.SetDefaultPatternLength(int.Parse(param[0]));
                    song.FamitrackerSpeed = int.Parse(param[1]);
                    song.FamitrackerTempo = int.Parse(param[2]);
                    columns = new int[song.Channels.Length];
                }
                else if (line.StartsWith("COLUMNS"))
                {
                    var param = line.Substring(7).Split(new[] { ' ', ':' }, StringSplitOptions.RemoveEmptyEntries);
                    for (int j = 0; j < song.Channels.Length; j++)
                    {
                        columns[j] = int.Parse(param[j]);
                    }
                }
                else if (line.StartsWith("ORDER"))
                {
                    var orderIdx = Convert.ToInt32(line.Substring(6, 2), 16);
                    var values   = line.Substring(5).Split(':')[1].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    var order    = new int[song.Channels.Length];
                    for (int j = 0; j < song.Channels.Length; j++)
                    {
                        int patternIdx = Convert.ToInt32(values[j], 16);
                        var name       = values[j];
                        var pattern    = song.Channels[j].GetPattern(name);

                        if (pattern == null)
                        {
                            pattern = song.Channels[j].CreatePattern(name);
                        }

                        song.Channels[j].PatternInstances[orderIdx] = pattern;
                    }

                    song.SetLength(song.Length + 1);
                }
                else if (line.StartsWith("PATTERN"))
                {
                    patternName = line.Substring(8);
                }
                else if (line.StartsWith("ROW"))
                {
                    var channels = line.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
                    var n        = Convert.ToInt32(channels[0].Substring(4, 2), 16);

                    for (int j = 1; j <= song.Channels.Length; j++)
                    {
                        var noteData = channels[j].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                        var pattern  = song.Channels[j - 1].GetPattern(patternName);

                        if (pattern == null)
                        {
                            continue;
                        }

                        //var fxData = new RowFxData[song.PatternLength];
                        if (!patternFxData.ContainsKey(pattern))
                        {
                            patternFxData[pattern] = new RowFxData[song.PatternLength, 4];
                        }

                        // Note
                        if (noteData[0] == "---")
                        {
                            pattern.GetOrCreateNoteAt(n).Value = Note.NoteStop;
                        }
                        else if (noteData[0] == "===")
                        {
                            pattern.GetOrCreateNoteAt(n).Value = Note.NoteRelease;
                        }
                        else if (noteData[0] != "...")
                        {
                            int famitoneNote;

                            if (j == 4)
                            {
                                famitoneNote = (Convert.ToInt32(noteData[0].Substring(0, 1), 16) + 31) + 1;
                            }
                            else
                            {
                                int semitone = TextToNoteLookup[noteData[0].Substring(0, 2)];
                                int octave   = noteData[0][2] - '0';
                                famitoneNote = octave * 12 + semitone + 1;
                            }

                            if (famitoneNote >= Note.MusicalNoteMin && famitoneNote <= Note.MusicalNoteMax)
                            {
                                var note = pattern.GetOrCreateNoteAt(n);
                                note.Value      = (byte)famitoneNote;
                                note.Instrument = j == 5 ? null : instruments[Convert.ToInt32(noteData[1], 16)];
                            }
                            else
                            {
                                // Note outside of range.
                            }
                        }

                        // Volume
                        if (noteData[2] != ".")
                        {
                            pattern.GetOrCreateNoteAt(n).Volume = Convert.ToByte(noteData[2], 16);
                        }

                        // Read FX.
                        for (int k = 0; k < columns[j - 1]; k++)
                        {
                            var fxStr = noteData[3 + k];

                            if (fxStr == "...")
                            {
                                continue;
                            }

                            var fx = new RowFxData();

                            if (project.ExpansionAudio == Project.ExpansionFds && FdsTextToEffectLookup.TryGetValue(fxStr[0], out var fdsFx))
                            {
                                fx.fx = (byte)fdsFx;
                            }
                            else
                            {
                                fx.fx = TextToEffectLookup[fxStr[0]];
                            }

                            fx.param = Convert.ToByte(fxStr.Substring(1), 16);
                            patternFxData[pattern][n, k] = fx;

                            ApplySimpleEffects(fx, pattern, n, patternLengths);
                        }
                    }
                }
            }

            FinishImport();

            return(project);
        }
コード例 #6
0
ファイル: NsfFile.cs プロジェクト: MassVOiD/FamiStudio
        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);
        }