Ejemplo n.º 1
0
        public byte[] GetBytes()
        {
            byte[] buffer = null;
            byte oplChnl = MainForm.channelMap[midiChannel];
            if (oplChnl < 18)
            {
                byte baseReg = (oplChnl < 9) ? (byte)0x5e : (byte)0x5f;
                switch (type)
                {
                    case METype.progchange:
                        // read the patch file
                        byte[] gmData = GeneralMidi.GetInstrument(program);
                        MainForm.ChannelKSL[oplChnl] = gmData[8];

                        if ((gmData[0] & 4) != 0)
                        {
                            buffer = new byte[66];
                            // double voice instrument
                            // operator 1
                            byte addr1 = 0x20;
                            byte addr2 = 0x28;
                            Array.Copy(new byte[3] { baseReg, (byte)(addr1 + channelToOperatorOffset[oplChnl % 9]), gmData[4] }, 0, buffer, 0, 3);
                            Array.Copy(new byte[3] { baseReg, (byte)(addr1 + 0x40 + channelToOperatorOffset[oplChnl % 9]), gmData[5] }, 0, buffer, 3, 3);
                            Array.Copy(new byte[3] { baseReg, (byte)(addr1 + 0x60 + channelToOperatorOffset[oplChnl % 9]), gmData[6] }, 0, buffer, 6, 3);
                            Array.Copy(new byte[3] { baseReg, (byte)(addr1 + 0xC0 + channelToOperatorOffset[oplChnl % 9]), gmData[7] }, 0, buffer, 9, 3);
                            Array.Copy(new byte[3] { baseReg, (byte)(addr1 + 0x20 + channelToOperatorOffset[oplChnl % 9]), (byte)(gmData[8] + gmData[9]) }, 0, buffer, 12, 3);
                            Array.Copy(new byte[3] { baseReg, (byte)(addr1 + 0xA0 + oplChnl % 9), (byte)(gmData[10] | 0x30) }, 0, buffer, 15, 3);
                            // operator 2
                            Array.Copy(new byte[3] { baseReg, (byte)(addr1 + 3 + channelToOperatorOffset[oplChnl % 9]), gmData[11] }, 0, buffer, 18, 3);
                            Array.Copy(new byte[3] { baseReg, (byte)(addr1 + 0x40 + 3 + channelToOperatorOffset[oplChnl % 9]), gmData[12] }, 0, buffer, 21, 3);
                            Array.Copy(new byte[3] { baseReg, (byte)(addr1 + 0x60 + 3 + channelToOperatorOffset[oplChnl % 9]), gmData[13] }, 0, buffer, 24, 3);
                            Array.Copy(new byte[3] { baseReg, (byte)(addr1 + 0xC0 + 3 + channelToOperatorOffset[oplChnl % 9]), gmData[14] }, 0, buffer, 27, 3);
                            Array.Copy(new byte[3] { baseReg, (byte)(addr1 + 0x20 + 3 + channelToOperatorOffset[oplChnl % 9]), (byte)(gmData[15] | gmData[16]) }, 0, buffer, 30, 3);
                            // operator 3
                            Array.Copy(new byte[3] { baseReg, (byte)(addr2 + channelToOperatorOffset[oplChnl % 9]), gmData[20] }, 0, buffer, 33, 3);
                            Array.Copy(new byte[3] { baseReg, (byte)(addr2 + 0x40 + channelToOperatorOffset[oplChnl % 9]), gmData[21] }, 0, buffer, 36, 3);
                            Array.Copy(new byte[3] { baseReg, (byte)(addr2 + 0x60 + channelToOperatorOffset[oplChnl % 9]), gmData[22] }, 0, buffer, 39, 3);
                            Array.Copy(new byte[3] { baseReg, (byte)(addr2 + 0xC0 + channelToOperatorOffset[oplChnl % 9]), gmData[23] }, 0, buffer, 42, 3);
                            Array.Copy(new byte[3] { baseReg, (byte)(addr2 + 0x20 + channelToOperatorOffset[oplChnl % 9]), (byte)(gmData[24] + gmData[25]) }, 0, buffer, 45, 3);
                            Array.Copy(new byte[3] { baseReg, (byte)(addr2 + 0xA0 + oplChnl % 9), (byte)(gmData[26] | 0x30) }, 0, buffer, 48, 3);
                            // operator 4
                            Array.Copy(new byte[3] { baseReg, (byte)(addr2 + 3 + channelToOperatorOffset[oplChnl % 9]), gmData[27] }, 0, buffer, 51, 3);
                            Array.Copy(new byte[3] { baseReg, (byte)(addr2 + 0x40 + 3 + channelToOperatorOffset[oplChnl % 9]), gmData[28] }, 0, buffer, 54, 3);
                            Array.Copy(new byte[3] { baseReg, (byte)(addr2 + 0x60 + 3 + channelToOperatorOffset[oplChnl % 9]), gmData[29] }, 0, buffer, 57, 3);
                            Array.Copy(new byte[3] { baseReg, (byte)(addr2 + 0xC0 + 3 + channelToOperatorOffset[oplChnl % 9]), gmData[30] }, 0, buffer, 60, 3);
                            Array.Copy(new byte[3] { baseReg, (byte)(addr2 + 0x20 + 3 + channelToOperatorOffset[oplChnl % 9]), (byte)(gmData[31] | gmData[32]) }, 0, buffer, 63, 3);
                        }
                        else
                        {
                            buffer = new byte[33];
                            // Single voice channel
                            // operator 1
                            Array.Copy(new byte[3] { baseReg, (byte)(0x20 + channelToOperatorOffset[oplChnl % 9]), gmData[4] }, 0, buffer, 0, 3);  // tremolo/vibrato/sustain/KSR/Freq Mult
                            Array.Copy(new byte[3] { baseReg, (byte)(0x60 + channelToOperatorOffset[oplChnl % 9]), gmData[5] }, 0, buffer, 3, 3);  // attack/decay
                            Array.Copy(new byte[3] { baseReg, (byte)(0x80 + channelToOperatorOffset[oplChnl % 9]), gmData[6] }, 0, buffer, 6, 3);  // sustain/release
                            Array.Copy(new byte[3] { baseReg, (byte)(0xE0 + channelToOperatorOffset[oplChnl % 9]), gmData[7] }, 0, buffer, 9, 3);  // Waveform select
                            Array.Copy(new byte[3] { baseReg, (byte)(0x40 + channelToOperatorOffset[oplChnl % 9]), (byte)(gmData[8] + gmData[9]) }, 0, buffer, 12, 3);  // KSL/Output Level
                            Array.Copy(new byte[3] { baseReg, (byte)(0xC0 + oplChnl % 9), (byte)(gmData[10] | 0xF0) }, 0, buffer, 15, 3);  // Speaker/Feedback/Syn Type
                                                                                                                                           // operator 2
                            Array.Copy(new byte[3] { baseReg, (byte)(0x23 + channelToOperatorOffset[oplChnl % 9]), gmData[11] }, 0, buffer, 18, 3);  // tremolo/vibrato/sustain/KSR/Freq Mult
                            Array.Copy(new byte[3] { baseReg, (byte)(0x63 + channelToOperatorOffset[oplChnl % 9]), gmData[12] }, 0, buffer, 21, 3);  // attack/decay
                            Array.Copy(new byte[3] { baseReg, (byte)(0x83 + channelToOperatorOffset[oplChnl % 9]), gmData[13] }, 0, buffer, 24, 3);  // sustain/release
                            Array.Copy(new byte[3] { baseReg, (byte)(0xE3 + channelToOperatorOffset[oplChnl % 9]), gmData[14] }, 0, buffer, 27, 3);  // Waveform select
                            Array.Copy(new byte[3] { baseReg, (byte)(0x43 + channelToOperatorOffset[oplChnl % 9]), (byte)(gmData[15] + gmData[16]) }, 0, buffer, 30, 3);  // KSL/Output Level
                        }
                        break;
                    case METype.noteon:

                        if (midiChannel == 9 && MainForm.PercussionSet != 0)
                        {
                            buffer = new byte[6];
                            bool BD = (note == 35) | (note == 36);
                            bool SN = (note == 38) | (note == 40);
                            bool TT = (note == 41) | (note == 45);
                            bool CY = (note == 49) | (note == 55) | (note == 57);
                            bool HH = (note == 46) | (note == 42);

                            buffer[0] = 0x5e;
                            buffer[1] = 0xBD;
                            buffer[3] = 0x5e;
                            if (BD)
                            {
                                MainForm.PercussionSet = velocity == 0 ? (byte)(MainForm.PercussionSet & ~0x10) : (byte)(MainForm.PercussionSet | 0x10);
                                buffer[4] = (byte)(0x40 + 0x10);

                            }
                            if (SN)
                            {
                                MainForm.PercussionSet = velocity == 0 ? (byte)(MainForm.PercussionSet & ~0x8) : (byte)(MainForm.PercussionSet | 0x8);
                                buffer[4] = (byte)(0x40 + 0x14);
                            }
                            if (TT)
                            {
                                MainForm.PercussionSet = velocity == 0 ? (byte)(MainForm.PercussionSet & ~0x4) : (byte)(MainForm.PercussionSet | 0x4);
                                buffer[4] = (byte)(0x40 + 0x12);
                            }
                            if (CY)
                            {
                                MainForm.PercussionSet = velocity == 0 ? (byte)(MainForm.PercussionSet & ~0x2) : (byte)(MainForm.PercussionSet | 0x2);
                                buffer[4] = (byte)(0x40 + 0x15);
                            }
                            if (HH)
                            {
                                MainForm.PercussionSet = velocity == 0 ? (byte)(MainForm.PercussionSet & ~0x1) : (byte)(MainForm.PercussionSet | 0x1);
                                buffer[4] = (byte)(0x40 + 0x11);
                            }
                            buffer[2] = MainForm.PercussionSet;
                            buffer[5] = (byte)(0x3F - (velocity >> 1));  // attenuation
                        }
                        else
                        {
                            buffer = new byte[9];
                            byte[] onFreq = GetFreq(note);
                            buffer[0] = baseReg;
                            buffer[1] = (byte)(0xA0 + oplChnl % 9);
                            buffer[2] = onFreq[0];
                            buffer[3] = baseReg;
                            buffer[4] = (byte)(0xB0 + oplChnl % 9);
                            buffer[5] = (byte)(onFreq[1] | (velocity != 0 ? 0x20 : 0));  // set KEY on

                            buffer[6] = baseReg;
                            buffer[7] = (byte)(0x40 + channelToOperatorOffset[oplChnl % 9]);
                            buffer[8] = (byte)(MainForm.ChannelKSL[oplChnl] | (0x3F - (velocity >> 1)));  // attenuation
                        }
                        break;
                    case METype.noteoff:
                        buffer = new byte[3];
                        _ = GetFreq(note);
                        if (midiChannel == 9 && MainForm.PercussionSet != 0)
                        {
                            bool BD = (note == 35) | (note == 36);
                            bool SN = (note == 38) | (note == 40);
                            bool TT = (note == 41) | (note == 45);
                            bool CY = (note == 49) | (note == 57);
                            bool HH = (note == 46) | (note == 42);
                            buffer[0] = 0x5e;
                            buffer[1] = 0xBD;
                            if (BD)
                            {
                                MainForm.PercussionSet = (byte)(MainForm.PercussionSet & ~0x10);
                                buffer[2] = (byte)(0x40 + 0x10);

                            }
                            if (SN)
                            {
                                MainForm.PercussionSet = (byte)(MainForm.PercussionSet & ~0x8);
                                buffer[2] = (byte)(0x40 + 0x14);
                            }
                            if (TT)
                            {
                                MainForm.PercussionSet = (byte)(MainForm.PercussionSet & ~0x4);
                                buffer[2] = (byte)(0x40 + 0x12);
                            }
                            if (CY)
                            {
                                MainForm.PercussionSet = (byte)(MainForm.PercussionSet & ~0x2);
                                buffer[2] = (byte)(0x40 + 0x15);
                            }
                            if (HH)
                            {
                                MainForm.PercussionSet = (byte)(MainForm.PercussionSet & ~0x1);
                                buffer[2] = (byte)(0x40 + 0x11);
                            }
                        }
                        else
                        {
                            buffer[0] = baseReg;
                            buffer[1] = (byte)(0xB0 + oplChnl % 9);
                            buffer[2] = 0;
                        }
                        break;
                }
            }
            else
            {
                // program the drum channels

            }
            return buffer;
        }
Ejemplo n.º 2
0
        private void GenerateVGMButton_Click(object sender, EventArgs e)
        {
            // Print all events
            events.Sort(new MidiEventComparer());

            StringBuilder sb = new StringBuilder();

            // First pass - map midi channels to OPL3 channels
            byte        fourOps           = 0;
            byte        twoOps            = 0;
            List <byte> availableChannels = new List <byte>();

            availableChannels.Add(0);
            availableChannels.Add(1);
            availableChannels.Add(2);
            availableChannels.Add(3);
            availableChannels.Add(4);
            availableChannels.Add(5);
            if (PercussionSet == 0)
            {
                availableChannels.Add(6);
                availableChannels.Add(7);
                availableChannels.Add(8);
            }
            availableChannels.Add(9);
            availableChannels.Add(10);
            availableChannels.Add(11);
            availableChannels.Add(12);
            availableChannels.Add(13);
            availableChannels.Add(14);
            availableChannels.Add(15);
            availableChannels.Add(16);
            availableChannels.Add(17);
            byte[] twoOpChannels = new byte[15];
            for (int i = 0; i < channelMap.Length; i++)
            {
                channelMap[i] = 0xF0;
            }
            if (PercussionSet != 0)
            {
                channelMap[9] = 6;
            }

            foreach (MidiEvent ev in events)
            {
                if (ev.type != METype.progchange)
                {
                    break;
                }

                if (ev.midiChannel != 9 || PercussionSet != 0)
                {
                    byte[] prog = GeneralMidi.GetInstrument(ev.program);
                    if (prog[0] == 4)
                    {
                        // allocate 4 ops until you can't
                        if (fourOps < 7)
                        {
                            // check if the channel has already been assigned
                            if (channelMap[ev.midiChannel] == 0xF0)
                            {
                                byte opChnl = (byte)(fourOps < 3 ? fourOps : fourOps + 5);
                                channelMap[ev.midiChannel] = opChnl;
                                availableChannels.Remove(opChnl);
                                availableChannels.Remove((byte)(opChnl + 3));
                                fourOps++;
                            }
                        }
                        else
                        {
                            // disable this channel
                            channelMap[ev.midiChannel] = 0xFF;
                        }
                    }
                    else
                    {
                        twoOpChannels[twoOps] = ev.midiChannel;
                        twoOps++;
                    }
                }
            }
            if (fourOps * 4 + twoOps * 2 + 6 > 36)
            {
                throw new Exception("Insufficient number of operators!");
            }
            else
            {
                sb.AppendFormat("Two Op Channels: {0}, Four Op Channels: {1}", twoOps, fourOps).AppendLine();
            }
            // given a number of 4 operator channels, return the starting offset
            for (int i = 0; i < twoOps; i++)
            {
                byte top = availableChannels[0];
                channelMap[twoOpChannels[i]] = top;
                availableChannels.Remove(top);
            }
            byte OPL3Mode      = (byte)(fourOps != 0 ? 1 : 0);
            byte connectionSel = (byte)(Math.Pow(2, fourOps) - 1);

            int          totalWaits = 0;
            int          totalBytes = 0;
            MemoryStream ms         = new MemoryStream();

            // set the machine in OPL3 mode - no timers
            byte[] initialRegister =
            {
                0x5f,           5, OPL3Mode,      // OPL3 mode
                0x5e,           1, 0x20,          // Waveform Select - Test Registers
                0x5e,           2,    0,          // timer 1
                0x5e,           3,    0,          // timer 2
                0x5e,           4, 0x60,          // RST, Timer Masks, Timer Starts
                0x5e,           8, 0x40,          // Keyboard Split
                0x5e,        0xBD, PercussionSet, // Drum Mode
                // address 1
                0x5f,           1,  0x0,          // Waveform Select - Test Registers
                0x5f,           4,    0,          // connection sel
                0x5e,        0xB6,  0xc,          // clearing KEY ON for percussion
                0x5e,        0xB7,  0xc,          // clearing KEY ON for percussion
                0x5e,        0xB8,  0xc,          // clearing KEY ON for percussion
                0x5e,        0xA6, 0xf0,          // freq bd
                0x5e,        0xA7, 0xf0,          // freq sn
                0x5e,        0xA8, 0xf0,          // freq tt

                //0x5e, 0xC0, 0x0,   // enabling output
                //0x5e, 0xC1, 0x0,   // enabling output
                //0x5e, 0xC2, 0x0,   // enabling output
                //0x5e, 0xC3, 0x0,   // enabling output
                //0x5e, 0xC4, 0x0,   // enabling output
                //0x5e, 0xC5, 0x0,   // enabling output
                0x5e,        0xC6,  0x0, // enabling output
                0x5e,        0xC7,  0x0, // enabling output
                0x5e,        0xC8,  0x0, // enabling output

                // default snare sound
                //01 f6 08 05 0 0c 08 20 f6 04 01 0 0 0
                0x5e, 0x20 + 0x14,   01,
                0x5e, 0x60 + 0x14, 0xf6,
                0x5e, 0x80 + 0x14,    8,
                0x5e, 0xE0 + 0x14,    5,
                0x5e, 0x40 + 0x14, 0 + 0xC
            };
            ms.Write(initialRegister, 0, initialRegister.Length);
            totalBytes += initialRegister.Length;

            int idx = 0;

            foreach (MidiEvent ev in events)
            {
                if (ev.index - idx > 0)
                {
                    int wait = (ev.index - idx) * samplesPerTick;
                    totalWaits += wait;
                    while (wait > 65535)
                    {
                        sb.Append("Adding Wait: ").Append(65535).AppendLine();
                        ms.Write(new byte[3] {
                            0x61, 0xFF, 0xFF
                        }, 0, 3);
                        totalBytes += 3;
                        wait       -= 65535;
                    }
                    if (wait > 0)
                    {
                        sb.Append("Adding Wait: ").Append(wait).AppendLine();
                        ms.Write(new byte[3] {
                            0x61, (byte)(wait & 0xFF), (byte)(wait >> 8)
                        }, 0, 3);
                        totalBytes += 3;
                    }
                }
                if (SingleChannel.SelectedIndex == 0 || (SingleChannel.SelectedIndex - 1 == ev.midiChannel))
                {
                    sb.Append(ev.index).Append("\t").Append(ev).AppendLine();
                    byte[] partial = ev.GetBytes();
                    if (partial != null)
                    {
                        ms.Write(partial, 0, partial.Length);
                        totalBytes += partial.Length;
                    }
                }
                idx = ev.index;
            }
            ms.Write(new byte[1] {
                0x66
            }, 0, 1);                             // end of song
            totalBytes         += 1;
            MIDIOutputText.Text = sb.ToString();
            byte[] gd3 = CreateGD3();

            // Write the file
            string vgmFileName = Path.ChangeExtension(FileLabel.Text, ".vgm");

            if (File.Exists(vgmFileName))
            {
                File.Delete(vgmFileName);
            }
            FileStream stream = new FileStream(vgmFileName, FileMode.CreateNew);

            byte[] header = GetVGMHeader(totalWaits, totalBytes + 0x80, gd3.Length);
            stream.Write(header, 0, header.Length);
            // write data
            byte[] data = ms.GetBuffer();
            stream.Write(data, 0, (int)ms.Length);

            // write GD3 stuff
            stream.Write(gd3, 0, gd3.Length);
            stream.Flush();
            stream.Close();
        }