Crudebyte Logo
JACKiOS  1.9.10.3
JACK Seniors

Short summary of JACK iOS differences for experienced JACK developers.

This page is a short summary for developers who are already familiar with developing with the JACK API on other operating systems. It is dedicated to summarize the key differences between this iOS version of the JACK API compared to the one you are used to from other systems.

There were quite some complications to bring JACK on iOS. On one hand technical issues, since iOS is a very constraint environment, and licensing issues on the other hand, since there are no DLLs allowed for iOS apps being published on the Apple App Store, forcing us to distribute a static libary version of JACK and resolving license implications caused by this. Due to those reasons we were forced to adjust the JACK API for iOS to some extent.

Removed API parts

The following parts have been removed from the iOS version of the JACK API:

  • "Control" API: Due to licensing issues. It was just used on server side to configure the server's drivers in a more clean way. The current iOS drivers are configured in a more direct way for now.
  • "Internal Client" API: Technical Reason. There are no DLLs possible/allowed for iOS apps on the Apple App Store.
  • jslist.h: Due to licensing issues. Was not a part of the JACK API anyway.
  • "Network" API: Currently not implemented on iOS.
  • ringbuffer.h: Was not a part of the JACK API, rather shipped for convenience as utility code for new applications. Removed to avoid license misapprehensions.
  • "Session" API: Due to Technical reasons, cannot be used on a sandboxed environment like iOS in current form.

Added API parts

There was only one new API part been added to the JACK iOS API:

  • "Custom" API: Introduced to allow clients to provide an icon representing them in the JACK GUI. Since iOS apps have sandboxed file systems, there is no shared global file system that would allow apps to share files like on other systems. Read Sharing client specific, custom data among all clients for details.

as well as the following additional functions:

Linking libjack

Like mentioned above, libjack is only available as static library on iOS. Download the latest version of the JACK iOS SDK and extract it to some arbitrary place on your Mac. Then drag jack.framework into your iOS app's Xcode project. The framework contains the static library and the header files.

Background Mode

On iOS, apps are by default suspended (by the SpringBoard service) if the app is not in foreground. Since that would destroy the whole concept behind JACK, you need to make sure the audio background mode is enabled in your app's Info.plist file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDisplayName</key>
<string>My App Name</string>
...
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
</dict>
</plist>

This ensures that your app will continue running in the background after the user pressed the home button of the device (actually in conjunction with the automatic CoreAudio mechanisms already handled for you by the JACK library).

The coding part

Except of API parts being removed / added to the JACK iOS API, as already explained above, there is only very little you need to know regarding new things on code level. The few ones are enumerated next.

Publish client icon

To show an icon for your JACK client on the "Client Map" screen of the JACK (server) app, use the following Objective C code:

#include <jack/custom.h>
...
NSString* iconFile = [[NSBundle mainBundle] pathForResource:@"Icon-Metro136" ofType:@"png"];
NSFileHandle* fileHandle = [NSFileHandle fileHandleForReadingAtPath:iconFile];
NSData* data = [fileHandle readDataToEndOfFile];
const void* rawData = [data bytes];
const size_t size = [data length];
jack_custom_publish_data(client, "icon.png", rawData, size);
[fileHandle closeFile];

Adjust "Icon-Metro136" to the actual name of your icon image file (PNG format expected at this point). And make sure that icon file is added to your Xcode project. Currently icons on the "Client Map" of the JACK (server) app are shown with a resolution of 136x136px on Retina displays. If the icon you provide has a different resolution, it will be scaled to the appropriate resolution. You may replace the Objective C code above of course by any other means (i.e. by some standard C++ code to load the PNG file). The relevant part is only the jack_custom_publish_data() call, which actually transmits the PNG icon as raw data buffer to the JACK server. The key name "icon.png" is mandatory on this call. If you use another name, it will not appear on the JACK GUI.

Create JACK client

It is important on iOS that you take care about the JackVersionError result flag. You may handle the jack_client_open() step in iOS apps like suggested below:

#include <jack/jack.h>
...
jack_status_t status;
jack_client_t* client = jack_client_open("My App Name", JackNullOption, &status);
if (!client) {
if (status & JackVersionError)
fprintf(stderr, "App not compatible with running JACK version!\n");
else
fprintf(stderr, "JACK (server) app seems not to be running!\n");
return -1;
}

The call above usually just might fail because of the 2 reasons shown.

  1. The official JACK (server) app must be running, since it not only provides the control GUI of the JACK system, but also the JACK server. We recommend you, to simply call jack_client_open() on app start, and in case it succeeds, initialize your app for using JACK. If the call fails, initialize your app to use your usual audio/MIDI system like the CoreAudio/CoreMIDI API. This way end users don't have to configure your app explicitly. Keep in mind the end user might start quite a bunch of JACK client apps!
  2. It is important that you check for the JackVersionError flag on iOS, and i.e. prompt the user a message box in this case. Reason: libjack is statically linked to your app binary on iOS. So the JACK library you linked now, might become incompatible to a future version of JACK. Due to static linking the iOS version of JACK is much more sensible regarding version changes than on other systems. We took precautions that version incompatibility should not happen soon. It might happen one day though, however it will most probably just require that you download the latest version of the JACK SDK and rebuild your app.

