floatinghotpot / cordova-plugin-nativeaudio

The low latency audio plugin is designed to enable low latency and polyphonic audio from Cordova/PhoneGap applications, using a very simple and basic API.
MIT License
233 stars 290 forks source link

Feature: Support cordova file system urls #24

Open wootwoot1234 opened 9 years ago

wootwoot1234 commented 9 years ago

Cordova has cleaned up it's file api and now makes it really easy to create/edit/move files. The problem is, I can't pass a 'file://' url to this plugin only relative paths. Would it be possible to add support for absolute paths or for 'file://" urls?

https://github.com/apache/cordova-plugin-file/blob/master/doc/index.md

Thanks

fidoboy commented 9 years ago

:+1: please

gylippus commented 9 years ago

@wootwoot1234 @fidoboy Have either of you found a solution or workaround to this?

Thanks.

fidoboy commented 9 years ago

no :(

gylippus commented 9 years ago

@fidoboy turns out I may be onto something.

In the LowLatencyAudio.m file around line 52 is where it is looking for the file.

I have modified this based on some code I found at https://github.com/floatinghotpot/cordova-plugin-lowlatencyaudio/blob/master/src/ios/LowLatencyAudio.m and from the CDVFile.m file for finding files.

Basically I am checking for the file in two different places so that I can use local files in the www and ones that I have downloaded and am passing the full CDV File path (cordova.file.dataDirectory + ). This is for iOS only, but I presume there will be something similar for Android if it is needed there too.

NSNumber* existingReference = [audioMapping objectForKey: audioID];
        if (existingReference == nil)
        {
            NSString* basePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"www"];
            NSString* path = [NSString stringWithFormat:@"%@", assetPath];
            NSString* pathFromWWW = [NSString stringWithFormat:@"%@/%@", basePath, assetPath];

            NSURL *fileURL = [NSURL URLWithString:path];
            NSURL *resolvedFileURL = [fileURL URLByResolvingSymlinksInPath];
            path = [resolvedFileURL path];
            if ([[NSFileManager defaultManager] fileExistsAtPath : path])
            {
                NSLog(@"could find file at path: %@", path);
                LowLatencyAudioAsset* asset = [[LowLatencyAudioAsset alloc] initWithPath:path withVoices:voices];
                [audioMapping setObject:asset  forKey: audioID];

                pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: CONTENT_LOAD_REQUESTED];
                [self.commandDelegate sendPluginResult:pluginResult callbackId:callbackID];
            }
            else if ([[NSFileManager defaultManager] fileExistsAtPath : pathFromWWW]) {
                NSLog(@"could find file at pathFromWWW: %@", pathFromWWW);
                LowLatencyAudioAsset* asset = [[LowLatencyAudioAsset alloc] initWithPath:pathFromWWW withVoices:voices];
                [audioMapping setObject:asset  forKey: audioID];

                pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: CONTENT_LOAD_REQUESTED];
                [self.commandDelegate sendPluginResult:pluginResult callbackId:callbackID];
            }
            else
            {
                NSLog(@"could not file: %@", path);
                pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: ERROR_NOT_FOUND];        
                [self.commandDelegate sendPluginResult:pluginResult callbackId:callbackID];
            }
        }
        else 
        {
            pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: WARN_EXISTING_REFERENCE];        
            [self.commandDelegate sendPluginResult:pluginResult callbackId:callbackID];
        }
gylippus commented 9 years ago

@fidoboy I just realized that I was using an even older version of this plugin so I believe it would actually be more like this for this project

            NSString* basePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"www"];
            NSString* path = [NSString stringWithFormat:@"%@", assetPath];
            NSString* pathFromWWW = [NSString stringWithFormat:@"%@/%@", basePath, assetPath];

            NSURL *fileURL = [NSURL URLWithString:path];
            NSURL *resolvedFileURL = [fileURL URLByResolvingSymlinksInPath];
            path = [resolvedFileURL path];

            if ([[NSFileManager defaultManager] fileExistsAtPath : path]) {
                NSLog(@"could find file at path: %@", path);
                LowLatencyAudioAsset* asset = [[LowLatencyAudioAsset alloc] initWithPath:path withVoices:voices withVolume:volume];
                [audioMapping setObject:asset  forKey: audioID];

                [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: CONTENT_LOAD_REQUESTED] callbackId:callbackId];
            } else if ([[NSFileManager defaultManager] fileExistsAtPath : pathFromWWW]) {
                NSLog(@"could find file at pathFromWWW: %@", pathFromWWW);
                LowLatencyAudioAsset* asset = [[LowLatencyAudioAsset alloc] initWithPath:pathFromWWW withVoices:voices withVolume:volume];
                [audioMapping setObject:asset  forKey: audioID];

                [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: CONTENT_LOAD_REQUESTED] callbackId:callbackId];
            } else {
                NSLog( @"audio file not found" );
                [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: ERROR_NOT_FOUND] callbackId:callbackId];
            }
fidoboy commented 9 years ago

Thanks gylippus your code is very useful, but it could be pretty cool to have the same on android.

ddresch commented 9 years ago

