Пример #1
0
        public void Serialize(ref Song song)
        {
            int songId = song == null ? -1 : song.Id;

            Serialize(ref songId);
        }
Пример #2
0
        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);
        }
Пример #3
0
        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);
            }
        }
Пример #4
0
        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);
                }
            }
        }
Пример #5
0
        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;
                    }
                }
            }
        }
Пример #6
0
 public Channel(Song song, int type, int songLength)
 {
     this.song = song;
     this.type = type;
 }