private void OutputInstruments()
        {
            // Process all envelope, make unique, etc.
            var uniqueEnvelopes     = new SortedList <uint, byte[]>();
            var instrumentEnvelopes = new Dictionary <Envelope, uint>();

            var defaultEnv          = new byte[] { 0xc0, 0x7f, 0x00, 0x00 };
            var defaultPitchEnv     = new byte[] { 0x00, 0xc0, 0x7f, 0x00, 0x01 };
            var defaultEnvCRC       = CRC32.Compute(defaultEnv);
            var defaultPitchEnvCRC  = CRC32.Compute(defaultPitchEnv);
            var defaultEnvName      = "";
            var defaultPitchEnvName = "";

            uniqueEnvelopes.Add(defaultEnvCRC, defaultEnv);

            if (kernel == FamiToneKernel.FamiStudio)
            {
                uniqueEnvelopes.Add(defaultPitchEnvCRC, defaultPitchEnv);
            }

            foreach (var instrument in project.Instruments)
            {
                for (int i = 0; i < Envelope.Count; i++)
                {
                    var env = instrument.Envelopes[i];

                    if (env == null)
                    {
                        continue;
                    }

                    if (kernel != FamiToneKernel.FamiStudio && i == Envelope.DutyCycle)
                    {
                        continue;
                    }

                    byte[] processed;

                    switch (i)
                    {
                    case Envelope.N163Waveform:
                        processed = env.BuildN163Waveform();
                        break;

                    case Envelope.FdsModulation:
                        processed = env.BuildFdsModulationTable().Select(m => (byte)m).ToArray();
                        break;

                    case Envelope.FdsWaveform:
                        processed = env.Values.Take(env.Length).Select(m => (byte)m).ToArray();
                        break;

                    default:
                        processed = ProcessEnvelope(env,
                                                    i == Envelope.Volume && kernel == FamiToneKernel.FamiStudio,
                                                    i == Envelope.Pitch && kernel == FamiToneKernel.FamiStudio);
                        break;
                    }

                    if (processed == null)
                    {
                        if (kernel == FamiToneKernel.FamiStudio && i == Envelope.Pitch)
                        {
                            instrumentEnvelopes[env] = defaultPitchEnvCRC;
                        }
                        else
                        {
                            instrumentEnvelopes[env] = defaultEnvCRC;
                        }
                    }
                    else
                    {
                        uint crc = CRC32.Compute(processed);
                        uniqueEnvelopes[crc]     = processed;
                        instrumentEnvelopes[env] = crc;
                    }
                }
            }

            // Write instruments
            lines.Add($"{ll}instruments:");

            for (int i = 0, j = 0; i < project.Instruments.Count; i++)
            {
                var instrument = project.Instruments[i];

                if (instrument.ExpansionType != Project.ExpansionFds &&
                    instrument.ExpansionType != Project.ExpansionN163 &&
                    instrument.ExpansionType != Project.ExpansionVrc7)
                {
                    var volumeEnvIdx   = uniqueEnvelopes.IndexOfKey(instrumentEnvelopes[instrument.Envelopes[Envelope.Volume]]);
                    var arpeggioEnvIdx = uniqueEnvelopes.IndexOfKey(instrumentEnvelopes[instrument.Envelopes[Envelope.Arpeggio]]);
                    var pitchEnvIdx    = uniqueEnvelopes.IndexOfKey(instrumentEnvelopes[instrument.Envelopes[Envelope.Pitch]]);

                    if (kernel == FamiToneKernel.FamiStudio)
                    {
                        var dutyEnvIdx = instrument.IsEnvelopeActive(Envelope.DutyCycle) ? uniqueEnvelopes.IndexOfKey(instrumentEnvelopes[instrument.Envelopes[Envelope.DutyCycle]]) : uniqueEnvelopes.IndexOfKey(defaultEnvCRC);

                        lines.Add($"\t{dw} {ll}env{volumeEnvIdx},{ll}env{arpeggioEnvIdx},{ll}env{dutyEnvIdx},{ll}env{pitchEnvIdx}");
                    }
                    else
                    {
                        var duty      = instrument.IsEnvelopeActive(Envelope.DutyCycle) ? instrument.Envelopes[Envelope.DutyCycle].Values[0] : 0;
                        var dutyShift = instrument.ExpansionType == Project.ExpansionNone ?    6 : 4;
                        var dutyBits  = instrument.ExpansionType == Project.ExpansionNone ? 0x30 : 0;

                        lines.Add($"\t{db} ${(duty << dutyShift) | dutyBits:x2} ;instrument {i:x2} ({instrument.Name})");
                        lines.Add($"\t{dw} {ll}env{volumeEnvIdx}, {ll}env{arpeggioEnvIdx}, {ll}env{pitchEnvIdx}");
                        lines.Add($"\t{db} $00");
                    }

                    instrumentIndices[instrument] = j++;
                }
            }

            lines.Add("");

            // FDS, N163 and VRC7 instruments are special.
            if (project.ExpansionAudio == Project.ExpansionFds ||
                project.ExpansionAudio == Project.ExpansionN163 ||
                project.ExpansionAudio == Project.ExpansionVrc7)
            {
                lines.Add($"{ll}instruments_{project.ExpansionAudioShortName.ToLower()}:");

                for (int i = 0, j = 0; i < project.Instruments.Count; i++)
                {
                    var instrument = project.Instruments[i];

                    if (instrument.ExpansionType != Project.ExpansionNone)
                    {
                        var volumeEnvIdx   = uniqueEnvelopes.IndexOfKey(instrumentEnvelopes[instrument.Envelopes[Envelope.Volume]]);
                        var arpeggioEnvIdx = uniqueEnvelopes.IndexOfKey(instrumentEnvelopes[instrument.Envelopes[Envelope.Arpeggio]]);
                        var pitchEnvIdx    = uniqueEnvelopes.IndexOfKey(instrumentEnvelopes[instrument.Envelopes[Envelope.Pitch]]);

                        lines.Add($"\t{dw} {ll}env{volumeEnvIdx}, {ll}env{arpeggioEnvIdx}, {ll}env{pitchEnvIdx}");

                        if (instrument.ExpansionType == Project.ExpansionFds)
                        {
                            var fdsWavEnvIdx = uniqueEnvelopes.IndexOfKey(instrumentEnvelopes[instrument.Envelopes[Envelope.FdsWaveform]]);
                            var fdsModEnvIdx = uniqueEnvelopes.IndexOfKey(instrumentEnvelopes[instrument.Envelopes[Envelope.FdsModulation]]);

                            lines.Add($"\t{db} {instrument.FdsMasterVolume}");
                            lines.Add($"\t{dw} {ll}env{fdsWavEnvIdx}, {ll}env{fdsModEnvIdx}, {instrument.FdsModSpeed}");
                            lines.Add($"\t{db} {instrument.FdsModDepth}, {instrument.FdsModDelay}, $00");
                        }
                        else if (instrument.ExpansionType == Project.ExpansionN163)
                        {
                            var n163WaveIdx = uniqueEnvelopes.IndexOfKey(instrumentEnvelopes[instrument.Envelopes[Envelope.N163Waveform]]);

                            lines.Add($"\t{db} ${instrument.N163WavePos / 2:x2}, ${instrument.N163WaveSize / 2:x2}");
                            lines.Add($"\t{dw} {ll}env{n163WaveIdx}");
                            lines.Add($"\t{db} $00, $00, $00, $00, $00, $00");
                        }
                        else if (instrument.ExpansionType == Project.ExpansionVrc7)
                        {
                            lines.Add($"\t{db} ${(instrument.Vrc7Patch << 4):x2}, $00");
                            lines.Add($"\t{db} {String.Join(",", instrument.Vrc7PatchRegs.Select(r => $"${r:x2}"))}");
                        }

                        instrumentIndices[instrument] = j++;
                    }
                }

                lines.Add("");
            }

            // Write samples.
            lines.Add($"{ll}samples:");

            if (project.UsesSamples)
            {
                for (int i = 1; i < project.SamplesMapping.Length; i++)
                {
                    var mapping            = project.SamplesMapping[i];
                    var sampleOffset       = 0;
                    var sampleSize         = 0;
                    var samplePitchAndLoop = 0;
                    var sampleName         = "";

                    if (mapping != null && mapping.Sample != null)
                    {
                        sampleOffset       = project.GetAddressForSample(mapping.Sample) >> 6;
                        sampleSize         = mapping.Sample.Data.Length >> 4;
                        sampleName         = $"({mapping.Sample.Name})";
                        samplePitchAndLoop = mapping.Pitch | ((mapping.Loop ? 1 : 0) << 6);
                    }

                    lines.Add($"\t{db} ${sampleOffset:x2}+{lo}(FT_DPCM_PTR),${sampleSize:x2},${samplePitchAndLoop:x2}\t;{i} {sampleName}");
                }

                lines.Add("");
            }

            // Write envelopes.
            int idx = 0;

            foreach (var kv in uniqueEnvelopes)
            {
                var name = $"{ll}env{idx++}";
                lines.Add($"{name}:");
                lines.Add($"\t{db} {String.Join(",", kv.Value.Select(i => $"${i:x2}"))}");

                if (kv.Key == defaultEnvCRC)
                {
                    defaultEnvName = name;
                }
                if (kv.Key == defaultPitchEnvCRC)
                {
                    defaultPitchEnvName = name;
                }
            }

            // Write the unique vibrato envelopes.
            if (kernel == FamiToneKernel.FamiStudio)
            {
                // Create all the unique vibrato envelopes.
                foreach (var s in project.Songs)
                {
                    foreach (var c in s.Channels)
                    {
                        foreach (var p in c.Patterns)
                        {
                            foreach (var note in p.Notes.Values)
                            {
                                if (note.HasVibrato)
                                {
                                    if (note.VibratoDepth == 0 || note.VibratoSpeed == 0)
                                    {
                                        note.RawVibrato         = 0;
                                        vibratoEnvelopeNames[0] = defaultPitchEnvName;
                                        continue;
                                    }

                                    var  env       = Envelope.CreateVibratoEnvelope(note.VibratoSpeed, note.VibratoDepth);
                                    var  processed = ProcessEnvelope(env, false, true);
                                    uint crc       = CRC32.Compute(processed);
                                    if (!uniqueEnvelopes.ContainsKey(crc))
                                    {
                                        var name = $"{ll}env{idx++}";
                                        lines.Add($"{name}:");
                                        lines.Add($"\t{db} {String.Join(",", processed.Select(i => $"${i:x2}"))}");

                                        uniqueEnvelopes[crc] = processed;
                                        vibratoEnvelopeNames[note.RawVibrato] = name;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
Beispiel #2
0
        private int OutputInstruments()
        {
            // Process all envelope, make unique, etc.
            var uniqueEnvelopes     = new SortedList <uint, byte[]>();
            var instrumentEnvelopes = new Dictionary <Envelope, uint>();

            var defaultEnv    = new byte[] { 0xc0, 0x00, 0x00 };
            var defaultEnvCRC = CRC32.Compute(defaultEnv);

            uniqueEnvelopes.Add(defaultEnvCRC, defaultEnv);

            foreach (var instrument in project.Instruments)
            {
                foreach (var env in instrument.Envelopes)
                {
                    var processed = ProcessEnvelope(env);

                    if (processed == null)
                    {
                        instrumentEnvelopes[env] = defaultEnvCRC;
                    }
                    else
                    {
                        uint crc = CRC32.Compute(processed);
                        uniqueEnvelopes[crc]     = processed;
                        instrumentEnvelopes[env] = crc;
                    }
                }
            }

            int size = 0;

            // Write instruments
            lines.Add($"{ll}instruments:");

            for (int i = 0; i < project.Instruments.Count; i++)
            {
                var instrument = project.Instruments[i];

                var volumeEnvIdx   = uniqueEnvelopes.IndexOfKey(instrumentEnvelopes[instrument.Envelopes[Envelope.Volume]]);
                var arpeggioEnvIdx = uniqueEnvelopes.IndexOfKey(instrumentEnvelopes[instrument.Envelopes[Envelope.Arpeggio]]);
                var pitchEnvIdx    = uniqueEnvelopes.IndexOfKey(instrumentEnvelopes[instrument.Envelopes[Envelope.Pitch]]);

                lines.Add($"\t{db} ${(instrument.DutyCycle << 6) | 0x30:x2} ;instrument {i:x2} ({instrument.Name})");
                lines.Add($"\t{dw} {ll}env{volumeEnvIdx},{ll}env{arpeggioEnvIdx},{ll}env{pitchEnvIdx}");
                lines.Add($"\t{db} $00");

                size += 2 * 3 + 2;
            }

            lines.Add("");

            // Write samples.
            lines.Add($"{ll}samples:");

            if (project.UsesSamples)
            {
                for (int i = 1; i < project.SamplesMapping.Length; i++)
                {
                    var mapping            = project.SamplesMapping[i];
                    var sampleOffset       = 0;
                    var sampleSize         = 0;
                    var samplePitchAndLoop = 0;
                    var sampleName         = "";

                    if (mapping != null && mapping.Sample != null)
                    {
                        sampleOffset       = project.GetAddressForSample(mapping.Sample) >> 6;
                        sampleSize         = mapping.Sample.Data.Length >> 4;
                        sampleName         = $"({mapping.Sample.Name})";
                        samplePitchAndLoop = mapping.Pitch | ((mapping.Loop ? 1 : 0) << 6);
                    }

                    lines.Add($"\t{db} ${sampleOffset:x2}+{lo}(FT_DPCM_PTR),${sampleSize:x2},${samplePitchAndLoop:x2}\t;{i} {sampleName}");
                    size += 3;
                }

                lines.Add("");
            }

            // Write envelopes.
            int idx = 0;

            foreach (var env in uniqueEnvelopes.Values)
            {
                lines.Add($"{ll}env{idx++}:");
                lines.Add($"\t{db} {String.Join(",", env.Select(i => $"${i:x2}"))}");
                size += env.Length;
            }

            return(size);
        }
Beispiel #3
0
        private int OutputInstruments()
        {
            // Process all envelope, make unique, etc.
            var uniqueEnvelopes     = new SortedList <uint, byte[]>();
            var instrumentEnvelopes = new Dictionary <Envelope, uint>();

            var defaultEnv          = new byte[] { 0xc0, 0x7f, 0x00, 0x00 };
            var defaultPitchEnv     = new byte[] { 0x00, 0xc0, 0x7f, 0x00, 0x01 };
            var defaultEnvCRC       = CRC32.Compute(defaultEnv);
            var defaultPitchEnvCRC  = CRC32.Compute(defaultPitchEnv);
            var defaultEnvName      = "";
            var defaultPitchEnvName = "";

            uniqueEnvelopes.Add(defaultEnvCRC, defaultEnv);

            if (kernel == FamiToneKernel.FamiTone2FS)
            {
                uniqueEnvelopes.Add(defaultPitchEnvCRC, defaultPitchEnv);
            }

            foreach (var instrument in project.Instruments)
            {
                for (int i = 0; i < Envelope.Max; i++)
                {
                    var env       = instrument.Envelopes[i];
                    var processed =
                        ProcessEnvelope(env,
                                        i == Envelope.Volume && kernel == FamiToneKernel.FamiTone2FS,
                                        i == Envelope.Pitch && kernel == FamiToneKernel.FamiTone2FS);

                    if (processed == null)
                    {
                        if (kernel == FamiToneKernel.FamiTone2FS && i == Envelope.Pitch)
                        {
                            instrumentEnvelopes[env] = defaultPitchEnvCRC;
                        }
                        else
                        {
                            instrumentEnvelopes[env] = defaultEnvCRC;
                        }
                    }
                    else
                    {
                        uint crc = CRC32.Compute(processed);
                        uniqueEnvelopes[crc]     = processed;
                        instrumentEnvelopes[env] = crc;
                    }
                }
            }

            int size = 0;

            // Write instruments
            lines.Add($"{ll}instruments:");

            for (int i = 0; i < project.Instruments.Count; i++)
            {
                var instrument = project.Instruments[i];

                var volumeEnvIdx   = uniqueEnvelopes.IndexOfKey(instrumentEnvelopes[instrument.Envelopes[Envelope.Volume]]);
                var arpeggioEnvIdx = uniqueEnvelopes.IndexOfKey(instrumentEnvelopes[instrument.Envelopes[Envelope.Arpeggio]]);
                var pitchEnvIdx    = uniqueEnvelopes.IndexOfKey(instrumentEnvelopes[instrument.Envelopes[Envelope.Pitch]]);
                var dutyShift      = instrument.ExpansionType == Project.ExpansionVrc6 ? 4 : 6;
                var dutyBits       = instrument.ExpansionType == Project.ExpansionVrc6 ? 0 : 0x30;

                lines.Add($"\t{db} ${(instrument.DutyCycle << dutyShift) | dutyBits:x2} ;instrument {i:x2} ({instrument.Name})");
                lines.Add($"\t{dw} {ll}env{volumeEnvIdx},{ll}env{arpeggioEnvIdx},{ll}env{pitchEnvIdx}");
                lines.Add($"\t{db} $00");

                size += 2 * 3 + 2;
            }

            lines.Add("");

            // Write samples.
            lines.Add($"{ll}samples:");

            if (project.UsesSamples)
            {
                for (int i = 1; i < project.SamplesMapping.Length; i++)
                {
                    var mapping            = project.SamplesMapping[i];
                    var sampleOffset       = 0;
                    var sampleSize         = 0;
                    var samplePitchAndLoop = 0;
                    var sampleName         = "";

                    if (mapping != null && mapping.Sample != null)
                    {
                        sampleOffset       = project.GetAddressForSample(mapping.Sample) >> 6;
                        sampleSize         = mapping.Sample.Data.Length >> 4;
                        sampleName         = $"({mapping.Sample.Name})";
                        samplePitchAndLoop = mapping.Pitch | ((mapping.Loop ? 1 : 0) << 6);
                    }

                    lines.Add($"\t{db} ${sampleOffset:x2}+{lo}(FT_DPCM_PTR),${sampleSize:x2},${samplePitchAndLoop:x2}\t;{i} {sampleName}");
                    size += 3;
                }

                lines.Add("");
            }

            // Write envelopes.
            int idx = 0;

            foreach (var kv in uniqueEnvelopes)
            {
                var name = $"{ll}env{idx++}";
                lines.Add($"{name}:");
                lines.Add($"\t{db} {String.Join(",", kv.Value.Select(i => $"${i:x2}"))}");
                size += kv.Value.Length;

                if (kv.Key == defaultEnvCRC)
                {
                    defaultEnvName = name;
                }
                if (kv.Key == defaultPitchEnvCRC)
                {
                    defaultPitchEnvName = name;
                }
            }

            // Write the unique vibrato envelopes.
            if (kernel == FamiToneKernel.FamiTone2FS)
            {
                // Create all the unique vibrato envelopes.
                foreach (var s in project.Songs)
                {
                    foreach (var c in s.Channels)
                    {
                        foreach (var p in c.Patterns)
                        {
                            for (int n = 0; n < s.PatternLength; n++)
                            {
                                var note = p.Notes[n];

                                if (note.HasVibrato)
                                {
                                    if (note.VibratoDepth == 0 || note.VibratoSpeed == 0)
                                    {
                                        p.Notes[n].Vibrato      = 0;
                                        vibratoEnvelopeNames[0] = defaultPitchEnvName;
                                        continue;
                                    }

                                    var  env       = Envelope.CreateVibratoEnvelope(note.VibratoSpeed, note.VibratoDepth);
                                    var  processed = ProcessEnvelope(env, false, true);
                                    uint crc       = CRC32.Compute(processed);
                                    if (!uniqueEnvelopes.ContainsKey(crc))
                                    {
                                        var name = $"{ll}env{idx++}";
                                        lines.Add($"{name}:");
                                        lines.Add($"\t{db} {String.Join(",", processed.Select(i => $"${i:x2}"))}");
                                        size += env.Length;

                                        uniqueEnvelopes[crc] = processed;
                                        vibratoEnvelopeNames[note.Vibrato] = name;
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(size);
        }