Audioshare functionality for iOS apps

I’ve already discussed this with the JUCE dev team, but I’m putting this here as a placeholder.
From my experience of launching an iOS app a couple of weeks ago (SpaceCraft Granular Synth) I have had a huge number of requests for Audioshare functionality. Audio share is a highly respected and popular file management app in the iOS audio community. http://kymatica.com/Software/AudioShare
For a great example of it in action, see Borderlands import function, so slick!
It would be awesome if Audioshare integration could be a built-in feature of JUCE for iOS apps, maybe included as one of the examples.
Note that it is already possible to access to Audioshare folder via the native iOS Files app (which I load asynchronously from SpaceCraft), but people are specifically asking for a more integrated functionality.

1 Like

If you’re ok with going down into Objective-C++ it shouldn’t be too hard to wrap the AudioShare library yourself, within your app, or as a JUCE module (which could then be shared with the community ;)) IMO we should not expect the JUCE team to implement everything we need, but build it and share it if it does not yet exist and might be useful to others…

That’s a good point Adam, I didn’t mean for it to come across as presumptuous, it would certainly be a good opportunity for me to start learning objective-C :slight_smile: If I can figure it out myself I’ll definitely share it with the JUCE team!

The idea of sharing my own code to be used in JUCE(!!) would be a scary prospect but I might just go for it :wink:

I meant more with the community, i.e. here :wink: TBH such functionality which is limited to one platform is unlikely to make it into the JUCE library.

1 Like

Looking at the docs, the API looks quite simple. All you need to do is put the Objective C code within a .mm file and method definitions in a header. See https://gist.github.com/adamski/7a4fcb91925fd9f73ae849842f918cbb for an example of how to do this (ignore the Windows specific bits).

If you’re already familiar with C++ then Objective-C is fairly straightforward to learn (if a little quirky).

I know you’re the man when it comes to this sort of thing, I watched your native APIs youtube yesterday in fact. Once I have the other updates sorted I’ll take the plunge. Thanks for the advice.

I finally managed to get around to properly integrating objective-C into my iOS app (for the purposes of shared file access between standalone and AUv3), although I really am a beginner when it comes to obj-C.

I have also tried to get file import from Audioshare working and having gone through the other steps in the Audioshare SDK. The process is :

  1. call [[AudioShare sharedInstance] initiateSoundImport]; from within my app --> OK
  2. audioshare automatically opens and asks to choose a file --> OK
  3. click ‘import into app’ button in Audioshare reopens my app --> OK
  4. checkPendingImport:url should be called within my app --> this part is not working (see below)

This issue is that I need to place this code (from the Audioshare SDK https://github.com/lijon/AudioShareSDK) in an “openURL handler”…

if([[AudioShare sharedInstance] checkPendingImport:url withBlock:^(NSString *path) {

   // Move the temporary file into our Documents folder
   NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
   NSString *documentsDirectory = [paths objectAtIndex:0];
   NSString *destination = [documentsDirectory stringByAppendingPathComponent:[path lastPathComponent]];
   [[NSFileManager defaultManager] moveItemAtPath:path toPath:destination error:nil];

   // Load the imported file
   [mySoundEngine loadSample:destination];

 }]) {
   return YES;
 }

The Audioshare dev (who is not familiar with JUCE) told me that this would usually be located in AppDelegate.m file but in JUCE it is not obvious (to me, at least) where this code should be placed. I have spotted some objective-C ‘openURL’ in the JUCE code (juce_ios_Audio.cpp and juce_mac_Files.mm) but they don’t seem to be called in step 4 above.

If I can get this working then anybody making a JUCE iOS app can have Audioshare! I’d really appreciate if anybody can give some pointers to an objective-C (and iOS) numpty.

Here’s a pattern I recently learned for integrating objectiveC stuff so it appears like regular JUCE classes, via the Listener pattern:

//Foo.h
struct Foo
{
    Foo();
    enum NativeAction
    {
        a, b, c, //etc
    };
    void handleNativeAction(NativeAction nativeAction);
    struct Listener
    {
         virtual ~Listener() { }
         virtual void nativeActionCallback(Foo*, NativeAction&) = 0;       
    };
    Array<Listener*> listeners;
    void addListener( Listener* L ) { listeners.addIfNotAlreadyThere( L ); }
    void removeListener( Listener* L ) { listeners.removeFirstMatching( L ); }
    //don't forget your listener locks!
};
//Foo.mm file
#include "Foo.h"
#import //CoreFoundations.h or UIKit or whatever

@interface MyObjCWrapper : NSObject
{
    Foo* myFoo;
    //other data members
};
//member functions of MyObjCWrapper
-(id) init;
-(void) setOwner:(Foo*)owner;
-(void) someCallback:(ArgType)args;
@end
@implementation MyObjCWrapper
-(id)init 
{ 
    if( self = [super init] )
    {
         //do all of the setup into native iOS calls here so 
         //someCallback gets called when the native callbacks are triggered
    }
    return self;
}
-(void) setOwner:(Foo*)owner
{
    myFoo = owner;
}
-void someCallback:(ArgType)args
{
    //convert args to Foo::NativeAction
    Foo::NativeAction fooArg = convert(args); 
    myFoo->handleNativeAction(fooArg); //pass it to Foo instance
}
@end 
//Foo implementation
Foo::Foo()
{
    //I'm not sure how this doesn't leak.  
    //it probably doesn't matter on iOS, tbh because Apple just hard-crashes 
    //apps when memory is needed.
    //maybe Foo needs a destructor to release this properly? 
    MyObjCWrapper* wrapper = [[MyObjCWrapper alloc] init];
    [wrapper setOwner:this];
}

void Foo::handleNativeAction(NativeAction someParam)
{
    //now your objC result has been passed to pure C++
    for( auto* listener : listeners )
        listener->nativeActionCallback(this, someParam);
}

Just add a Foo member to your class, and register your class as a Foo::Listener

struct MyClass : public Foo::Listener
{
    void nativeActionCallback(Foo*, Foo::NativeAction& action) override 
    {
        //tadah, your ObjC results get passed to JUCE here.
    }
    Foo foo;

    MyClass() { foo.addListener(this); }
};

I’m not sure if that answers your question, but you can probably use this to wrap that openURL objectiveC function into a JUCE listener-based class.

2 Likes

Thanks very much @matkatmusic, appreciated. It has also been suggested to look into START_JUCE_APPLICATION_WITH_CUSTOM_DELEGATE although there doesn’t seem to be much in the way of examples available. I’m hoping to find some time to investigate these suggestions tomorrow…