public void Serialize(ref Song song) { int songId = song == null ? -1 : song.Id; Serialize(ref songId); }
public static Project Load(string filename) { var project = new Project(); var envelopes = new Dictionary <int, Envelope>[Envelope.Max] { new Dictionary <int, Envelope>(), new Dictionary <int, Envelope>(), new Dictionary <int, Envelope>() }; var duties = new Dictionary <int, int>(); var instruments = new Dictionary <int, Instrument>(); var dpcms = new Dictionary <int, DPCMSample>(); var columns = new int[5] { 1, 1, 1, 1, 1 }; var noteLookup = new Dictionary <string, int> { ["A-"] = 9, ["A#"] = 10, ["B-"] = 11, ["C-"] = 0, ["C#"] = 1, ["D-"] = 2, ["D#"] = 3, ["E-"] = 4, ["F-"] = 5, ["F#"] = 6, ["G-"] = 7, ["G#"] = 8 }; 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("MACRO")) { var halves = line.Substring(5).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]); if (type < 3) { var env = new Envelope(); env.Length = curve.Length; env.Loop = loop; env.Release = type == Envelope.Volume ? rel : -1; for (int j = 0; j < curve.Length; j++) { env.Values[j] = sbyte.Parse(curve[j]); } if (type == 2) { env.ConvertToAbsolute(); } envelopes[type][idx] = env; } else if (type == 4) { duties[idx] = int.Parse(curve[0]); } } else if (line.StartsWith("DPCMDEF")) { var param = SplitStringKeepQuotes(line.Substring(7)); var name = param[2]; var j = 2; while (!project.IsDPCMSampleNameUnique(name)) { name = param[2] + "-" + j++; } currentDpcm = project.CreateDPCMSample(name, new byte[int.Parse(param[1])]); dpcms[int.Parse(param[0])] = currentDpcm; dpcmWriteIdx = 0; } else if (line.StartsWith("DPCM")) { 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")) { var param = SplitStringKeepQuotes(line.Substring(8)); 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 name = param[6]; var j = 2; if (!project.IsInstrumentNameUnique(name)) { name = param[6] + "-" + j++; } var instrument = project.CreateInstrument(name); if (vol >= 0) { instrument.Envelopes[0] = envelopes[0][vol].Clone(); } if (arp >= 0) { instrument.Envelopes[1] = envelopes[1][arp].Clone(); } if (pit >= 0) { instrument.Envelopes[2] = envelopes[2][pit].Clone(); } if (dut >= 0) { instrument.DutyCycle = duties[dut]; } instruments[idx] = instrument; } else if (line.StartsWith("TRACK")) { var param = SplitStringKeepQuotes(line.Substring(5)); song = project.CreateSong(param[3]); song.Length = 0; song.PatternLength = int.Parse(param[0]); song.Speed = int.Parse(param[1]); song.Tempo = int.Parse(param[2]); } else if (line.StartsWith("COLUMNS")) { var param = line.Substring(7).Split(new[] { ' ', ':' }, StringSplitOptions.RemoveEmptyEntries); for (int j = 0; j < 5; 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[5]; for (int j = 0; j < 5; 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.Length++; } else if (line.StartsWith("PATTERN")) { patternName = line.Substring(8); } else if (line.StartsWith("ROW")) { var channels = line.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); var rowIdx = Convert.ToInt32(channels[0].Substring(4, 2), 16); for (int j = 1; j <= 5; j++) { var noteData = channels[j].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); var pattern = song.Channels[j - 1].GetPattern(patternName); if (pattern == null) { continue; } // Note if (noteData[0] == "---") { pattern.Notes[rowIdx].Value = Note.NoteStop; } else if (noteData[0] == "===") { pattern.Notes[rowIdx].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 = noteLookup[noteData[0].Substring(0, 2)]; int octave = noteData[0][2] - '0'; famitoneNote = octave * 12 + semitone + 1; } if (famitoneNote >= Note.NoteMin && famitoneNote <= Note.NoteMax) { pattern.Notes[rowIdx].Value = (byte)famitoneNote; pattern.Notes[rowIdx].Instrument = j == 5 ? null : instruments[Convert.ToInt32(noteData[1], 16)]; } else { // Note outside of range. } } // Volume if (noteData[2] != ".") { pattern.Notes[rowIdx].Volume = Convert.ToByte(noteData[2], 16); } // Read FX. for (int k = 0; k < columns[j - 1]; k++) { string fx = noteData[3 + k]; switch (fx[0]) { case 'B': // Jump pattern.Notes[rowIdx].Effect = Note.EffectJump; break; case 'D': // Skip pattern.Notes[rowIdx].Effect = Note.EffectSkip; break; case 'F': // Tempo pattern.Notes[rowIdx].Effect = Note.EffectSpeed; break; default: continue; } pattern.Notes[rowIdx].EffectParam = Convert.ToByte(fx.Substring(1), 16); } } } } foreach (var s in project.Songs) { s.RemoveEmptyPatterns(); foreach (var c in s.Channels) { c.ColorizePatterns(); } } return(project); }
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]; var newNote = (Note)null; if (pattern != null) { pattern.Notes.TryGetValue(location.NoteIndex, out 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; } // Clear any pending release. if (newNote.IsMusicalOrStop) { releaseCounter = 0; } // 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) { var releaseLocation = location.Advance(song, newNote.Release); // Don't process release that go beyond the end of the song. if (releaseLocation.IsInSong(song)) { releaseCounter = newNote.Release; } } var stopLocation = location.Advance(song, newNote.Duration); // Don't stop notes that go beyond the end of the song. if (stopLocation.IsInSong(song)) { durationCounter = newNote.Duration; } } if (newNote.HasVolumeSlide) { channel.ComputeVolumeSlideNoteParams(newNote, location, famitrackerSpeed, palPlayback, out volumeSlideStep, out _); } // Store note for later if delayed. if (newNote.HasNoteDelay) { delayedNote = newNote; delayedNoteCounter = newNote.NoteDelay + 1; delayedNoteSlidePitch = noteSlidePitch; delayedNoteSlideStep = noteSlideStep; delayedNoteVolumeSlideStep = volumeSlideStep; return; } PlayNote(newNote, noteSlidePitch, noteSlideStep, volumeSlideStep, needClone); } }
private void BuildChannelColors(Song song, List <VideoChannelState> channels, VideoFrameMetadata[] meta, int colorMode) { Color[,] colors = new Color[meta.Length, meta[0].channelNotes.Length]; // Get the note colors. for (int i = 0; i < meta.Length; i++) { var m = meta[i]; m.channelColors = new Color[m.channelNotes.Length]; for (int j = 0; j < channels.Count; j++) { var note = m.channelNotes[channels[j].songChannelIndex]; if (note != null && note.IsMusical) { var color = Theme.LightGreyFillColor1; if (colorMode == OscilloscopeColorType.Channel) { var channel = song.Channels[channels[j].songChannelIndex]; for (int p = 0; p < channel.PatternInstances.Length; p++) { if (channel.PatternInstances[p] != null) { color = channel.PatternInstances[p].Color; break; } } } else if (colorMode != OscilloscopeColorType.None) { if (channels[j].channel.Type == ChannelType.Dpcm) { if (colorMode == OscilloscopeColorType.InstrumentsAndSamples) { var mapping = channels[j].channel.Song.Project.GetDPCMMapping(note.Value); if (mapping != null) { color = mapping.Sample.Color; } } } else { if (note.Instrument != null && (colorMode == OscilloscopeColorType.Instruments || colorMode == OscilloscopeColorType.InstrumentsAndSamples)) { color = note.Instrument.Color; } } } colors[i, j] = color; } } } // Extend any color until we hit another one. for (int i = 0; i < colors.GetLength(0) - 1; i++) { for (int j = 0; j < colors.GetLength(1); j++) { if (colors[i, j].A != 0) { if (colors[i + 1, j].A == 0) { colors[i + 1, j] = colors[i, j]; } } else { colors[i, j] = Theme.LightGreyFillColor1; } } } const int ColorBlendTime = 5; // Blend the colors. for (int i = 0; i < meta.Length; i++) { var m = meta[i]; for (int j = 0; j < m.channelColors.Length; j++) { int avgR = 0; int avgG = 0; int avgB = 0; int count = 0; for (int k = i; k < i + ColorBlendTime && k < meta.Length; k++) { avgR += colors[k, j].R; avgG += colors[k, j].G; avgB += colors[k, j].B; count++; } m.channelColors[j] = Color.FromArgb(avgR / count, avgG / count, avgB / count); } } }
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; } if (newNote.Arpeggio == null && note.Arpeggio != null && newNote.IsValid && !newNote.IsMusical) { newNote.Arpeggio = note.Arpeggio; } 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; } } } }
public Channel(Song song, int type, int songLength) { this.song = song; this.type = type; }