Advertisement
believe_me

MIDI

Sep 15th, 2022
340
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 17.57 KB | None | 0 0
  1. /*
  2.     Programming MIDI: Parsing, Displaying (& Playing) MIDI Files
  3.     "Better get these done before im virused..." - javidx9
  4.     License (OLC-3)
  5.     ~~~~~~~~~~~~~~~
  6.     Copyright 2018-2020 OneLoneCoder.com
  7.     Redistribution and use in source and binary forms, with or without
  8.     modification, are permitted provided that the following conditions
  9.     are met:
  10.     1. Redistributions or derivations of source code must retain the above
  11.     copyright notice, this list of conditions and the following disclaimer.
  12.     2. Redistributions or derivative works in binary form must reproduce
  13.     the above copyright notice. This list of conditions and the following
  14.     disclaimer must be reproduced in the documentation and/or other
  15.     materials provided with the distribution.
  16.     3. Neither the name of the copyright holder nor the names of its
  17.     contributors may be used to endorse or promote products derived
  18.     from this software without specific prior written permission.
  19.     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20.     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21.     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22.     A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23.     HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24.     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25.     LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26.     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27.     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28.     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29.     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30.     Relevant Video: https://youtu.be/040BKtnDdg0
  31.     Links
  32.     ~~~~~
  33.     YouTube:    https://www.youtube.com/javidx9
  34.                 https://www.youtube.com/javidx9extra
  35.     Discord:    https://discord.gg/WhwHUMV
  36.     Twitter:    https://www.twitter.com/javidx9
  37.     Twitch:     https://www.twitch.tv/javidx9
  38.     GitHub:     https://www.github.com/onelonecoder
  39.     Patreon:    https://www.patreon.com/javidx9
  40.     Homepage:   https://www.onelonecoder.com
  41.    
  42.     Community:  https://community.onelonecoder.com
  43.     Author
  44.     ~~~~~~
  45.     David Barr, aka javidx9, ©OneLoneCoder 2018, 2019, 2020
  46. */
  47.  
  48.  
  49. #define OLC_PGE_APPLICATION
  50. #include "olcPixelGameEngine.h"
  51.  
  52. #include <fstream>
  53. #include <array>
  54.  
  55. //#pragma comment(lib, "winmm.lib")
  56.  
  57.  
  58. struct MidiEvent
  59. {
  60.     enum class Type
  61.     {
  62.         NoteOff,
  63.         NoteOn,
  64.         Other
  65.     } event;
  66.  
  67.     uint8_t nKey = 0;
  68.     uint8_t nVelocity = 0;
  69.     uint32_t nDeltaTick = 0;
  70. };
  71.  
  72.  
  73. struct MidiNote
  74. {
  75.     uint8_t nKey = 0;
  76.     uint8_t nVelocity = 0;
  77.     uint32_t nStartTime = 0;
  78.     uint32_t nDuration = 0;
  79. };
  80.  
  81. struct MidiTrack
  82. {
  83.     std::string sName;
  84.     std::string sInstrument;
  85.     std::vector<MidiEvent> vecEvents;
  86.     std::vector<MidiNote> vecNotes;
  87.     uint8_t nMaxNote = 64;
  88.     uint8_t nMinNote = 64;
  89. };
  90.  
  91.  
  92. class MidiFile
  93. {
  94. public:
  95.     enum EventName : uint8_t
  96.     {                  
  97.         VoiceNoteOff = 0x80,
  98.         VoiceNoteOn = 0x90,
  99.         VoiceAftertouch = 0xA0,
  100.         VoiceControlChange = 0xB0,
  101.         VoiceProgramChange = 0xC0,
  102.         VoiceChannelPressure = 0xD0,
  103.         VoicePitchBend = 0xE0,
  104.         SystemExclusive = 0xF0,    
  105.     };
  106.  
  107.     enum MetaEventName : uint8_t
  108.     {
  109.         MetaSequence = 0x00,
  110.         MetaText = 0x01,
  111.         MetaCopyright = 0x02,
  112.         MetaTrackName = 0x03,
  113.         MetaInstrumentName = 0x04,
  114.         MetaLyrics = 0x05,
  115.         MetaMarker = 0x06,
  116.         MetaCuePoint = 0x07,
  117.         MetaChannelPrefix = 0x20,
  118.         MetaEndOfTrack = 0x2F,
  119.         MetaSetTempo = 0x51,
  120.         MetaSMPTEOffset = 0x54,
  121.         MetaTimeSignature = 0x58,
  122.         MetaKeySignature = 0x59,
  123.         MetaSequencerSpecific = 0x7F,
  124.     };
  125.  
  126. public:
  127.     MidiFile()
  128.     {
  129.     }
  130.  
  131.     MidiFile(const std::string& sFileName)
  132.     {
  133.         ParseFile(sFileName);
  134.     }
  135.  
  136.     void Clear()
  137.     {
  138.  
  139.     }
  140.  
  141.     bool ParseFile(const std::string& sFileName)
  142.     {
  143.         // Open the MIDI File as a stream
  144.         std::ifstream ifs;
  145.         ifs.open(sFileName, std::fstream::in | std::ios::binary);
  146.         if (!ifs.is_open())
  147.             return false;
  148.  
  149.  
  150.         // Helper Utilities ====================
  151.  
  152.         // Swaps byte order of 32-bit integer
  153.         auto Swap32 = [](uint32_t n)
  154.         {
  155.             return (((n >> 24) & 0xff) | ((n << 8) & 0xff0000) | ((n >> 8) & 0xff00) | ((n << 24) & 0xff000000));
  156.         };
  157.  
  158.         // Swaps byte order of 16-bit integer
  159.         auto Swap16 = [](uint16_t n)
  160.         {
  161.             return ((n >> 8) | (n << 8));
  162.         };
  163.  
  164.         // Reads nLength bytes form file stream, and constructs a text string
  165.         auto ReadString = [&ifs](uint32_t nLength)
  166.         {
  167.             std::string s;
  168.             for (uint32_t i = 0; i < nLength; i++) s += ifs.get();
  169.             return s;
  170.         };
  171.  
  172.         // Reads a compressed MIDI value. This can be up to 32 bits long. Essentially if the first byte, first
  173.         // bit is set to 1, that indicates that the next byte is required to construct the full word. Only
  174.         // the bottom 7 bits of each byte are used to construct the final word value. Each successive byte
  175.         // that has MSB set, indicates a further byte needs to be read.
  176.         auto ReadValue = [&ifs]()
  177.         {
  178.             uint32_t nValue = 0;
  179.             uint8_t nByte = 0;
  180.  
  181.             // Read byte
  182.             nValue = ifs.get();
  183.  
  184.             // Check MSB, if set, more bytes need reading
  185.             if (nValue & 0x80)
  186.             {
  187.                 // Extract bottom 7 bits of read byte
  188.                 nValue &= 0x7F;
  189.                 do
  190.                 {
  191.                     // Read next byte
  192.                     nByte = ifs.get();
  193.  
  194.                     // Construct value by setting bottom 7 bits, then shifting 7 bits
  195.                     nValue = (nValue << 7) | (nByte & 0x7F);
  196.                 }
  197.                 while (nByte & 0x80); // Loop whilst read byte MSB is 1
  198.             }
  199.  
  200.             // Return final construction (always 32-bit unsigned integer internally)
  201.             return nValue;
  202.         };
  203.  
  204.         uint32_t n32 = 0;
  205.         uint16_t n16 = 0;
  206.  
  207.         // Read MIDI Header (Fixed Size)
  208.         ifs.read((char*)&n32, sizeof(uint32_t));
  209.         uint32_t nFileID = Swap32(n32);
  210.         ifs.read((char*)&n32, sizeof(uint32_t));
  211.         uint32_t nHeaderLength = Swap32(n32);
  212.         ifs.read((char*)&n16, sizeof(uint16_t));
  213.         uint16_t nFormat = Swap16(n16);
  214.         ifs.read((char*)&n16, sizeof(uint16_t));
  215.         uint16_t nTrackChunks = Swap16(n16);
  216.         ifs.read((char*)&n16, sizeof(uint16_t));
  217.         uint16_t nDivision = Swap16(n16);
  218.  
  219.         for (uint16_t nChunk = 0; nChunk < nTrackChunks; nChunk++)
  220.         {          
  221.             std::cout << "===== NEW TRACK" << std::endl;
  222.             // Read Track Header
  223.             ifs.read((char*)&n32, sizeof(uint32_t));
  224.             uint32_t nTrackID = Swap32(n32);
  225.             ifs.read((char*)&n32, sizeof(uint32_t));
  226.             uint32_t nTrackLength = Swap32(n32);
  227.  
  228.             bool bEndOfTrack = false;
  229.  
  230.             vecTracks.push_back(MidiTrack());
  231.  
  232.             uint32_t nWallTime = 0;
  233.  
  234.             uint8_t nPreviousStatus = 0;
  235.  
  236.             while (!ifs.eof() && !bEndOfTrack)
  237.             {
  238.                 // Fundamentally all MIDI Events contain a timecode, and a status byte*
  239.                 uint32_t nStatusTimeDelta = 0;
  240.                 uint8_t nStatus = 0;
  241.  
  242.  
  243.                 // Read Timecode from MIDI stream. This could be variable in length
  244.                 // and is the delta in "ticks" from the previous event. Of course this value
  245.                 // could be 0 if two events happen simultaneously.
  246.                 nStatusTimeDelta = ReadValue();
  247.  
  248.                 // Read first byte of message, this could be the status byte, or it could not...
  249.                 nStatus = ifs.get();
  250.  
  251.                 // All MIDI Status events have the MSB set. The data within a standard MIDI event
  252.                 // does not. A crude yet utilised form of compression is to omit sending status
  253.                 // bytes if the following sequence of events all refer to the same MIDI Status.
  254.                 // This is called MIDI Running Status, and is essential to succesful decoding of
  255.                 // MIDI streams and files.
  256.                 //
  257.                 // If the MSB of the read byte was not set, and on the whole we were expecting a
  258.                 // status byte, then Running Status is in effect, so we refer to the previous
  259.                 // confirmed status byte.
  260.                 if (nStatus < 0x80)
  261.                 {
  262.                     // MIDI Running Status is happening, so refer to previous valid MIDI Status byte
  263.                     nStatus = nPreviousStatus;
  264.  
  265.                     // We had to read the byte to assess if MIDI Running Status is in effect. But!
  266.                     // that read removed the byte form the stream, and that will desync all of the
  267.                     // following code because normally we would have read a status byte, but instead
  268.                     // we have read the data contained within a MIDI message. The simple solution is
  269.                     // to put the byte back :P
  270.                     ifs.seekg(-1, std::ios_base::cur);
  271.                 }
  272.  
  273.                
  274.  
  275.                 if ((nStatus & 0xF0) == EventName::VoiceNoteOff)
  276.                 {
  277.                     nPreviousStatus = nStatus;
  278.                     uint8_t nChannel = nStatus & 0x0F;
  279.                     uint8_t nNoteID = ifs.get();
  280.                     uint8_t nNoteVelocity = ifs.get();
  281.                     vecTracks[nChunk].vecEvents.push_back({ MidiEvent::Type::NoteOff, nNoteID, nNoteVelocity, nStatusTimeDelta });
  282.                 }
  283.  
  284.                 else if ((nStatus & 0xF0) == EventName::VoiceNoteOn)
  285.                 {
  286.                     nPreviousStatus = nStatus;
  287.                     uint8_t nChannel = nStatus & 0x0F;
  288.                     uint8_t nNoteID = ifs.get();
  289.                     uint8_t nNoteVelocity = ifs.get();
  290.                     if(nNoteVelocity == 0)
  291.                         vecTracks[nChunk].vecEvents.push_back({ MidiEvent::Type::NoteOff, nNoteID, nNoteVelocity, nStatusTimeDelta });
  292.                     else
  293.                         vecTracks[nChunk].vecEvents.push_back({ MidiEvent::Type::NoteOn, nNoteID, nNoteVelocity, nStatusTimeDelta });
  294.                 }
  295.  
  296.                 else if ((nStatus & 0xF0) == EventName::VoiceAftertouch)
  297.                 {
  298.                     nPreviousStatus = nStatus;
  299.                     uint8_t nChannel = nStatus & 0x0F;
  300.                     uint8_t nNoteID = ifs.get();
  301.                     uint8_t nNoteVelocity = ifs.get();
  302.                     vecTracks[nChunk].vecEvents.push_back({ MidiEvent::Type::Other });
  303.                 }
  304.  
  305.                 else if ((nStatus & 0xF0) == EventName::VoiceControlChange)
  306.                 {
  307.                     nPreviousStatus = nStatus;
  308.                     uint8_t nChannel = nStatus & 0x0F;
  309.                     uint8_t nControlID = ifs.get();
  310.                     uint8_t nControlValue = ifs.get();
  311.                     vecTracks[nChunk].vecEvents.push_back({ MidiEvent::Type::Other });
  312.                 }
  313.  
  314.                 else if ((nStatus & 0xF0) == EventName::VoiceProgramChange)
  315.                 {
  316.                     nPreviousStatus = nStatus;
  317.                     uint8_t nChannel = nStatus & 0x0F;
  318.                     uint8_t nProgramID = ifs.get();                
  319.                     vecTracks[nChunk].vecEvents.push_back({ MidiEvent::Type::Other });
  320.                 }
  321.  
  322.                 else if ((nStatus & 0xF0) == EventName::VoiceChannelPressure)
  323.                 {
  324.                     nPreviousStatus = nStatus;
  325.                     uint8_t nChannel = nStatus & 0x0F;
  326.                     uint8_t nChannelPressure = ifs.get();
  327.                     vecTracks[nChunk].vecEvents.push_back({ MidiEvent::Type::Other });
  328.                 }
  329.  
  330.                 else if ((nStatus & 0xF0) == EventName::VoicePitchBend)
  331.                 {
  332.                     nPreviousStatus = nStatus;
  333.                     uint8_t nChannel = nStatus & 0x0F;
  334.                     uint8_t nLS7B = ifs.get();
  335.                     uint8_t nMS7B = ifs.get();
  336.                     vecTracks[nChunk].vecEvents.push_back({ MidiEvent::Type::Other });
  337.  
  338.                 }
  339.  
  340.                 else if ((nStatus & 0xF0) == EventName::SystemExclusive)
  341.                 {
  342.                     nPreviousStatus = 0;
  343.  
  344.                     if (nStatus == 0xFF)
  345.                     {
  346.                         // Meta Message
  347.                         uint8_t nType = ifs.get();
  348.                         uint8_t nLength = ReadValue();
  349.  
  350.                         switch (nType)
  351.                         {
  352.                         case MetaSequence:
  353.                             std::cout << "Sequence Number: " << ifs.get() << ifs.get() << std::endl;
  354.                             break;
  355.                         case MetaText:
  356.                             std::cout << "Text: " << ReadString(nLength) << std::endl;
  357.                             break;
  358.                         case MetaCopyright:
  359.                             std::cout << "Copyright: " << ReadString(nLength) << std::endl;
  360.                             break;
  361.                         case MetaTrackName:
  362.                             vecTracks[nChunk].sName = ReadString(nLength);
  363.                             std::cout << "Track Name: " << vecTracks[nChunk].sName << std::endl;                           
  364.                             break;
  365.                         case MetaInstrumentName:
  366.                             vecTracks[nChunk].sInstrument = ReadString(nLength);
  367.                             std::cout << "Instrument Name: " << vecTracks[nChunk].sInstrument << std::endl;
  368.                             break;
  369.                         case MetaLyrics:
  370.                             std::cout << "Lyrics: " << ReadString(nLength) << std::endl;
  371.                             break;
  372.                         case MetaMarker:
  373.                             std::cout << "Marker: " << ReadString(nLength) << std::endl;
  374.                             break;
  375.                         case MetaCuePoint:
  376.                             std::cout << "Cue: " << ReadString(nLength) << std::endl;
  377.                             break;
  378.                         case MetaChannelPrefix:
  379.                             std::cout << "Prefix: " << ifs.get() << std::endl;
  380.                             break;
  381.                         case MetaEndOfTrack:
  382.                             bEndOfTrack = true;
  383.                             break;
  384.                         case MetaSetTempo:
  385.                             // Tempo is in microseconds per quarter note   
  386.                             if (m_nTempo == 0)
  387.                             {
  388.                                 (m_nTempo |= (ifs.get() << 16));
  389.                                 (m_nTempo |= (ifs.get() << 8));
  390.                                 (m_nTempo |= (ifs.get() << 0));
  391.                                 m_nBPM = (60000000 / m_nTempo);
  392.                                 std::cout << "Tempo: " << m_nTempo << " (" << m_nBPM << "bpm)" << std::endl;
  393.                             }
  394.                             break;
  395.                         case MetaSMPTEOffset:
  396.                             std::cout << "SMPTE: H:" << ifs.get() << " M:" << ifs.get() << " S:" << ifs.get() << " FR:" << ifs.get() << " FF:" << ifs.get() << std::endl;
  397.                             break;
  398.                         case MetaTimeSignature:
  399.                             std::cout << "Time Signature: " << ifs.get() << "/" << (2 << ifs.get()) << std::endl;
  400.                             std::cout << "ClocksPerTick: " << ifs.get() << std::endl;
  401.  
  402.                             // A MIDI "Beat" is 24 ticks, so specify how many 32nd notes constitute a beat
  403.                             std::cout << "32per24Clocks: " << ifs.get() << std::endl;
  404.                             break;
  405.                         case MetaKeySignature:
  406.                             std::cout << "Key Signature: " << ifs.get() << std::endl;
  407.                             std::cout << "Minor Key: " << ifs.get() << std::endl;
  408.                             break;
  409.                         case MetaSequencerSpecific:
  410.                             std::cout << "Sequencer Specific: " << ReadString(nLength) << std::endl;
  411.                             break;
  412.                         default:
  413.                             std::cout << "Unrecognised MetaEvent: " << nType << std::endl;
  414.                         }
  415.                     }
  416.  
  417.                     if (nStatus == 0xF0)
  418.                     {
  419.                         // System Exclusive Message Begin
  420.                         std::cout << "System Exclusive Begin: " << ReadString(ReadValue())  << std::endl;
  421.                     }
  422.  
  423.                     if (nStatus == 0xF7)
  424.                     {
  425.                         // System Exclusive Message Begin
  426.                         std::cout << "System Exclusive End: " << ReadString(ReadValue()) << std::endl;
  427.                     }
  428.                 }          
  429.                 else
  430.                 {
  431.                     std::cout << "Unrecognised Status Byte: " << nStatus << std::endl;
  432.                 }
  433.             }
  434.         }
  435.  
  436.  
  437.         // Convert Time Events to Notes
  438.         for (auto& track : vecTracks)
  439.         {
  440.             uint32_t nWallTime = 0;
  441.  
  442.             std::list<MidiNote> listNotesBeingProcessed;
  443.  
  444.             for (auto& event : track.vecEvents)
  445.             {
  446.                 nWallTime += event.nDeltaTick;
  447.  
  448.                 if (event.event == MidiEvent::Type::NoteOn)
  449.                 {
  450.                     // New Note
  451.                     listNotesBeingProcessed.push_back({ event.nKey, event.nVelocity, nWallTime, 0 });
  452.                 }
  453.  
  454.                 if (event.event == MidiEvent::Type::NoteOff)
  455.                 {
  456.                     auto note = std::find_if(listNotesBeingProcessed.begin(), listNotesBeingProcessed.end(), [&](const MidiNote& n) { return n.nKey == event.nKey; });
  457.                     if (note != listNotesBeingProcessed.end())
  458.                     {
  459.                         note->nDuration = nWallTime - note->nStartTime;
  460.                         track.vecNotes.push_back(*note);
  461.                         track.nMinNote = std::min(track.nMinNote, note->nKey);
  462.                         track.nMaxNote = std::max(track.nMaxNote, note->nKey);
  463.                         listNotesBeingProcessed.erase(note);
  464.                     }
  465.                 }
  466.             }
  467.         }
  468.  
  469.         return true;
  470.     }
  471.  
  472. public:
  473.     std::vector<MidiTrack> vecTracks;
  474.     uint32_t m_nTempo = 0;
  475.     uint32_t m_nBPM = 0;
  476.  
  477. };
  478.  
  479.  
  480. class olcMIDIViewer : public olc::PixelGameEngine
  481. {
  482. public:
  483.     olcMIDIViewer()
  484.     {
  485.         sAppName = "MIDI File Viewer";
  486.     }
  487.  
  488.  
  489.     MidiFile midi;
  490.  
  491.     //HMIDIOUT hInstrument;
  492.     size_t nCurrentNote[16]{ 0 };
  493.  
  494.     double dSongTime = 0.0;
  495.     double dRunTime = 0.0;
  496.     uint32_t nMidiClock = 0;
  497.    
  498.  
  499. public:
  500.     bool OnUserCreate() override
  501.     {
  502.  
  503.         midi.ParseFile("ff7_battle.mid");
  504.  
  505.         /*
  506.         int nMidiDevices = midiOutGetNumDevs();
  507.         if (nMidiDevices > 0)
  508.         {
  509.             if (midiOutOpen(&hInstrument, 2, NULL, 0, NULL) == MMSYSERR_NOERROR)
  510.             {
  511.                 std::cout << "Opened midi" << std::endl;
  512.             }
  513.         }
  514.         */
  515.  
  516.  
  517.         return true;
  518.     }
  519.  
  520.     float nTrackOffset = 1000;
  521.  
  522.     bool OnUserUpdate(float fElapsedTime) override
  523.     {
  524.         Clear(olc::BLACK);
  525.         uint32_t nTimePerColumn = 50;
  526.         uint32_t nNoteHeight = 2;
  527.         uint32_t nOffsetY = 0;
  528.        
  529.         if (GetKey(olc::Key::LEFT).bHeld) nTrackOffset -= 10000.0f * fElapsedTime;
  530.         if (GetKey(olc::Key::RIGHT).bHeld) nTrackOffset += 10000.0f * fElapsedTime;
  531.  
  532.  
  533.         for (auto& track : midi.vecTracks)
  534.         {
  535.             if (!track.vecNotes.empty())
  536.             {
  537.                 uint32_t nNoteRange = track.nMaxNote - track.nMinNote;
  538.  
  539.                 FillRect(0, nOffsetY, ScreenWidth(), (nNoteRange + 1) * nNoteHeight, olc::DARK_GREY);
  540.                 DrawString(1, nOffsetY + 1, track.sName);
  541.  
  542.                 for (auto& note : track.vecNotes)
  543.                 {
  544.                     FillRect((note.nStartTime - nTrackOffset) / nTimePerColumn, (nNoteRange - (note.nKey - track.nMinNote)) * nNoteHeight + nOffsetY, note.nDuration / nTimePerColumn, nNoteHeight, olc::WHITE);
  545.                 }
  546.                  
  547.                 nOffsetY += (nNoteRange + 1) * nNoteHeight + 4;
  548.             }
  549.         }
  550.  
  551.         // BELOW - ABSOLUTELY HORRIBLE BODGE TO PLAY SOUND
  552.         // DO NOT USE THIS CODE...
  553.        
  554.         /*
  555.         dRunTime += fElapsedTime;
  556.         uint32_t nTempo = 4;
  557.         int nTrack = 1;
  558.         while (dRunTime >= 1.0 / double(midi.m_nBPM * 8))
  559.         {
  560.             dRunTime -= 1.0 / double(midi.m_nBPM * 8);
  561.             // Single MIDI Clock
  562.             nMidiClock++;
  563.             int i = 0;
  564.             int nTrack = 1;
  565.             //for (nTrack = 1; nTrack < 3; nTrack++)
  566.             {
  567.                 if (nCurrentNote[nTrack] < midi.vecTracks[nTrack].vecEvents.size())
  568.                 {
  569.                     if (midi.vecTracks[nTrack].vecEvents[nCurrentNote[nTrack]].nDeltaTick == 0)
  570.                     {
  571.                         uint32_t nStatus = 0;
  572.                         uint32_t nNote = midi.vecTracks[nTrack].vecEvents[nCurrentNote[nTrack]].nKey;
  573.                         uint32_t nVelocity = midi.vecTracks[nTrack].vecEvents[nCurrentNote[nTrack]].nVelocity;
  574.                         if (midi.vecTracks[nTrack].vecEvents[nCurrentNote[nTrack]].event == MidiEvent::Type::NoteOn)
  575.                             nStatus = 0x90;
  576.                         else
  577.                             nStatus = 0x80;
  578.                         midiOutShortMsg(hInstrument, (nVelocity << 16) | (nNote << 8) | nStatus);
  579.                         nCurrentNote[nTrack]++;
  580.                     }
  581.                     else
  582.                         midi.vecTracks[nTrack].vecEvents[nCurrentNote[nTrack]].nDeltaTick--;
  583.                 }
  584.             }
  585.         }
  586.         if (GetKey(olc::Key::SPACE).bPressed)
  587.         {
  588.             midiOutShortMsg(hInstrument, 0x00403C90);
  589.         }
  590.         if (GetKey(olc::Key::SPACE).bReleased)
  591.         {
  592.             midiOutShortMsg(hInstrument, 0x00003C80);
  593.         }
  594.         */
  595.  
  596.  
  597.         return true;
  598.     }
  599.  
  600.    
  601. };
  602.  
  603. int main()
  604. {
  605.     olcMIDIViewer demo;
  606.     if (demo.Construct(1280, 960, 1, 1))
  607.         demo.Start();
  608.     return 0;
  609. }
Tags: midi
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement