Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for targeting iOS #65

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open

Add support for targeting iOS #65

wants to merge 6 commits into from

Conversation

fwcd
Copy link

@fwcd fwcd commented Feb 3, 2024

Since iOS is largely based on the same frameworks as macOS, the changes required to support iOS are minor. It does, however, lack some time-related methods, most notably CoreAudio/HostTime.h and the suggested approach appears to be using mach time instead: https://developer.apple.com/library/archive/qa/qa1643/_index.html

With this PR portmidi can now be built for iOS simply by changing the CMAKE_SYSTEM_NAME:

cmake -B build -DCMAKE_SYSTEM_NAME=iOS
cmake --build build

Copy link
Author

@fwcd fwcd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some notes on the implementation

Comment on lines 975 to 982
UInt64 current_host_time(void)
{
#if TARGET_OS_OSX
return AudioGetCurrentHostTime();
#else
return mach_absolute_time();
#endif
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mach methods are available on macOS too, so we could probably drop the macOS-specific AudioGetCurrentHostTime, AudioGetHostClockFrequency etc. I've kept them around under a TARGET_OS_OSX conditional for now to avoid accidentally introducing a regression if there's a bug in the (albeit quite short) mach-based implementation for iOS. Since it introduces an additional, likely not very heavily used code path, I would also be open to removing the macOS branch again. Thoughts welcome.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some reason to choose mach_absolute_time()?

It seems to be the officially recommended drop-in replacement: https://developer.apple.com/library/archive/qa/qa1643/_index.html

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clock_gettime_nsec_np(CLOCK_UPTIME_RAW) seems to be a wrapper around mach_absolute_time:

https://github.com/apple-oss-distributions/Libc/blob/c5a3293354e22262702a3add5b2dfc9bb0b93b85/gen/clock_gettime.c#L117-L119

So it's probably a question of style which one to choose? mach_absolute_time seems pretty widely used, so I'm sure they won't deprecate that anytime soon.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, because documentation for mach_absolute_time says use clock_gettime_nsec_np(CLOCK_UPTIME_RAW) (!). But OK, if Apple can't decide, I'll go with your choice.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the link, I must have missed that note. Interesting that they mention fingerprinting. But regardless, mach_absolute_time is the lower level API and almost all projects I've seen that use Core Audio/Core MIDI on iOS went with mach_absolute_time too.

porttime/ptmacosx_mach.c Outdated Show resolved Hide resolved
porttime/ptmacosx_mach.c Outdated Show resolved Hide resolved
#include <CoreAudio/HostTime.h>
#else
#include <mach/mach_time.h>
#endif

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be doing what PortTime was intended to do: provide a system-independent way to access time functions.
There are actually 2 porttime implementations for Mac: ptmacosx_cf.c based on core foundation calls and ptmacosx_mach.c based on Mach calls, but it looks like ptmacosx_mach.c still uses core foundation calls for time functions.
I'd much prefer to add system-independent functions in ptmacosx_mach.c and porttime.h than here for getting and translating nanos to portmidi (milliseconds).
A critical question though, is have you established that the nanos you get from Mach calls are the same nanos you get from CoreAudio? It's important because CoreMidi uses CoreAudio timestamps (undocumented but they match in tests) and it won't work to use just any nanosecond timer. I have no idea if mach gets the same nano values.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hence #65 (comment):

Ideally, we should probably share these utility methods between porttime and pm_mac. The right place for that would likely be porttime, but we'd also create a leaky abstraction by exposing these implementation details, even if only to the (also platform-specific) pm_mac. Ideas welcome.

The question would be how pmmacosxcm.c would consume those functions (or whether there's a way to model these conversions in a fully platform-agnostic way, so they can be part of the main porttime interface. Haven't dug deep enough into porttime to understand the architectural decisions there though).

A critical question though, is have you established that the nanos you get from Mach calls are the same nanos you get from CoreAudio? It's important because CoreMidi uses CoreAudio timestamps (undocumented but they match in tests) and it won't work to use just any nanosecond timer. I have no idea if mach gets the same nano values.

That would indeed be interesting to check, I haven't done so yet. I would be surprised though if they would differ.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think your additional functions to get host time and convert host time to micros and nanos is fine. At this point, only OSX and IOS would use them, and I think it's fine to make them Apple-only functions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm working to integrate a lot of changes into PortMidi. Returning to this, the whole reason for incorporating CoreAudio time into PortMidi was that the absolute time values are important because they are used as Midi timestamps by MacOS (I don't think this is documented). I believe many PortMidi tests would work fine with bad timestamps, so it's important to look at some MIDI packets in iOS and compare their timestamps to time sources to see what time source is being used (or find it in documentation if it is stated anywhere). Similarly, output timing should be tested to see if absolute timestamps based on mach_absolute_time() or whatever can be used to produce correctly timed output. If you can verify the time source is correct (at least usable for timing and seems correct), then this would be a nice addition to PortMidi.

static UInt64 current_host_time(void);
static UInt64 nanos_to_host_time(UInt64 nanos);
static UInt64 host_time_to_nanos(UInt64 host_time);
static UInt64 micros_per_host_tick(void);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

portmidi may be overoptimized to use host_time when possible rather than always using nanos (which would give a simpler, smaller set of time functions), but OK, let's extend porttime. These additional functions can exist for apple only (since they are not called from other platforms). Names should be, e.g. Pt_Current_Host_Time -- now I kind of hate this style, but it was originally supposed to be consistent with PortAudio, and better to be consistent that not (or to break everything by changing naming conventions now).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Names should be, e.g. Pt_Current_Host_Time

Pt_CurrentHostTime? That would be consistent with the PortMidi naming style:

PMEXPORT int Pm_HasHostError(PortMidiStream * stream);

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've moved these functions to a new header, ptmacosx.h, since they are Apple-specific and so they don't leak into the porttime interface on other platforms:

UInt64 Pt_CurrentHostTime(void);
UInt64 Pt_NanosToHostTime(UInt64 nanos);
UInt64 Pt_HostTimeToNanos(UInt64 host_time);
UInt64 Pt_MicrosPerHostTick(void);

fwcd added a commit to fwcd/vcpkg that referenced this pull request Feb 29, 2024
@fwcd fwcd requested a review from rbdannenberg March 3, 2024 20:42
fwcd added a commit to fwcd/vcpkg that referenced this pull request Jun 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants