{"id":13800935,"url":"https://github.com/bixb922/umidiparser","last_synced_at":"2025-05-13T10:30:39.355Z","repository":{"id":58021075,"uuid":"527786658","full_name":"bixb922/umidiparser","owner":"bixb922","description":"MIDI file parser for Micropython, CircuitPython and Python","archived":false,"fork":false,"pushed_at":"2025-03-26T19:14:21.000Z","size":2726,"stargazers_count":36,"open_issues_count":3,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-28T14:07:22.492Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bixb922.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-23T01:16:28.000Z","updated_at":"2025-04-23T03:55:52.000Z","dependencies_parsed_at":"2024-03-06T15:31:09.957Z","dependency_job_id":"3c68133f-04db-4a83-a5fe-007da27dd824","html_url":"https://github.com/bixb922/umidiparser","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bixb922%2Fumidiparser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bixb922%2Fumidiparser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bixb922%2Fumidiparser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bixb922%2Fumidiparser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bixb922","download_url":"https://codeload.github.com/bixb922/umidiparser/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253922790,"owners_count":21984780,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-04T00:01:17.797Z","updated_at":"2025-05-13T10:30:39.341Z","avatar_url":"https://github.com/bixb922.png","language":"Python","funding_links":[],"categories":["Libraries"],"sub_categories":["Audio"],"readme":"# umidiparser - low footprint MIDI file parser for Micropython, CircuitPython and Python\n\n# Contents\n1.  [Overview](#1-overview)\n     * [DESCRIPTION](#description)\n     * [INSTALLATION](#installation)\n     * [NEW: MIDI OUT OVER SERIAL](#new-midi-out-over-serial)\n     * [MIDI FILE COMPATIBILITY](#midi-file-compatibility)\n     * [CPU AND MEMORY (RAM) USAGE](#cpu-and-memory-ram-usage)\n     * [PORTABILITY](#portability)\n     * [CLASSES](#classes)\n     * [DEPENDENCIES](#dependencies)\n2.  [Class MidiFile](#2-class-midifile)\n     * [MidiFile constructor parameters](#midifile-constructor-parameters)\n         * [filename](#filename)\n         * [buffer_size=100](#buffer-size100)\n     * [MidiFile Methods](#midifile-methods)\n         * [Iterating through the events of a MIDI file](#iterating-through-the-events-of-a-midi-file)\n         * [play](#play)\n         * [length_us](#length-us)\n     * [MidiFile properties](#midifile-properties)\n         * [buffer_size](#buffer-size)\n         * [filename](#filename)\n         * [format_type](#format-type)\n         * [miditicks_per_quarter](#miditicks-per-quarter)\n         * [reuse_event_object](#reuse-event-object)\n     * [MidiFile instance variables](#midifile-instance-variables)\n         * [tracks](#tracks)\n3.  [Class MidiTrack](#3-class-miditrack)\n     * [Methods](#methods)\n4.  [Class MidiEvent](#4-class-midievent)\n     * [Properties available for all events](#properties-available-for-all-events)\n         * [status](#status)\n         * [delta_us](#delta-us)\n         * [delta_miditicks](#delta-miditicks)\n         * [data](#data)\n     * [Event specific properties](#event-specific-properties)\n     * [MIDI channel event specific properties](#midi-channel-event-specific-properties)\n     * [Meta and Sysex event specific properties](#meta-and-sysex-event-specific-properties)\n     * [MidiEvent methods](#midievent-methods)\n         * [copy()](#copy)\n         * [is_channel()](#is-channel)\n         * [is_meta()](#is-meta)\n         * [str(event)](#str-event)\n     * [MidiEvent properties](#midievent-properties)\n         * [channel](#channel)\n         * [clocks_per_click](#clocks-per-click)\n         * [control](#control)\n         * [data](#data)\n         * [denominator](#denominator)\n         * [frame_rate](#frame-rate)\n         * [frames](#frames)\n         * [hours](#hours)\n         * [key](#key)\n         * [minutes](#minutes)\n         * [name](#name)\n         * [notated_32nd_notes_per_beat](#notated-32nd-notes-per-beat)\n         * [note](#note)\n         * [number](#number)\n         * [numerator](#numerator)\n         * [pitch](#pitch)\n         * [port](#port)\n         * [program](#program)\n         * [seconds](#seconds)\n         * [sub_frames](#sub-frames)\n         * [tempo](#tempo)\n         * [text](#text)\n         * [to_midi](#to-midi)\n         * [value](#value)\n         * [velocity](#velocity)\n     * [MIDI event values as text: gmfunctions.py](#midi-event-values-as-text-gmfunctionspy)\n     * [Change log for version 1.1 and 1.2](#change-log-for-version-11-and-12)\n     * [Change log for version 1.3](#change-log-for-version-13)\n     * [After version 1.3](#after-version-13)\n     * [AUTHOR](#author)\n     * [LICENSE](#license)\n# 1. Overview\n\n## DESCRIPTION\nThis module reads MIDI files (SMF files) and gets all the MIDI events contained in the file. It also can return each event at the corresponding time.\n\nExample:\n\n```py\n    import umidiparser\n    import utime\n    for event in umidiparser.MidiFile(\"example.mid\").play():\n        # .play will sleep, avoiding time drift, before returning the event on time\n        # Process the event according to type\n        if event.status == umidiparser.NOTE_ON:\n            ... start the note with midi number event.note \n\t\ton channel event.channel with event.velocity\n        elif event.status == umidiparser.NOTE_OFF :\n            ... stop the note event.note .\n        elif event.status == umidiparser.PROGRAM_CHANGE:\n            ... change midi program to event.program on event.channel ....\n        else:\n           # Show all events not processed\n           print(\"other event\", event )\n```\n\nThis module does not contain a sound synthesizer, only the capabilities to\nread and interpret a MIDI file.\n\nMemory and CPU usage are optimized for a microcontroller with limited resources with Micropython or CircuitPython. On CPython this module is very fast.\n\nCPU and memory usage can be lowered even more by reusing the same MidiEvent over and over\nduring the process:\n```py\n    for event in MidiFile(\"example.mid\", reuse_event_object=True ):\n        ... process event....\n```\nNormally, only a small portion of the file will be buffered in memory. The default is 100 bytes per track.\nThis allows to process a files\nmuch larger than the available heap or RAM. See \"CPU AND MEMORY\" below.\n\nIf there is enough RAM available to buffer the complete file, you can use `for event in MidiFile(\"example.mid\", buffer_size=0)`. This will load the complete file in RAM and uses a bit less of CPU for the parsing of each event.\n\nThis module will calculate the time between events, using the parameters and events available in the MIDI file (pulses per beat field in the file header and set tempo events).\n\n## INSTALLATION\nCPython (regular Python): ```pip install umidiparser```\n\nMicropython: Install directly to your microcontroller with ```mpremote mip install github:bixb922/umidiparser```\n\nCircuitPython: get umidiparser.py and copy it to the /lib folder of the microcontroller.\n\n*Important notice for CircuitPython*\n\nOn many boards, the @micropython.native decorator is disabled, and you will get the following error message: ```SyntaxError: invalid micropython decorator```. If that is the case, please edit your copy of umidiparser.py and comment or delete all ```@micropython.native``` statements. This decorator speeds up execution, but there is no functional difference without this decorator. ```umidiparser``` is quite fast without the decorator. \n\n*Important notice for MicroPython*\n\nIf you use ```mpremote mount``` and the MIDI files are accessed on the PC, you will get the following error message: ```AttributeError: 'RemoteFile' object has no attribute 'tell'```. You have two options:\n* You can open the MIDI file with ```umidiparser.MidiFile(\"mymidi.mid\", buffer_size=0)```. This will avoid tell/seek operations, but the file will be read entirely to memory.\n* Copy the MIDI files to flash, and read them from flash. While ```mpremote mount``` is active, the PC's file system is at ```/remote``` and this is the default directory, but the flash is still avaiable at ```/```, so opening files with a relative path will open files on the PC, while opening files with an absolute path (starting with ```/```) will read files on flash.\n\n## NEW: MIDI OUT OVER SERIAL\n\nNewly added: here is a description on how to play MIDI files to a sound module over a MIDI serial (5 pin DIN) connection: [MIDI over serial output with ESP32 and MicroPython](midi_over_serial/midi_over_serial.md)\n\n## MIDI FILE COMPATIBILITY\nThe parser will parse MIDI files, also known as SMF files, with format type 0, 1 and 2. \nRunning status events are\nrecognized. All standard MIDI channel events (note on, note off,\nprogram change, control change, etc) and meta and sysex events (track name,\nkey signature, time signature, etc) recognized. This module is based on the MIDI file standard version 1.1\n\nFor multitrack file type 1 files, tracks are automatically\nmerged during playback.\n\nMIDI file format errors such as running status event at the beginning of\na track or incorrect file header fields are detected insofar they could\nlead to wrong results. Non standard data chunks are ignored. Missing\nend of track events are inserted on the fly.\nRunning status events may have a meta or sysex event in between, as this\nis non standard but common in MIDI files.\nNon standard or custom meta messages are recognized. Values of fields out\nof range, such as note values between 128 and 255 are passed to the calling\nprogram.\n\nThere are no capabilities to modify or create MIDI files.\n\n## CPU AND MEMORY (RAM) USAGE\nParsing on a ESP32 at 200 Mhz CPU clock with Micropython takes about\none millisecond per event, and even less if you use the reuse_event_object\nof the MidiFile object. Times with CircuitPython on a RP2040 are similar.\n\nWith the reuse_event_object parameter set to true, CPU overhead should be\nabout 15% lower, and less heap is used.\n\nOn the ESP32 with MicroPython, parsing a MIDI file with buffer_size=100 and\nreuse_event_object=True uses about 20kbytes of heap. Heap usage on a RP2040 (Raspberry Pi Pico) is similar. Each MIDI event parsed will\nallocate and free less than 100 bytes of heap, so garbage collections are less\nfrequent if little RAM is available.\nMost of the processing such as parsing event data into individual fields\nis delayed until the information is really needed or asked for.\n\nFrequently used methods have the @micropython.native decorator for speed.\n\n## PORTABILITY\nThe code is optimized for Micropython or CircuitPython.\nIt also works with same functionality under regular Python (CPython).\nThis allows to develop or test on Linux, Mac or Windows and then port to\nMicropython. \n\n## CLASSES\nThe starting point is the MidiFile object, allowing to iterate through\nthe events. Events are returned as MidiEvent objects, and the fields\nof the event are visible as attributes of the MidiEvent.\n\n## DEPENDENCIES\nImports *time* to compute time differences. MidiFile.play() will use the time.sleep or time.sleep_us function.\n\nImports *asyncio* for async version of MidiFile.play() \n\nImports *sys* to get sys.name.implementation.\n\n\n# 2. Class MidiFile\n\n```py\nmf = MidiFile( filename, buffer_size=100, reuse_event_object=false)`\n```\nParses a MIDI file, returning an iterator over the MIDI events in the file. You then can iterate through the events of the file, see the iterator and the *play* method.\n\nIt is also possible to get the tracks using the mf.track[] list and iterate through the events of one track only, useful for format type 2 files.\n\n## MidiFile constructor parameters\n\n### filename\n\nThe name of a MIDI file, usually a .mid, .kar or .rtx MIDI file.\nThe MIDI file will always be opened read only.\n\n### buffer_size=100\n\nThe buffer size that will be allocated for each track. a value of zero means copy all tracks to RAM during processing.The default value is 100 bytes.\n\nIn order to process files larger than the available RAM,\n*buffer_size=n* will allocate *n* bytes of buffer for\neach track, and read each tracks in *n* byte portions during the\nprocessing of the file, i.e. while iterating through the events. One file descriptor (one open file) will be used for each track, but the total RAM needed will be about *buffer_size* multiplied by the number of tracks of the MIDI file.\n\nMIDI file format 0 files have only one track, so only one buffer will be allocated. \n\nA buffer size of less than 10 bytes will increase CPU overhead. A buffer size much larger than 100 will probably not give a\nrelevant performance advantage, unless the device where the file\nresides is very slow.\n\nIf *buffer_size=0^, all tracks will be read to memory.\nThis will need as much RAM as the file size of a complete MIDI file.\nIn this case, the time needed to process each event will not depend on\nfile access and will be both faster and more dependable than using a\n*buffer_size* different to 0, if the device where the file resides is slow.\n\nThe default value for *buffer_size* is 100 bytes.\n\n* reuse_event_object=False\n\nWith the default value of False, for each step of the iteration a new MidiEvent is returned.\nIf you do:\n```py\n    for event in MidiFile(\"example.mid\"):\n        ... process each event ...\n```\nthen each iteration of the loop will yield a new MidiEvent object.\nThis is the normal and expected behavior for Python iterators.\n\nHowever, if you if you have little RAM or need higher processing speed use:\n```py\n    for event in MidiFile(\"example.mid\", reuse_event_object=True):\n         ... process each event ....\n```\nIn this case, for each iteration of the loop, the same MidiEvent object is returned over over and over \nfor each step of the loop, but overwritten with the new data. However, if you want to store an event\nfor later use,  you'll have to make a deep copy using `event.copy`.\n\nAll combinations of *reuse_event_object* and *buffer_size* are valid.\n\nExceptions raised:\n\nThe file must start with a standard MIDI file header (MThd), if not, a\nValueError is raised.\nThe MIDI header chunk must be at least 6 bytes long, or a ValueError\nis raised.\nValueError is raised if no header present, or too short.\nValueError is raised if the header contains SMPTE time codes (not supported).\nChunks after the header that are not tracks identified with the MTrk header are ignored.\n\n\n## MidiFile Methods\n\n### Iterating through the events of a MIDI file\n\nTo get all the events of a format type 0 or format type 1 MIDI file,\niterate through the MidiFile object, for example:\n```py    \n    import umidiparser\n    for event in MidiFile(\"example.mid\"):\n        print(event)\n```\n\nEvents will be returned in ascending order in time. For format type 1\nmultitrack files, all tracks are merged.\n\nThe *event.delta_us* property is calculated as the\ntime in microseconds between last and this event, taking into account both\nthe set tempo meta events and the \"MIDI ticks per quarter note\" (also known as \"pulses per beat\")\nparameter in the MIDI file header. For type 1 files, all set tempo meta\nevents may be in track 0 (as it is usually done), or they may occur in any track.\n\nFor a multitrack file, *event.delta_us^ is the time difference with the\nprevious event, which may be in the same or a different track.\n\nTo get each event at the proper time of the event, use the \n*MidiFile.play* method. This method compensates for the delays during processing.\n\nIn all cases, only one end of track meta event is returned\nto the caller when the end of file is reached.\nEvents beyond an end of track event are ignored.\n\nExceptions:\n\nA *RuntimeError* is raised if format type 2 is processed with this method.\n\n\n### play\n\nIterate through the events of a MIDI file or a track,\nsleep until the event has to take place, and\nyield the event.\n\nExample:\n```py\n    for event in MidiFile(\"example.mid\").play():\n         .... process the event ...\n```\nThe play function will wait the necessary time between iterations\nso that each event is yielded on time to be processed.\n\nThis function compensates for the accumulated error in the\nprocessing of each event. Since sleep functions will sleep\nAT LEAST the time specified, normally the time slept will be longer. This\nmeans, for a long file of several thousand events, events may get ever later.\nThis is noticeable especially \nif the processing of each event takes significant time.\nEven so, since play uses the sleep_us function, sometimes\nyou may get the event a bit later than the correct time.\n\nFor Micropython, *time.sleep_us()* is used. For CircuitPython and CPython *time.sleep()* is used. \n\nThere is also a asyncio version of MidiFile.play() available:\n```py\n    async for event in MidiFile(\"example.mid\").play():\n        ... process the event ...\n```\nThis will work exactly like the normal for, except it will pause for the next event with *await*, yielding control back to the asyncio loop. \n\n### length_us\n\nComputes and returns the length of the MidiFile in microseconds.\n\nBe aware that on a microcontroller, calculating the length of\na large MIDI file might take a several seconds.\nThis is due to the way MIDI files work. In order to\nget the playing length, this method needs to parse\nthe entire file, compute and add the time differences of all events.\n\nExceptions:\n\n*RuntimeError* for format type 2 files. It is not possible to calculate the\nplaying time of a format 2 file, since for format 2 files,\nthe tracks are not meant to be merged.\n\n       \n\n## MidiFile properties\n\nThe properties cannot be changed, they are set with the MidiFile constructor.\n\n### buffer_size\n\nReturns the *buffer_size value*. \n        \n### filename\n\nReturns the file name of the MIDI file. \n        \n### format_type\n\nReturns the MIDI format type as stored in the header of the MIDI file:\n\n* format_type 0 files are single track MIDI files.\nTo parse a type 0 or type 1 file, use the MidiFile object as iterator. If the file is multitrack, this module will merge all tracks, and yield events in ascending time order:\n```py\n    for event in MidiFile(\"example.mid\"):\n        ...process each event\n```\n\n* format_type 2 files are multitrack files where each track behaves like a format 0 single track MIDI file. Merging\ntracks is not allowed. Track number \"n\" can be  parsed as follows:\n```py\n    for event in MidiFile(\"format2file.mid\").tracks[n]:\n         .... process event...\n```\nOr also:\n```py\n    For event in MidiFile(\"format2file.mid\").tracks[n].play():\n         .... process event...\n```\nIn fact, you could use the same methods on type 0 and type 1 files to access each track separately.\n\n        \n### miditicks_per_quarter\n\nReturn the MIDI ticks per quarter note (also called pulses per beat) field of the MIDI file header.\n        \n        \n### reuse_event_object\n\nReturn the value of the reuse_event_object property.\n\n## MidiFile instance variables\n### tracks\nList of MidiTrack objects, one for each track.\n\n# 3. Class MidiTrack\nThe MidiFile object exposes the list of tracks.\n\n## Methods\n\n\n# 4. Class MidiEvent\n\nRepresents a parsed MIDI event. You get MidiEvent objects iterating through a MidiFile.\n\n## Properties available for all events\n\n### status\n\nThis is the event type, such as note on, note off,\nset tempo, end of track, sysex. You can compare event.status\nwith the constants umidiparser.NOTE_ON, umidiparser.NOTE_OFF,\numidiparser.PROGRAM_CHANGE, umidiparser.LYRICS, umidiparser.SYSEX, etc.\n\nAvailable for all events types. \n\nExample:\n```py\n    if event.status == umidiparser.NOTE_OFF:\n        ... process note off event ...\n    elif event.status == umidiparser.KEY_SIGNATURE:\n        ... process key signature meta event ...\n```\nPossible values are constants in umidiparser:\n* Channel messages\n- NOTE_OFF\n- NOTE_ON\n- POLYTOUCH\n- CONTROL_CHANGE\n- PROGRAM_CHANGE\n- AFTERTOUCH\n- PITCHWHEEL\n\n* Meta messages \n- SEQUENCE_NUMBER\n- TEXT\n- COPYRIGHT\n- TRACK_NAME\n- INSTRUMENT_NAME\n- LYRICS\n- MARKER\n- CUE_MARKER\n- PROGRAM_NAME\n- DEVICE_NAME\n- CHANNEL_PREFIX\n- MIDI_PORT\n- END_OF_TRACK\n- SET_TEMPO\n- SMPTE_OFFSET\n- TIME_SIGNATURE\n- KEY_SIGNATURE\n- SEQUENCER_SPECIFIC\n\n * Sysex/escape events\n - SYSEX\n - ESCAPE\n\n\n\n### delta_us\n\nTime in microseconds since the last event for this\nevent to start. delta_us is calculated using the delta in MIDI ticks, the pulses per quarter in the MIDI file header and the set tempo events in the file, using integer arithmetic to avoid floating point use.\n\n### delta_miditicks\n\nDifference, in MIDI ticks (MIDI pulses) between last event\nand this event. This number is also known as \"delta time\" of the event.\nFor single track files, the time difference is with the previous event\nof the same track. When parsing multitrack files, tracks are merged and this\ntime is set during playback to the time difference with the previous event, which may or may not be in the same track.\n\n### data\nContains the raw event data.\n\n\n## Event specific properties\n\nFor each type of event, only the applicable properties are enabled.\nThe following list shows the event status and properties\nthat you can get.\n\nFor example: for a note_on event, event.status is umidiparser.NOTE_ON\nand the properties event.note, event.channel, and event.velocity return the \nvalues of the event.\n\nAttributes that are not available throw an AttributeError on access.\n\n## MIDI channel event specific properties\n\n* NOTE_OFF (midi event status byte=0x80 to 0x8f)\n    - channel (int)\n    - note (int)\n    - velocity (int)\n* NOTE_ON (midi event status byte=0x90 to 0x9f)\n    - channel (int)\n    - note (int)\n    - velocity (int)\n* POLYTOUCH (midi event status byte=0xa0 to 0xaf)\n    - channel (int)\n    - note (int)\n    - value (int)\n* CONTROL_CHANGE (midi event status byte=0xb0 to 0xbf)\n    - channel (int)\n    - control (int)\n    - value (int)\n* PROGRAM_CHANGE (midi event status byte=0xc0 to 0xcf)\n    - channel (int)\n    - program (int)\n* AFTERTOUCH (midi event status byte=0xd0 to 0xdf)\n    - channel (int)\n    - value (int)\n* PITCHWHEEL (midi event status byte=0xe0 to 0xef)\n    - channel (int)\n    - pitch (int)\n\n## Meta and Sysex event specific properties\n* SEQUENCE_NUMBER (midi meta 0xff 0x00)\n    - number (int)\n* TEXT (midi meta 0xff 0x01)\n    - text (str)\n* COPYRIGHT (midi meta 0xff 0x02)\n    - text (str)\n* TRACK_NAME (midi meta 0xff 0x03)\n    - name (str)\n* INSTRUMENT_NAME (midi meta 0xff 0x04)\n    - name (str)\n* LYRICS (midi meta 0xff 0x05)\n    - text (str)\n* MARKER (midi meta 0xff 0x06)\n    - text (str)\n* CUE_MARKER (midi meta 0xff 0x07)\n    - text (str)\n* PROGRAM_NAME (midi meta 0xff 0x08)\n    - name (str)\n* DEVICE_NAME (midi meta 0xff 0x09)\n    - name (str)\n* CHANNEL_PREFIX (midi meta 0xff 0x20)\n    - channel (int)\n* MIDI_PORT (midi meta 0xff 0x21)\n    - port (int)\n* END_OF_TRACK (midi meta 0xff 0x2f)\n    - no event specific attributes available\n* SET_TEMPO (midi meta 0xff 0x51)\n    - tempo (int)\n* SMPTE_OFFSET (midi meta 0xff 0x54)\n    - frame_rate (int)\n    - frames (int)\n    - hours (int)\n    - minutes (int)\n    - seconds (int)\n    - sub_frames (int)\n* TIME_SIGNATURE (midi meta 0xff 0x58)\n    - clocks_per_click (int)\n    - denominator (int)\n    - notated_32nd_notes_per_beat (int)\n    - numerator (int)\n* KEY_SIGNATURE (midi meta 0xff 0x59)\n    - key (str)\n* SEQUENCER_SPECIFIC (midi meta 0xff 0x7f)\n    - data (memoryview)\n\nText strings are decoded as ASCII. Non ASCII characters are shown in hexa i.e. \\x80.\n\n* SYSEX  0xf0\n    - data (memoryview)\n* ESCAPE  0xf7\n    - data (memoryview)\n\n## MidiEvent methods\n\n### copy()\n\nReturns a deep copy (a complete independent copy) of the event as a new object.\n\nThis can be useful to get a copy of the event in case of using\nthe reuse_event_object=True option in the MidiFile.\n\nExample 1: if you need a list of all MidiEvents\nin a file, then either use:\n```py\n    event_list = [event.copy for event in MidiFile(\"example.mid\",\n        reuse_event_object=True) ]\n```\nor use:\n```py\n    event_list = [ event for event in MidiFile(\"example.mid\") ]\n```        \n### is_channel()\n\nReturns True if this is a channel event such as note on, program change\nor pitchwheel. \n\nReturns False for meta events, Sysex or Escape events.\n\n### is_meta()\n\nReturns True if this is a Meta event, such as\nlyrics, set tempo or key signature.\n\nReturns False if this is a MIDI channel event,\nor a Sysex or Escape event.\n\n### str(event)\nWill translate the event information to a string, for example:\n```py\n    print(event)    \n    event_as_a_string = str(event)\n```\nReturns a string with a description of the MidiEvent, starting\nwith the event name in lowercase (note_on, note_off, pitchwheel, set_tempo,\nend_of_track, etc), delta time in MIDI ticks, delta time in microseconds,\nfirst few bytes of the raw data for meta events, and all properties\nthat are allowed for the event type. For example, a \"note on\" event might\nbe shown as:\n\n\"note_on delta[miditicks]=10 delta[usec]=9500 note=60 velocity=64 channel=5\"\n\nThe order of the properties in the string may vary. \n\n\n## MidiEvent properties\n### channel\n\nReturns the channel number for the event, 0-15.\n\nchannel property is available for:  NOTE_OFF NOTE_ON\nPOLYTOUCH CONTROL_CHANGE PROGRAM_CHANGE AFTERTOUCH\nCHANNEL_PREFIX\n        \n### clocks_per_click\n\nReturns the clocks_per_click for the TIME_SIGNATURE meta messages, 0-255.\n        \n### control\n\nReturns the value for the controller 0-127 for a CONTROL_CHANGE event.\n        \n### data\n\nReturns the raw data for the underlying message, with no transofrmations,\nas a memoryview, without the event status byte or meta prefix.\n\nFor midi channel events, the length is either 1 or 2 bytes\naccording to the event type, for example a \"note on\" event always\nhas 2 bytes of data.\nFor a meta or sysex event, \"data\" contains the payload of the message,\nthat is, without meta prefix and length.\nFor sysex and escape events, the status (0xf0, xf7) is not included.\n\nThe main purpose is to retrieve message data for sysex and escape events. Also, you can decode text and name fields, such as lyrics, with a encoding better suited for the file, if available.\n        \n### denominator\n\nReturns the denominator for the TIME_SIGNATURE meta messages, 0-255.\n        \n### frame_rate\n\nReturns the frame for the SMPTE_OFFSET meta messages,\nwhich can be 24, 25, 29.97 or 30.\n\nAn invalid value in the MIDI file will raise a *IndexError*\n        \n### frames\n\nReturns the frames for the SMPTE_OFFSET meta message,\nusually from 0 to 255.\n        \n### hours\n\nReturns the hour for the SMPTE_OFFSET meta message,\nusually from 0 to 23.\n        \n### key\n\nReturns the key, as string, for a KEY_SIGNATURE meta event.\n  \nFor mayor keys:\nC, D, E, F, G, A, B, C#, F#, Cb, Db, Eb, Gb, Ab\n\nFor minor keys:\nCm, Dm, Em, Fm, Gm, Am, Bm, C#m, F#m, Cbm, Dbm, Ebm, Gbm, Abm\n\nIf the midi message contains a value out of range, a *ValueError*\nis raised. The raw data can be accessed with the data property.\n        \n### minutes\n\nReturns the minutes for the SMPTE_OFFSET meta message,\nusually from 0 to 59.\n        \n### name\n\nReturns the *name* data field for certain meta events.\n\nname property is available for:  TRACK_NAME INSTRUMENT_NAME PROGRAM_NAME DEVICE_NAME\n\nSee text property for description of text decoding.\n\nThe raw data can be retrieved using the data property.\n        \n### notated_32nd_notes_per_beat\n\nReturns the notated_32nd_notes_per_beat for the TIME_SIGNATURE meta messages,\n0-255.\n        \n### note\n\nReturns the note number for the event, usually 0-127.\n\nnote property available for:  NOTE_OFF NOTE_ON POLYTOUCH\n        \n### number\n\nReturns number of a SEQUENCE_NUMBER meta event.\nValues range from 0 to 2**24.\n        \n### numerator\n\nReturns the numerator for the TIME_SIGNATURE meta messages, 0-255.\n        \n### pitch\n\nReturns the pitch for a PITCHWHEEL midi channel event.\n\n-8192 is the lowest value possible, 0 (zero) means \"no pitch bend\"\nand 8191 is the highest value possible.\n        \n### port\n\nReturns the port number  0-256 for a meta MIDI_PORT message\n        \n### program\n\nReturns the program number 0-127 for a PROGRAM_CHANGE event.\n        \n### seconds\n\nReturns the seconds for the SMPTE_OFFSET meta message,\nusually from 0 to 59.        \n       \n### sub_frames\n\nReturns the sub frames for the SMPTE_OFFSET meta message,\nusually from 0 to 59.\n        \n### tempo\n\nReturns the tempo in microseconds per quarter beat\nfor a SET_TEMPO meta event.\nThis module interprets the tempo event before returning it, so\nthe following events returned will have their *delta_us* property\ncalculated with the new tempo value.\n\n### text\n\nReturns the text for a meta events.\n\ntext property is available for:  TEXT COPYRIGHT LYRICS MARKER CUE_MARKER\n\nBoth event.text and event.name decode the data with ASCII. Unmapped characters are shown in hexadecimal, for example \\x0d\n\nThere are many MIDI files where text and name fields are encoded \nwith other encodings. The encoding used is not stored in the file.\n         \n### to_midi\n\nReturns the event as bytes, in a format that allows sending the\ndata to a MIDI controller.\n\nto_midi will raise AttributeError if the event is for MIDI meta messages, these\noccur in MIDI files and are not normally sent to MIDI controllers.\n        \n### value\n\nReturns the the value in the event.\n\nvalue property available for:  AFTERTOUCH, CONTROL_CHANGE, POLYTOUCH\n        \n### velocity\n\nReturns the velocity for the event, usually 0-127.\n\nvelocity property available for:  NOTE_OFF NOTE_ON\n\n## MIDI event values as text: gmfunctions.py\nThis is a file with functions to convert Midi note numbers to note names in english notation (C, Db, D), doremi notation and german notation (C, Des, D). There are translations of program numbers, control changes and values according to the General Midi (GM) standard. There is also a function to convert midi note numbers to frequencies.\n\nGet file gmfunctions.py, this is not part of the umidiparser package. The file is rather large (7kb), you might want to edit and prune on your PC before loading in your microcontroller. Also, you might want to change the names of notes with accidentals (sharp vs. flat) in the code on your PC.\n\n## Change log for version 1.1 and 1.2\n\nAdded CircuitPython compatibility\nRemoved use of os.path.abspath, does not exist in micropython/circuitpython\nRenamed time functions \"ticks_now_us\", \"ticks_diff_us\" to \"time_now_us\", \"time_diff_us\"\nChanged decode from meta event data for compatibility with all Python versions, now only ascii is decoded.\nNew method MidiTrack.play plays one single track.\nCorrected possible error if playing open file again.\nAdded event.is_channel() to test for channel events.\nAllow MidiFile.play() used in async for (with asyncio.sleep instead of sleep). Requires asyncio.\nPlay funcion computes event.timestamp_us for each event\n\n## Change log for version 1.3\nAdded support for installing with mpremote mip (Micropython). Corrected package name in README.md. Added gmfunctions.py in github. Fixed asyncio bug and typo for CircuitPython. \n\n## After version 1.3\nUpdated readme with restrictions. Improved code formatting.\n\n\n## AUTHOR\nHermann Paul von Borries\n\n## LICENSE\nMIT, Copyright (c) 2022 Hermann Paul von Borries\n\n\n        \n\n\n        \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbixb922%2Fumidiparser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbixb922%2Fumidiparser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbixb922%2Fumidiparser/lists"}