{"id":13396580,"url":"https://github.com/adamnemecek/WebMIDIKit","last_synced_at":"2025-03-13T23:31:33.442Z","repository":{"id":16702703,"uuid":"80470879","full_name":"adamnemecek/WebMIDIKit","owner":"adamnemecek","description":"Simplest MIDI Swift library","archived":false,"fork":false,"pushed_at":"2023-06-05T12:26:57.000Z","size":259,"stargazers_count":144,"open_issues_count":7,"forks_count":27,"subscribers_count":13,"default_branch":"master","last_synced_at":"2024-04-08T08:56:08.715Z","etag":null,"topics":["audio","audiokit","coremidi","ios","mac","macos","midi","midi-api","midi-events","music","music-library","sound","swift","swift-framework","swift-library","tvos","web-midi","web-standards","webmidi"],"latest_commit_sha":null,"homepage":"","language":"Swift","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/adamnemecek.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}},"created_at":"2017-01-30T22:36:57.000Z","updated_at":"2024-03-11T11:55:51.000Z","dependencies_parsed_at":"2024-01-06T10:29:11.616Z","dependency_job_id":null,"html_url":"https://github.com/adamnemecek/WebMIDIKit","commit_stats":{"total_commits":232,"total_committers":5,"mean_commits":46.4,"dds":0.05603448275862066,"last_synced_commit":"6f3506937c141f04edaaa182502984d7baaa2e26"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adamnemecek%2FWebMIDIKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adamnemecek%2FWebMIDIKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adamnemecek%2FWebMIDIKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adamnemecek%2FWebMIDIKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adamnemecek","download_url":"https://codeload.github.com/adamnemecek/WebMIDIKit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221421568,"owners_count":16817835,"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":["audio","audiokit","coremidi","ios","mac","macos","midi","midi-api","midi-events","music","music-library","sound","swift","swift-framework","swift-library","tvos","web-midi","web-standards","webmidi"],"created_at":"2024-07-30T18:00:57.345Z","updated_at":"2024-10-25T11:30:55.008Z","avatar_url":"https://github.com/adamnemecek.png","language":"Swift","funding_links":[],"categories":["Audio","Media and Graphics"],"sub_categories":[],"readme":"# WebMIDIKit: Simplest Swift MIDI library\n\n###__[Want to learn audio synthesis, sound design and how to make cool sounds in an afternoon? Check out Syntorial!](http://www.syntorial.com/#a_aid=AudioKit)__\n\n## About\n\n### What's MIDI \n\n[MIDI](https://en.wikipedia.org/wiki/MIDI) is a standard governing music software and music device interconnectivity. It lets you make music by sending data between applications and devices.\n\n### What's WebMIDI\n\n[WebMIDI](https://webaudio.github.io/web-midi-api/) is a browser API standard that brings the MIDI technology to the web. WebMIDI is minimal, it only describes MIDI port selection, receiving data from input ports and sending data to output ports. [WebMIDI is currently implemented in Chrome \u0026 Opera](http://caniuse.com/#feat=midi). Note that WebMIDI is relatively low level as messages are still represented as sequences of UInt8s (bytes/octets).\n\n### What's WebMIDIKit\nWebMIDIKit is an implementation of the WebMIDI API for macOS/iOS. On these OS, the native framework for working with MIDI is [CoreMIDI](https://developer.apple.com/reference/coremidi).\nCoreMIDI is old and the API is entirely in C (💩). Using it involves a lot of void pointer casting (💩^9.329), and other unspeakable things. Furthermore, some of the APIs didn't quite survive the transition to Swift and are essentially unusable in Swift (`MIDIPacketList` APIs, I'm looking at you).\n\nCoreMIDI is also extremely verbose and error prone. Selecting an input port and receiving data from it is __~80 lines__ of [convoluted Swift code](http://mattg411.com/coremidi-swift-programming/). __WebMIDIKit let's you do the same thing in 1.__\n\nWebMIDIKit is a part of the [AudioKit](https://github.com/audiokit/audiokit) project and will eventually replace [AudioKit's MIDI implementation](https://github.com/audiokit/AudioKit/tree/master/AudioKit/Common/MIDI).\n\nAlso note that WebMIDIKit adds some APIs which aren't a part of the WebMIDI standard. These are marked as non-standard in the code base.\n\n## Usage\n\n### Installation\n\nUse Swift Package Manager. Add the following `.Package` entry into your dependencies.\n\n```swift\n.Package(url: \"https://github.com/adamnemecek/webmidikit\", from: \"1.0.0\")\n```\n\n### Check out the [sample project](https://github.com/adamnemecek/WebMIDIKitDemo).\n\n### Selecting an input port and receiving MIDI messages from it\n\n```swift\nimport WebMIDIKit\n\n/// represents the MIDI session\nlet midi: MIDIAccess = MIDIAccess()\n\n/// prints all MIDI inputs available to the console and asks the user which port they want to select\nlet inputPort: MIDIInput? = midi.inputs.prompt()\n\n/// Receiving MIDI events \n/// set the input port's onMIDIMessage callback which gets called when the port receives MIDI packets\ninputPort?.onMIDIMessage = { (list: UnsafePointer\u003cMIDIPacketList\u003e) in\n    for packet in list {\n        print(\"received \\(packet)\")\n    }\n}\n\n```\n\n\n### Selecting an output port and sending MIDI packets to it\n```swift\n/// select an output port\nlet outputPort: MIDIOutput? = midi.outputs.prompt()\n\n/// send messages to it\noutputPort.map {\n\n\t/// send a note on message\n\t/// the bytes are in the normal MIDI message format (https://www.midi.org/specifications/item/table-1-summary-of-midi-message)\n\t/// i.e. you have to send two events, a note on event and a note off event to play a single note\n\t/// the format is as follows:\n\t/// byte0 = message type (0x90 = note on, 0x80 = note off)\n\t/// byte1 = the note played (0x60 = C8, see http://www.midimountain.com/midi/midi_note_numbers.html)\n\t/// byte2 = velocity (how loud the note should be 127 (=0x7f) is max, 0 is min)\n\n\tlet noteOn: [UInt8] = [0x90, 0x60, 0x7f]\n\tlet noteOff: [UInt8] = [0x80, 0x60, 0]\n\n\t/// send the note on event\n\t$0.send(noteOn)\n\n\t/// send a note off message 1000 ms (1 second) later\n\t$0.send(noteOff, offset: 1000)\n\n\t/// in WebMIDIKit, you can also chain these\n\t$0.send(noteOn)\n\t  .send(noteOff, offset: 1000)\n}\n```\n\nIf the output port you want to select has a corresponding input port you can also do\n\n```swift\nlet outputPort: MIDIOutput? = midi.output(for: inputPort)\n```\n\nSimilarly, you can find an input port for the output port.\n\n```swift\nlet inputPort2: MIDIInput? = midi.input(for: outputPort)\n```\n\n### Looping over ports\n\nPort maps are dictionary like collections of `MIDIInputs` or `MIDIOutputs` that are indexed with the port's id. As a result, you cannot index into them like you would into an array (the reason for this being that the endpoints can be added and removed so you cannot reference them by their index).\n```swift\nfor (id, port) in midi.inputs {\n\tprint(id, port)\n}\n```\n\n### Creating virtual ports\n\nTo create virtual input and output ports, use the the `createVirtualVirtualMIDIInput` and `createVirtualVirtualMIDIOutput` functions respectively.\n\n```swift\nlet virtualInput = midi.createVirtualMIDIInput(name: \"Virtual input\")\n\nlet virtualOutput = midi.createVirtualMIDIOutput(name: \"Virtual output\") { (list: UnsafePointer\u003cMIDIPacketList\u003e) in\n\n}\n```\n\n\n## Installation\n\nUse Swift Package Manager. Add the following `.Package` entry into your dependencies.\n\n```swift\n.Package(url: \"https://github.com/adamnemecek/webmidikit\", from: \"1.0.0\")\n```\n\n If you are having any build issues, look at the sample project [sample project](https://github.com/adamnemecek/WebMIDIKitDemo).\n\n## Documentation\n\n### MIDIAccess\nRepresents the MIDI session. See [spec](https://www.w3.org/TR/webmidi/#midiaccess-interface).\n\n```swift\nclass MIDIAccess {\n\t/// collections of MIDIInputs and MIDIOutputs currently connected to the computer\n\tvar inputs: MIDIInputMap { get }\n\tvar outputs: MIDIOutputMap { get }\n\n\t/// will be called if a port changes either connection state or port state\n\tvar onStateChange: ((MIDIPort) -\u003e ())? = nil { get set }\n\n\tinit()\n\t\n\t/// given an output, tries to find the corresponding input port\n\tfunc input(for port: MIDIOutput) -\u003e MIDIInput?\n\t\n\t/// given an input, tries to find the corresponding output port\n\t/// if you send data to the output port returned, the corresponding input port\n\t/// will receive it (assuming the `onMIDIMessage` is set)\n\tfunc output(for port: MIDIInput) -\u003e MIDIOutput?\n}\n```\n\n### MIDIPort\n\nSee [spec](https://www.w3.org/TR/webmidi/#midiport-interface). Represents the base class of `MIDIInput` and `MIDIOutput`.\n\nNote that you don't construct MIDIPorts nor it's subclasses yourself, you only get them from the `MIDIAccess` object. Also note that you only ever deal with subclasses or `MIDIPort` (`MIDIInput` or `MIDIOutput`) never `MIDIPort` itself.\n\n```swift\nclass MIDIPort {\n\n\tvar id: Int { get }\n\tvar manufacturer: String { get }\n\n\tvar name: String { get }\n\n\t/// .input (for MIDIInput) or .output (for MIDIOutput)\n\tvar type: MIDIPortType { get }\n\n\tvar version: Int { get }\n\n\t/// .connected | .disconnected,\n\t/// indicates if the port's endpoint is connected or not\n\tvar state: MIDIPortDeviceState { get }\n\n\t/// .open | .closed\n\tvar connection: MIDIPortConnectionState { get }\n\n\t/// open the port, is called implicitly when MIDIInput's onMIDIMessage is set or MIDIOutputs' send is called\n\tfunc open()\n\n\t/// closes the port\n\tfunc close()\n}\n```\n\n### MIDIInput\n\nAllows for receiving data send to the port.\n\nSee [spec](https://www.w3.org/TR/webmidi/#midiinput-interface).\n\n```swift\nclass MIDIInput: MIDIPort {\n\t/// set this and it will get called when the port receives messages.\n\tvar onMIDIMessage: ((UnsafePointer\u003cMIDIPacketList\u003e) -\u003e ())? = nil { get set }\n}\n```\n\n\n### MIDIOutput\n\n\nSee [spec](https://www.w3.org/TR/webmidi/#midioutput-interface).\n```swift\nclass MIDIOutput: MIDIPort {\n\n\t/// send data to port, note that unlike the WebMIDI API, \n\t/// the last parameter specifies offset from now, when the event should be scheduled (as opposed to absolute timestamp)\n\t/// the unit remains milliseconds though.\n\t/// note that right now, WebMIDIKit doesn't support sending multiple packets in the same call, to send multiple packets, you need on call per packet\n\tfunc send\u003cS: Sequence\u003e(_ data: S, offset: Timestamp = 0) -\u003e MIDIOutput where S.Iterator.Element == UInt8\n\t\n\t// clear all scheduled but yet undelivered midi events\n\tfunc clear()\n}\n```\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadamnemecek%2FWebMIDIKit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadamnemecek%2FWebMIDIKit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadamnemecek%2FWebMIDIKit/lists"}