private void FinishApply(Action callback) { song.DeleteNotesPastMaxInstanceLength(); song.InvalidateCumulativePatternCache(); song.Project.ValidateIntegrity(); callback(); }
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.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; 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, 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); }
public void Apply(bool custom = false) { 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); } } } } 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) { var convertTempo = false; if (noteLength != originalNoteLength) { convertTempo = ShowConvertTempoDialog(); } song.ChangeFamiStudioTempoGroove(groove, convertTempo); song.SetBeatLength(beatLength * song.NoteLength); song.SetDefaultPatternLength(patternLength * song.NoteLength); song.SetGroovePaddingMode(groovePadMode); } 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); } } if (patternsToResize.Count > 0) { if (ShowConvertTempoDialog()) { 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); } } } } song.DeleteNotesPastMaxInstanceLength(); song.InvalidateCumulativePatternCache(); song.Project.ValidateIntegrity(); }
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); }