adamnemecek / webmidikit Goto Github PK
View Code? Open in Web Editor NEWSimplest MIDI Swift library
License: MIT License
Simplest MIDI Swift library
License: MIT License
I have a Capacitor app, and have been looking for a iOS MIDI solution. This seems promising, I am wondering if anyone has had experience using this as a Capacitor Plugin?
PS... I ran into a few issues trying to build this (and the demo project) in Xcode 12.1.
encountered on Xcode 12.5 and Xcode 13.1
the manifest is missing a Swift tools version specification; consider prepending to the manifest '// swift-tools-version:5.4.0' to specify the current Swift toolchain version as the lowest Swift version supported by the project; if such a specification already exists, consider moving it to the top of the manifest, or prepending it with '//' to help Swift Package Manager find it
Line 94 from MidiPacketList.swift crashes with EXC_BAD_ACCESS.
p = MIDIPacketNext(&p).pointee
Any idea how to safeguard this line?
The problem is here:
Assertion failed: file WebMIDIKit/MIDIPortMap.swift, line 72
The code:
assert(port.state == .connected)
Is this assertion necessary?
This assertion fires when you unplug a MIDI device of which a port is used.
I think removing this cause for runtime exceptions in DEBUG builds is a good fit (because the assumption that the device is still connected isn't always true!) and cannot gracefully be handled at all.
Hey Adam, do you think could please make this repo private or delete it?
Hello Adam,
I was looking for a swift MIDI solution and found your code.
I agree that the PacketList API cannot be used from swift right now, but I found a simple way to work around it using some small inlined c-functions.
With these functions you can use the CoreMIDI PacketList API from swift without restrictions.
Feel free to check out the repository jrmadev/SwiftMIDI
I took a look at your packet iterating extension:
extension MIDIPacketList: Sequence {
public typealias Element = MIDIEvent
public func makeIterator() -> AnyIterator<Element> {
// the packet struct is copied here:
var p: MIDIPacket = packet
var i = (0..<numPackets).makeIterator()
return AnyIterator {
defer {
// the packet struct is copied here:
p = MIDIPacketNext(&p).pointee
}
return i.next().map { _ in .init(packet: &p) }
}
}
}
The iterated packets are copied from the original list.
In MIDIServices.h MIDIPacketNext
is documented like this:
/*!
@function MIDIPacketNext
@abstract Advances a MIDIPacket pointer to the MIDIPacket which
immediately follows it in memory if it is part of a MIDIPacketList.
@param pkt A pointer to a MIDIPacket in a MIDIPacketList.
@result The subsequent packet in the MIDIPacketList.
*/
When You assign the packet struct to a variable (struct are value types in swift) it is no longer part of a packet list. As a side effect you loose bytes in case the data is longer than 256 bytes.
MIDIPacketNext calculates pointer offsets i.e. when calling MIDIPacketNext(&p) the returned pointer will point eventually into memory outside of of the current packet.
In my repository I have a small code sample showing exactly this. IterateOverCopiedPackets
Greetings, Jan
any device, virtual or hardware can disconnect at any time via cable lost, virtual receiver app crashed or is just turned off.
Which means testing if something is connected that is not connected of course makes no sense. The enveloping function removes (actually just releases the entry) the entry from MIDIPortMap for inputs or outputs and we force to remove it for reason.
Lets make an example: we turn off an device, CoreMidi invalidates its entry, sends the notification about removal, notification is received and calls back to remove it from local MIDIPortMap. Now we try to test if something that can not be connected is connected when we got just the notification about just that.. lots of overthinking in this API
the problem with this is this ends up in something that is behaving like a leak. Have to admit that comes up in play with WkWebKit handling WebMidi with the help of MIDIDriver.swift and MIDIMessageHandler.swift
but messuring with Intruments i was able to see that memory allocation is growing and growing before/in & after onMIDIMessage
declared block is called.
App memory increases with any message received.
thats the point where i must admit i will start from scratch, likely ending up in new API that does it the classic way.
After adding dependency, the following error:
the manifest is missing a Swift tools version specification; consider prepending to the manifest '// swift-tools-version: 5.7.1' to specify the current Swift toolchain version as the lowest Swift version supported by the project; if such a specification already exists, consider moving it to the top of the manifest, or prepending it with '//' to help Swift Package Manager find it
AudioGetCurrentHostTime()
and AudioConvertNanosToHostTime(UInt64($0 * 1000000))
don't exist on iOS so we need to use https://developer.apple.com/library/archive/qa/qa1643/_index.html
any idea how we should go about using the CAHostTimeBase
obj-c class in order to get this done?
Hey Adam,
I'm trying to test your package in a minimal setup. I added it as required in my Package.swift
:
// swift-tools-version:5.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "WebMidiKitTest",
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/adamnemecek/WebMIDIKit.git", majorVersion: 1)
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "WebMidiKitTest",
dependencies: ["WebMIDIKit"]),
.testTarget(
name: "WebMidiKitTestTests",
dependencies: ["WebMidiKitTest"]),
]
)
But when I run swift build
I get the following error message:
/Users/nik/proj/IDEs/Xcode/WebMidiKitTest: error: manifest parse error(s):
/Users/nik/proj/IDEs/Xcode/WebMidiKitTest/Package.swift:11:10: error: ambiguous reference to member 'package'
.package(url: "https://github.com/adamnemecek/WebMIDIKit.git", majorVersion: 1)
I tried changing majorVersion: 1
to from: "1.0.0"
which seems to atleast conform to the function signature but this gave the following error:
Updating https://github.com/adamnemecek/WebMIDIKit.git
error: dependency graph is unresolvable; found these conflicting requirements:
Dependencies:
https://github.com/adamnemecek/WebMIDIKit.git @ 1.0.0..<2.0.0
Can you help me out here? Really depend on your library for a generative music project, thanks :)
MIDI events are not received when app goes to background. How can this be enabled?
When an MIDIEndpoint is gone (physical disconnected or software hosting a virtual port teared down) you can't ask for kMIDIPropertyUniqueID, it is gone already, there is not ID available no more.
internal final class MIDIEndpoint: Codable {
...
final var id: Int {
self[int: kMIDIPropertyUniqueID]
}
...
}
causing the API to crash anytime CoreMIDI is asked to get the ID when it can not answer such. That is a conceptual ERROR.
This is the reason why the Endpoint ID once evaluated is stored in your local PortMap/List.
In case a notification arrives telling it is gone it carries the ID that caused it with it.. but asking CoreMIDI again will do the crash.. Instead you take the notified Endpoint ID, iterate thru your PortMap and clean up the List to represent a proper local state of all available CoreMidi devices.
All MIDI Messages received are the same, even though they are not.
Message: 0x0000700003bc6050
Code:
func midiTest() {
inputPort?.onMIDIMessage = { (list: UnsafePointer<MIDIPacketList>) in
for packet in list {
print("received \(packet)")
}
}
}
with CoreMIDI and WebMIDIKit imports (and of course, foundation.)
dd
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.