private static int LoadAndMergePatternList(ProjectLoadBuffer serializer, Song song) { // Remap whatever original song we had to the current one. int songId = -1; serializer.Serialize(ref songId); serializer.RemapId(songId, song.Id); int numPatterns = 0; serializer.Serialize(ref numPatterns); var patternIdNameMap = new List <Tuple <int, int, string> >(); for (int i = 0; i < numPatterns; i++) { var patId = 0; var patChannel = 0; var patName = ""; serializer.Serialize(ref patId); serializer.Serialize(ref patChannel); serializer.Serialize(ref patName); patternIdNameMap.Add(new Tuple <int, int, string>(patId, patChannel, patName)); } var dummyPattern = new Pattern(); // Match patterns by name, create missing ones and remap IDs. for (int i = 0; i < numPatterns; i++) { var patId = patternIdNameMap[i].Item1; var patChannel = patternIdNameMap[i].Item2; var patName = patternIdNameMap[i].Item3; if (serializer.Project.IsChannelActive(patChannel)) { var existingPattern = song.GetChannelByType(patChannel).GetPattern(patName); if (existingPattern != null) { serializer.RemapId(patId, existingPattern.Id); dummyPattern.SerializeState(serializer); // Skip } else { var pattern = song.GetChannelByType(patChannel).CreatePattern(patName); serializer.RemapId(patId, pattern.Id); pattern.SerializeState(serializer); } } else { serializer.RemapId(patId, -1); dummyPattern.SerializeState(serializer); // Skip } } return(numPatterns); }
public void Advance(Song song, int patternIdx, int noteIdx) { var channel = song.GetChannelByType(channelType); var pattern = channel.PatternInstances[patternIdx]; if (pattern == null) { return; } var newNote = pattern.Notes[noteIdx]; if (newNote.IsValid) { slideStep = 0; if (newNote.IsSlideNote) { var noteTable = NesApu.GetNoteTableForChannelType(channel.Type, false); if (channel.ComputeSlideNoteParams(patternIdx, noteIdx, noteTable, out slidePitch, out slideStep, out _)) { newNote.Value = (byte)newNote.SlideNoteTarget; } } PlayNote(newNote); } else if (newNote.HasVolume) { note.Volume = newNote.Volume; } }
public void ProcessEffects(Song song, int patternIdx, int noteIdx, ref int speed, bool allowJump = true) { var pattern = song.GetChannelByType(channelType).PatternInstances[patternIdx]; if (pattern == null) { return; } if (pattern.Notes.TryGetValue(noteIdx, out var tmpNote)) { if (tmpNote.HasSpeed) { speed = tmpNote.Speed; } if (tmpNote.HasVibrato) { if (tmpNote.VibratoDepth != 0 && tmpNote.VibratoDepth != 0) { envelopes[Envelope.Pitch] = Envelope.CreateVibratoEnvelope(tmpNote.VibratoSpeed, tmpNote.VibratoDepth); envelopeIdx[Envelope.Pitch] = 0; envelopeValues[Envelope.Pitch] = 0; pitchEnvelopeOverride = true; } else { envelopes[Envelope.Pitch] = null; pitchEnvelopeOverride = false; } } } }
public void Validate(Song song, Dictionary <int, object> idMap) { Debug.Assert(this == song.GetChannelByType(type)); Debug.Assert(this.song == song); foreach (var inst in patternInstances) { Debug.Assert(inst == null || patterns.Contains(inst)); } foreach (var pat in patterns) { pat.Validate(this, idMap); } }
public void Advance(Song song, int patternIdx, int noteIdx, ref int famitrackerSpeed) { // When advancing row, if there was a delayed note, play it immediately. That's how FamiTracker does it. if (delayedNote != null) { PlayNote(delayedNote, delayedNoteSlidePitch, delayedNoteSlideStep); delayedNote = null; delayedNoteCounter = 0; } var channel = song.GetChannelByType(channelType); var pattern = channel.PatternInstances[patternIdx]; if (pattern == null) { return; } if (pattern.Notes.TryGetValue(noteIdx, out var newNote)) { // We dont delay speed effects. This is not what FamiTracker does, but I dont care. // There is a special place in hell for people who delay speed effect. if (newNote.HasSpeed) { famitrackerSpeed = newNote.Speed; } // Slide params needs to be computed right away since we wont have access to the play position/channel later. int noteSlidePitch = 0; int noteSlideStep = 0; if (newNote.IsValid && newNote.IsSlideNote) { channel.ComputeSlideNoteParams(newNote, patternIdx, noteIdx, famitrackerSpeed, noteTable, palPlayback, true, out noteSlidePitch, out noteSlideStep, out _); } // Store note for later if delayed. if (newNote.HasNoteDelay) { delayedNote = newNote; delayedNoteCounter = newNote.NoteDelay + 1; delayedNoteSlidePitch = noteSlidePitch; delayedNoteSlideStep = noteSlideStep; return; } PlayNote(newNote, noteSlidePitch, noteSlideStep); } }
public void Advance(Song song, NoteLocation location, ref int famitrackerSpeed) { // When advancing row, if there was a delayed note, play it immediately. That's how FamiTracker does it. if (delayedNote != null) { PlayNote(delayedNote, delayedNoteSlidePitch, delayedNoteSlideStep); delayedNote = null; delayedNoteCounter = 0; } var channel = song.GetChannelByType(channelType); var pattern = channel.PatternInstances[location.PatternIndex]; if (pattern == null) { return; } pattern.Notes.TryGetValue(location.NoteIndex, out var newNote); var needClone = true; // Generate a release note if the release counter reaches zero. if (releaseCounter > 0 && --releaseCounter == 0 && (newNote == null || !newNote.IsMusicalOrStop)) { newNote = newNote == null ? new Note() : newNote.Clone(); newNote.Value = Note.NoteRelease; needClone = false; } // Generate a stop note if the stop counter reaches zero. if (durationCounter > 0 && --durationCounter == 0 && (newNote == null || !newNote.IsMusicalOrStop)) { newNote = newNote == null ? new Note() : newNote.Clone(); newNote.Value = Note.NoteStop; needClone = false; } if (newNote != null) { // We dont delay speed effects. This is not what FamiTracker does, but I dont care. // There is a special place in hell for people who delay speed effect. if (newNote.HasSpeed) { famitrackerSpeed = newNote.Speed; } // Slide params needs to be computed right away since we wont have access to the play position/channel later. int noteSlidePitch = 0; int noteSlideStep = 0; if (newNote.IsMusical) { if (newNote.IsSlideNote) { channel.ComputeSlideNoteParams(newNote, location, famitrackerSpeed, noteTable, palPlayback, true, out noteSlidePitch, out noteSlideStep, out _); } if (newNote.HasRelease) { releaseCounter = newNote.Release; } durationCounter = newNote.Duration; } // Store note for later if delayed. if (newNote.HasNoteDelay) { delayedNote = newNote; delayedNoteCounter = newNote.NoteDelay + 1; delayedNoteSlidePitch = noteSlidePitch; delayedNoteSlideStep = noteSlideStep; return; } PlayNote(newNote, noteSlidePitch, noteSlideStep, needClone); } }
public void Advance(Song song, int patternIdx, int noteIdx, int famitrackerSpeed, int famitrackerBaseTempo) { var channel = song.GetChannelByType(channelType); var pattern = channel.PatternInstances[patternIdx]; if (pattern == null) { return; } if (pattern.Notes.TryGetValue(noteIdx, out var newNote)) { newNote = newNote.Clone(); if (newNote.IsValid) { if (!newNote.IsRelease) { slideStep = 0; } if (newNote.IsSlideNote) { if (channel.ComputeSlideNoteParams(newNote, patternIdx, noteIdx, famitrackerSpeed, famitrackerBaseTempo, noteTable, out slidePitch, out slideStep, out _)) { newNote.Value = (byte)newNote.SlideNoteTarget; } } if (!newNote.HasVolume && note.HasVolume) { newNote.Volume = note.Volume; } if (!newNote.HasFinePitch && note.HasFinePitch) { newNote.FinePitch = note.FinePitch; } if (!newNote.HasFdsModDepth && note.HasFdsModDepth) { newNote.FdsModDepth = note.FdsModDepth; } if (!newNote.HasFdsModSpeed && note.HasFdsModSpeed) { newNote.FdsModSpeed = note.FdsModSpeed; } if (newNote.Instrument == null && note.Instrument != null) { newNote.Instrument = note.Instrument; } PlayNote(newNote); } else { if (newNote.HasVolume) { note.Volume = newNote.Volume; } if (newNote.HasFinePitch) { note.FinePitch = newNote.FinePitch; } if (newNote.HasFdsModDepth) { note.FdsModDepth = newNote.FdsModDepth; } if (newNote.HasFdsModSpeed) { note.FdsModSpeed = newNote.FdsModSpeed; } if (newNote.Instrument != null) { note.Instrument = newNote.Instrument; } } } }