PortAudio / portaudio

PortAudio is a cross-platform, open-source C language library for real-time audio input and output.
Other
1.39k stars 291 forks source link

Add support for iOS #881

Open fwcd opened 5 months ago

fwcd commented 5 months ago

Fixes #156, fixes #749

This is a rebased version of Hans Petter Selasky's patch (originally taken from here), adding support for Core Audio on iOS. I have had success using this in an experimental iOS port of the DJ app Mixxx (see here).

Building for iOS

Building for iOS is as simple as setting the CMAKE_SYSTEM_NAME (on a Mac):

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

Notes on Usage

As suggested by Alexander Carôt, setting up the shared AVAudioSession is generally recommended for every iOS project using this, for example like this (error handling omitted for brevity):

AVAudioSession* session = AVAudioSession.sharedInstance;
AVAudioSessionCategory category = AVAudioSessionCategoryPlayback;
AVAudioSessionMode mode = AVAudioSessionModeDefault;
AVAudioSessionCategoryOptions options = AVAudioSessionCategoryOptionMixWithOthers;

NSError* error = nil;
[session setCategory:category mode:mode options:options error:&error];
[session setActive:true error:&error];

iOS does set up a default audio session, the defaults are, however, rarely the desired configuration for a media app. For example, sound by default only plays when the phone is not on silence. Setting up the AVAudioSession as described above solved that.

While it would probably be possible to include it in PortAudio's initialization procedure, this is (IMO) better handled by the library consumer, since it involves making decisions about whether e.g. the app should mute others etc., something different apps will likely want to handle differently.

Thoughts welcome.

fwcd commented 5 months ago

Did my best to try fixing all of the formatting issues (manually, the patch contained a strange mix of 3-space, 4-space and tab indentation and the VSCode formatter would have preferred a different style to what the rest of the project uses).

The pa_whitelint.py check now passes.

RossBencina commented 5 months ago

iOS does set up a default audio session

In fact, the audio session is a singleton, so it's not really a question of whether to set one up, but whether we're happy with the defaults. The docs say "Although the default audio session provides useful behavior, it generally doesn’t provide the audio behavior a media app needs. To change the default behavior, you configure your app’s audio session category." [1]

I notice that you mention the playback category, but if full-duplex is required that will not work.[2]

Let's stew on this.

I'd like to get some other people to confirm this PR works for them before merging.

[1] https://developer.apple.com/documentation/avfaudio/avaudiosession [2] https://developer.apple.com/documentation/avfaudio/avaudiosession/category

philburk commented 5 months ago

@fwcd - thanks for doing this! it will be great to have iOS support.

This is a rebased version of Hans Petter Selasky's patch (originally taken from here),

Did your first commit have changes beyond just those needed for rebasing?

fwcd commented 5 months ago

In fact, the audio session is a singleton, so it's not really a question of whether to set one up, but whether we're happy with the defaults.

Yeah, I might have phrased it badly, I meant setting up the singleton (as in the code snippet). The playback category is just an example there. The defaults behaving weirdly, in the sense that speaker output wouldn't work, was something that I didn't (and still don't) really understand. So I would appreciate if someone could comment on that.

Another, more minor, issue I see with configuring the audio session from PortAudio is that it will introduce a new dependency on a much higher-level framework (AVFAudio) compared to Core Audio, which PortAudio currently uses. I am not familiar enough with iOS' audio stack to comment on whether there is a way to express this solely in terms of Core Audio's API, but that is probably something we would not want to do anyway, given that using the AVAudioSession API is generally the recommended approach for iOS apps.

Did your first commit have changes beyond just those needed for rebasing?

No, the first commit is 1-to-1 the HPS patch.

RossBencina commented 5 months ago

The defaults behaving weirdly, in the sense that speaker output wouldn't work, was something that I didn't (and still don't) really understand.

The docs say that the default won't output audio if the phone is in silent mode, could that be the issue?

fwcd commented 5 months ago

Ah, looks like I missed that part, that would definitely explain it :D

But yeah, I guess that just underlines that it's rarely the right behavior for media apps.

RossBencina commented 5 months ago

that would definitely explain it :D

Could you revise and simplify the PR description to reflect the updated state of your understanding please?

fwcd commented 5 months ago

Hm, the CI error is strange, I can't reproduce it locally, neither with Makefiles, nor with Ninja, nor with vcpkg:

 CMake Error at /usr/local/Cellar/cmake/3.28.0/share/cmake/Modules/FindPackageHandleStandardArgs.cmake:230 (message):
  Could NOT find Threads (missing: Threads_FOUND)

I found this upstream issue, however: https://gitlab.kitware.com/cmake/cmake/-/issues/18993

RossBencina commented 2 months ago

Failing iOS clang CI. Please you could you rebase and check. Possibly could be fixed with the fix we did on master: https://github.com/PortAudio/portaudio/commit/7de41c1e07cdc506f4438e5ec9a1315a4ac9330c