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); }
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); }
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); }