You may also use jack_server_installed() to check whether JACK is installed on the device at all, and jack_server_running() to check whether the main JACK (server) app is currently running on the device.

Warning
In the rare case you want your own CoreAudio code rendering/capturing code to remain active for some reason, while using JACK at the same time in the same app, you should pass JackNoBackground to jack_client_open(). Reason: the JACK client framework automatically runs a fake CoreAudio mechanism to actually enable your app to remain active in the background, which would in that very special case cause a conflict.

Shutdown Callback

You should implement a JACK shutdown callback and register it with jack_on_info_shutdown(), like this

// Callback for being executed when the JACK (server) app is closing this
// JACK client for some reason. We definitely recommend you to implement
// such a callback for your app.
static void shut_down (jack_status_t code, const char* reason, void* arg)
{
if (code & JackClientKilled) {
// If this flag is set, then your client was closed by the user by
// pressing "X" button of the client icon in the JACK control app.
// In this case you should simply quit your app, since the user
// was already asked in the JACK control app whether he really wants
// close this app.
exit(0);
} else {
// Don't forget to free your JACK client when the JACK shutdown
// callback is triggered, because JACK does *not* do it for you!
dispatch_async(dispatch_get_main_queue() /*GUI Thread*/, ^{
client = NULL;
});
// ... otherwise if this block is reached, your client might got
// closed because the JACK control app (and with it the server)
// was closed or i.e. when JACK apps created too much CPU load,
// causing the server to sort out some of the clients, to prevent
// the device turning unresponsive. Either quit the app, or show a
// message dialog to the user, asking him if the app can be closed
// (i.e. preventing some unsaved data to get lost) or automatically
// switch your app from JACK mode to i.e. CoreAudio/CoreMIDI mode.
if (code & JackClientZombie) {
// Client was closed because of too much CPU load
} else {
// Possibly (still unknown) reason
}
}
}

You would dispatch jack_client_close() in the shutdown callback, since it is not valid to call jack_client_close() in the context (thread) of the shutdown callback. Be sure to free your client's resources in your app delegate's termination handler:

- (void)applicationWillTerminate:(UIApplication *)application
{
if (client) jack_client_close(client);
}

Freeing your client resources is so important, because it would otherwise leak system resources, which would even remain leaked after your app terminated. Note also that calling jack_client_close() is the clean way for an application to be removed from the JACK application graph. Not calling this function would typically cause the server to interrupt the audio stream and result in an audio glitch.

Remote App Start

app_launch_dialog_shot.png

The JACK control app provides a very important feature from user aspect: the ability to launch JACK client apps directly from the JACK (server) app and fast switching the screen between the various JACK apps, without forcing the user to do such things with home button and iOS desktop.

To support these important features, you need to add an URL schema entry to your app's Info.plist file.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDisplayName</key>
<string>My App Name</string>
...
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.foocompany.myapp</string>
<key>CFBundleURLSchemes</key>
<array>
<string>foocompanymyapp</string>
</array>
</dict>
</array>
</dict>
</plist>

It is important here that you choose an unique URL schema, which is not already been used by another iOS app. So preferably make the URL schema name i.e. a combination of your company's name and your app's name, which was i.e. foocompanymyapp in the example above.

We also recommend you to call jack_app_register() each time your app launches. For example in the following method of your app delegate.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
...
// ensure this app is known by JACK
return YES;
}

This call checks whether your app is already been "known" to the local JACK installation. If not, it will inform the JACK installation about the existence of your app, so that your app appears in the list of installed JACK apps and accordingly to allow the user to start your app directly within the JACK control app.

You can check the launchOptions argument of that app delegate method shown above, for finding out whether your app was launched regularly from the iOS desktop or by JACK instead:

// was this app launched from the iOS desktop or by some other app?
NSURL* url = [launchOptions valueForKey:UIApplicationLaunchOptionsURLKey];
NSString* src = [launchOptions valueForKey:UIApplicationLaunchOptionsSourceApplicationKey];
NSLog(@"App launched by app '%@' with URL '%@'", src, [url absoluteString]);

You may also provide a button in your app to switch back to the JACK control app. When that button is pressed by the user, simply call jack_gui_switch_to_client() with the following string argument:

// bring the JACK control app into foreground on the screen
jack_gui_switch_to_client(client, "jack");

which will immediately bring the JACK (server) app into foreground on the screen. You might even use the same function with your own JACK client name instead to bring your own app into foreground, i.e. to inform the user about some very important occurrence in your app while the user is currently watching at another app. However we discourage to use jack_gui_switch_to_client() to bring your own app "unasked" into foreground. It might make sense under very, very rare and really important circumstances. But in doubt, don't use the function for that purpose. Because otherwise it could end up your app being very annoying to the user, causing more damage to the user experience instead of bringing an advantage.

Note
You need to build your app with JACK iOS SDK 1.9.10.2 or higher for the remote app start and screen switching features.

That's it!

Nothing more to say.
Back to overview ...

DE • EN
Copyright © MMXIII Crudebyte. All rights reserved.

twitter