4

I am building an app with extensive audio requirements, so I am using SoundJS for that, and I am compiling the app using phonegap.

I am using the mobile safe approach to build a soundJS app. It seems that there are different audio plugins, including a special Cordova audio plugin. So, I am not able to register any of these plugins on the compiled app. This is because registering any plugin requires to check if this plugin is supported or not. In case of the cordova, the method isSupported checks for the following:

if (s._capabilities != null || !(window.cordova || window.PhoneGap || window.phonegap) || !window.Media) { return;}

This means when the app is compiled, there is no global variable called window.cordova or phonegap and no global variable called window.media (I think this is the media plugin that needs to be installed to get soundjs to work, and I have added it to the config.xml that I'm using for phonegap build.

So the question is, how to investigate what is wrong, how to know if for example the media plugin is not installed properly (all from the javascript variables that we can use, as I am not able to use any other debugging), or is it the case that when I compile using phonegap build there is no variables for cordova or phonegap.. can we list all global variables to see which ones are used?

Edit Thanks Jesse for drawing my attention to these points about phonegap, so I built a small app just to test the deviceready event, but for some reason it still doesn't work when compiled by phonegap build:

<!DOCTYPE html>
<html>
  <head>
    <title>Cordova Device Ready Example</title>

    <script type="text/javascript" charset="utf-8" src="js/soundjs-NEXT.min.js"></script>
    <script type="text/javascript" charset="utf-8" src="js/cordovaaudioplugin-NEXT.min.js"></script>
    <script type="text/javascript" charset="utf-8">

    // Call onDeviceReady when Cordova is loaded.
    //
    // At this point, the document has loaded but cordova-2.3.0.js has not.
    // When Cordova is loaded and talking with the native device,
    // it will call the event `deviceready`.
    //
    function onLoad() {
        document.getElementById("doc_loaded").innerHTML="Document Loaded"
        document.addEventListener("deviceready", onDeviceReady, false);
    }

    // Cordova is loaded and it is now safe to make calls Cordova methods
    //
    function onDeviceReady() {
        // Now safe to use the Cordova API\
        document.getElementById("device_loaded").innerHTML="Device Loaded"

        if (window.cordova || window.PhoneGap || window.phonegap){ 
            document.getElementById("phonegap_loaded").innerHTML="Phonegap Loaded"
        }
        if (window.Media){
            document.getElementById("media_loaded").innerHTML="Media Loaded"
        }

    }

    </script>
  </head>
  <body onload="onLoad()">
  Hello Hello, testing phonegap deviceready
  <div id="doc_loaded">Loading Doc</div>
  <div id="device_loaded">Loading Device</div>
  <div id="phonegap_loaded">Detecting Phonegap</div>
  <div id="media_loaded">Detecting Media</div>
  </body>
</html>

Can you please help me locate where can the problem be?

EDIT2 I figured out that the deviceready was not working because I didn't add cordova:

<script type="text/javascript" src="cordova.js"></script>

So, when I did, I was able to initialize the cordova audio plugin. However, I am still unable to play sound, despite using mobile safe approach:

(this code is hosted on arbsq.net/h6/)

<!DOCTYPE html>
<html>
<head>
    <title>SoundJS: Mobile Safe</title>

    <link href="css/shared.css" rel="stylesheet" type="text/css"/>
    <link href="css/examples.css" rel="stylesheet" type="text/css"/>
    <link href="css/soundjs.css" rel="stylesheet" type="text/css"/>
    <script src="js/examples.js"></script>
</head>

<body onload="loading_doc()">

<header  class="SoundJS">
    <h1>Mobile Safe Play</h1>

    <p>This example registers and plays a sound with SoundJS in a way that will
        work on mobile devices.</p>
</header>

<div class="content" id="content" style="height: auto">
    <p id="status">Hello World.</p>
</div>

<div id="error">
    <h2>Sorry!</h2>

    <p>SoundJS is not currently supported in your browser.</p>

    <p>Please <a href="http://github.com/CreateJS/SoundJS/issues" target="_blank">log a bug</a>
        with the device and browser you are using. Thank you.</p>
</div>

<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" charset="utf-8" src="js/soundjs-NEXT.min.js"></script>
<script type="text/javascript" charset="utf-8" src="js/cordovaaudioplugin-NEXT.min.js"></script>


<!-- We also provide hosted minified versions of all CreateJS libraries.
    http://code.createjs.com -->

<script id="editable">
    var displayMessage;     // the HTML element we use to display messages to the user
    this.myNameSpace = this.myNameSpace || {};
    function loading_doc() {
         if(( /(ipad|iphone|ipod|android|windows phone)/i.test(navigator.userAgent) )) {
            document.addEventListener('deviceready', init, false);
        } else {
            init();
        }
    }

    function init() {
        // store this off because walking the DOM to get the reference is expensive
        displayMessage = document.getElementById("status");

        // if this is on mobile, sounds need to be played inside of a touch event
        if (createjs.BrowserDetect.isIOS || createjs.BrowserDetect.isAndroid || createjs.BrowserDetect.isBlackberry || createjs.BrowserDetect.isWindowPhone) {
            //document.addEventListener("click", handleTouch, false);   // works on Android, does not work on iOS
            displayMessage.addEventListener("click", handleTouch, false);   // works on Android and iPad
            displayMessage.innerHTML = "Touch to Start";
        }
        else {
            handleTouch(null);
        }
    }

    // launch the app inside of this scope
    function handleTouch(event) {
        displayMessage.removeEventListener("click", handleTouch, false);
        // launch the app by creating it
        var thisApp = new myNameSpace.MyApp();
    }

    // create a namespace for the application


    // this is a function closure
    (function () {
        // the application
        function MyApp() {
            this.init();
        }

        MyApp.prototype = {
            src: null,            // the audio src we are trying to play
            soundInstance: null,  // the soundInstance returned by Sound when we create or play a src
            displayStatus: null,  // the HTML element we use to display messages to the user
            loadProxy: null,

            init: function () {
                // store the DOM element so we do repeatedly pay the cost to look it up
                this.displayStatus = document.getElementById("status");

                // this does two things, it initializes the default plugins, and if that fails the if statement triggers and we display an error
                // NOTE that WebAudioPlugin plays an empty sound when initialized, which activates web audio on iOS if played inside of a function with a touch event in its callstack
                if (!createjs.Sound.initializeDefaultPlugins()) {
                    document.getElementById("error").style.display = "block";
                    document.getElementById("content").style.display = "none";
                    return;
                }

                // Create a single item to load.
                var assetsPath = "audio/";
                this.src = assetsPath + "M-GameBG.ogg";

                this.displayStatus.innerHTML = "Waiting for load to complete.";  // let the user know what's happening
                // NOTE createjs.proxy is used to apply scope so we stay within the touch scope, allowing sound to play on mobile devices
                this.loadProxy = createjs.proxy(this.handleLoad, this);
                createjs.Sound.alternateExtensions = ["mp3"];   // add other extensions to try loading if the src file extension is not supported
                createjs.Sound.addEventListener("fileload", this.loadProxy); // add event listener for when load is completed.
                createjs.Sound.registerSound(this.src);  // register sound, which preloads by default

                return this;
            },

            // play a sound inside
            handleLoad: function (event) {
                this.soundInstance = createjs.Sound.play(event.src);    // start playback and store the soundInstance we are currently playing
                this.displayStatus.innerHTML = "Playing source: " + event.src;  // let the user know what we are playing
                createjs.Sound.removeEventListener("fileload", this.loadProxy); // we only load 1 sound, so remove the listener
            }
        }

        // add MyApp to myNameSpace
        myNameSpace.MyApp = MyApp;
    }());

</script>

</body>
</html>
hmghaly
  • 1,411
  • 3
  • 29
  • 47
  • Please see this link http://stackoverflow.com/questions/22515699/play-sound-on-phonegap-app-for-android – Sunil Sep 24 '15 at 13:40
  • @hmghaly Do you know are your libraries use just HTML5 audio for the playing the source? – MysterX Sep 28 '15 at 14:44
  • @MysterX probably yes – hmghaly Sep 28 '15 at 14:49
  • Unfortunately, if it is so, you can not play the HTML5-audio on the mobile platform without user action. It is restricted by safety policies. Try to look on the mobile hardware solution and play `mediaObject`, provided by it. I used it couple of monts ago on the one of my projects. https://github.com/apache/cordova-plugin-media It was an training application and I must played different sounds(.mp3 & .ogg) on each action tap) – MysterX Sep 28 '15 at 15:00

3 Answers3

2

@hmghaly,
the general method for checking for the availability of Phonegap is to use the 'deviceready' event that Cordova/Phonegap provide. In addition, it is required that you wait until this event completes.

You will want to read #4 of this article FAQ:
Top Mistakes by Developers new to Cordova/Phonegap

I will quote the important part from the documentation (which your should read):

This is a very important event that every Cordova application should use.

Cordova consists of two code bases: native and JavaScript. While the native code is loading, a custom loading image is displayed. However, JavaScript is only loaded once the DOM loads. This means your web application could, potentially, call a Cordova JavaScript function before it is loaded.

The Cordova deviceready event fires once Cordova has fully loaded. After the device has fired, you can safely make calls to Cordova function.

The documentation includes code examples that would relevant to your particular mobile device and platform.

Best of Luck

  • Hi @jesseMonroy650, thanks a lot to these tips about phonegap. So I tried to test this point about deviceready, can you please check the code in the edited part of the original post as it is not working when compiled by phonegap build? – hmghaly Sep 20 '15 at 11:47
  • Hi, it seems that we were just missing cordova, but even after added, there is still a problem, can you please check edit2 and the bounty statement? – hmghaly Sep 22 '15 at 08:45
1

Whilst it is not a complete answer I am currently working through the exact same problem and it was breaking at the exact same point.

if (s._capabilities != null || !(window.cordova || window.PhoneGap || window.phonegap) || !window.Media) { return;}

After you have ensured cordova is installed the next big thing is to ensure you actually have the cordova-plugin-media installed. The !window.Media bit in the line above. Sounds easy but if you simply add the plugin and build without reading all the output you can come unstuck.

The media plugin requires cordova version > 5.0 . The problem is that cordova is pinned at version 4.1.1 - at least mine was despite repeated total removal of cordova - several times via npm and manual total deletion of all npm caches.

Cordova is hard wired internally to install a particular version unless you tell it not to.

So make sure you are using

cordova platform add android@5.X.X

as appropriate to your version not just a plain old

cordova platform add android (BAD)

which will install the pinned version

If you do the latter cordova will happily build with version 4.1.1 despite the cli command

cordova -v

reporting you are on later version - in my case 5.4.1

It will then hit the plugin step - decide the environment is not appropriate for your plugin - spits out a warning and merrily continues with the build - minus the media plugin. Everything else will seem to work - the app will run and unless you dig into it you won't notice you are on an old version of cordova.

Note: they have just released a new version which moves the pinned version forward - so if you update to the latest version - you should be fine.

New Cordova Version Released

Brenton Thomas
  • 618
  • 1
  • 7
  • 17
1

If you are using SoundJS 0.6.2, then you don't have to include the MobileSafe code. Refer Official Doc

The problem I was facing from quite a long time was the local sound files were not loading successfully in iOS.

What I found: Latest iOS uses WKWebView. It appears to treat local files as if they came from a remote server, even though they're in the app itself, and such requests are blocked. Reference Source

Finally after lot of debugging and logging, the following solution worked for me:

  1. Add the Corodova file plugin.

    cordova plugin add cordova-plugin-file

  2. Change the local file path to this:

    cdvfile://localhost/bundle/www/you_folder_name/file_name.mp3

sumitb.mdi
  • 1,010
  • 14
  • 17