Hi @gylippus can you provide a working fork for this issue? I'm trying to integrate your posted code but struggling with errors like error: use of undeclared identifier 'voices' which is definitely a really simple case but I'm not Cocoa Dev so I just need to get it running :) Thx a lot if possible.

ddresch commented 9 years ago

@gylippus helps sometimes to ask and the answers follow by them self. Got it at least compiling now so no need to answer. Thanks!

gylippus commented 9 years ago

@ddresch glad to help :)

-Rubber Duck

RShergold commented 8 years ago

Are there any updates on this feature? I'm looking to play multiple audio tracks downloaded and saved in the app's Documents dir.

michaelkariv commented 8 years ago

Anyone implemented the fix for Android? I wish "file://" instead of appending "www/" to path was the implementation here

pmwisdom commented 8 years ago

Honestly, cordovas media plugin has a great function for both android and ios.

IOS for example:

// Maps a url for a resource path for playing
// "Naked" resource paths are assumed to be from the www folder as its base
- (NSURL*)urlForPlaying:(NSString*)resourcePath
{
    NSURL* resourceURL = nil;
    NSString* filePath = nil;

    // first try to find HTTP:// or Documents:// resources

    if ([resourcePath hasPrefix:HTTP_SCHEME_PREFIX] || [resourcePath hasPrefix:HTTPS_SCHEME_PREFIX]) {
        // if it is a http url, use it
        NSLog(@"Will use resource '%@' from the Internet.", resourcePath);
        resourceURL = [NSURL URLWithString:resourcePath];
    } else if ([resourcePath hasPrefix:DOCUMENTS_SCHEME_PREFIX]) {
        NSString* docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
        filePath = [resourcePath stringByReplacingOccurrencesOfString:DOCUMENTS_SCHEME_PREFIX withString:[NSString stringWithFormat:@"%@/", docsPath]];
        NSLog(@"Will use resource '%@' from the documents folder with path = %@", resourcePath, filePath);
    } else if ([resourcePath hasPrefix:CDVFILE_PREFIX]) {
        CDVFile *filePlugin = [self.commandDelegate getCommandInstance:@"File"];
        CDVFilesystemURL *url = [CDVFilesystemURL fileSystemURLWithString:resourcePath];
        filePath = [filePlugin filesystemPathForURL:url];
        if (filePath == nil) {
            resourceURL = [NSURL URLWithString:resourcePath];
        }
    } else {
        // attempt to find file path in www directory or LocalFileSystem.TEMPORARY directory
        filePath = [self.commandDelegate pathForResource:resourcePath];
        if (filePath == nil) {
            // see if this exists in the documents/temp directory from a previous recording
            NSString* testPath = [NSString stringWithFormat:@"%@/%@", [NSTemporaryDirectory()stringByStandardizingPath], resourcePath];
            if ([[NSFileManager defaultManager] fileExistsAtPath:testPath]) {
                // inefficient as existence will be checked again below but only way to determine if file exists from previous recording
                filePath = testPath;
                NSLog(@"Will attempt to use file resource from LocalFileSystem.TEMPORARY directory");
            } else {
                // attempt to use path provided
                filePath = resourcePath;
                NSLog(@"Will attempt to use file resource '%@'", filePath);
            }
        } else {
            NSLog(@"Found resource '%@' in the web folder.", filePath);
        }
    }
    // if the resourcePath resolved to a file path, check that file exists
    if (filePath != nil) {
        // create resourceURL
        resourceURL = [NSURL fileURLWithPath:filePath];
        // try to access file
        NSFileManager* fMgr = [NSFileManager defaultManager];
        if (![fMgr fileExistsAtPath:filePath]) {
            resourceURL = nil;
            NSLog(@"Unknown resource '%@'", resourcePath);
        }
    }

    return resourceURL;
}
michaelkariv commented 8 years ago

I am using cordovaMedia (via its angular wrapper ngCordova) and the problem is the delay. May be it is because there is no preload, like in NativeAudio.

optikalefx commented 8 years ago

I have an easy solution for this for anyone interested. Use CDV urls and then just resolve them to a full path.

export const getFile = function(path) {
    return new Ember.RSVP.Promise( (resolve) => {
        window.resolveLocalFileSystemURL(path, function(entry) {
            resolve(entry);
        });
    });
};

then you can

let path = fileEntry.toURL();
// need a root level path
path = path.replace('file://', '');

then use my fork which eliminates the hardcoded www, so you can play files from anywhere on your device.

https://github.com/optikalefx/cordova-plugin-lowlatencyaudio/commit/7aaf6ce948a697d344efe0e186aa017cf6569bc4
sommerper commented 8 years ago

How has this not been resolved yet? It's pretty important not being restricted to the www/ folder.

optikalefx commented 8 years ago

My fork has this resolved.

RoryPicko commented 7 years ago

simply...

path = path.replace('file://', '');

-- the above worked for ios, android needed a little more

in the folder plugin/cordova-plugin-nativeaudio/src/android

edit line 85 of NativeAudio.java from String fullPath = concat("www"+assetPath); to String fullPath = assetPath;

you will then need to remove the android platform and then re-add it for the plugin to update

cordova platforms rm android cordova platforms add android