To register and login, use your Google, Twitter, Facebook, LinkedIn, or OpenID credentials.

This is allowing us to stop most spam registrations. We've deleted most of the spam accounts that got through, and we're closely watching for more.

PdAudio vs PdService in Android app with long running background service

Nagasaki45Nagasaki45 Posts: 13
edited February 2013 in Pd Everywhere

Hello,

This is my first time for writing here. I'm an Android newbie and making my first steps with libpd. I've read Peter's book (which I think is great) and these days work on an Android app.

The main activity of my app is used solely for starting and stopping a long running service. The service has a broadcast receiver that receive Bluetooth messages and send them to Pd patch. Right now I initiate libpd using PdAudio and everything works fine until the screen is turns off. Then the service continue to run but the audio became very "glitchy". By the way, the service is a foreground service.

I'm not sure in which way to go to solve this problem. The options I have in mind are these: *) From my service bind to PdService as described in peter's book. With this solution I end up with 2 background services, one for the Bluetooth work (and libpd initialization) and one for running libpd, isn't it too much?

*) Extend PdService to have the Bluetooth functionality. In this case the problem is that I need to call functions to PdService from the main activity, which I don't want to use a lot and in fact I can't rely on when the screen turns off.

What do you think?

Thanks, and sorry for my bad English, Tom

Answers

  • pbrinkmannpbrinkmann Posts: 686 ✭✭

    Try this in your onCreate method: getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);

    I don't think there's anything wrong with having more than one foreground service.

  • Thanks for the answer.

    There is no getWindow() function in Service. Calling this function on my MainActivity doesn't help (tried it). I will try to separate the audio service to different service than my Bluetooth work service and see how it's work.

    Tom

  • Hi,

    I've finished the move from PdAudio to PdService. Again, everything works fine except that when the screen turns off the audio become very glitchy. I've made some improvements in my code so now the GC almost never kicks in (and when it is it only for 200k - 300k). My device is Galaxy S3.

    Thanks a lot, Tom

  • pbrinkmannpbrinkmann Posts: 686 ✭✭

    Have you tried wake locks? That might take care of glitches when the screen turns off.

  • Looks good. I will try this and let you know if it work. Thanks again, Tom

  • Hi again,

    I've tried to use wake locks. Using the flag PARTIAL_WAKE_LOCK doesn't help, the problem continues. When using another flags like SCREEN_BRIGHT_WAKE_LOCK the screen remains on and there is no problem until pressing the device shut down button and then the problem repeats again. I really need to be able to lock the device. My app should work when the device is in the users pocket (using headphones). By the way, when the screen turns off Logcat also stops to show new messages from the service.

    I've also tried to change the number of ticks per buffer with different values between 2 and 128 and it doesn't help.

    Tom

  • "By the way, when the screen turns off Logcat also stops to show new messages from the service." --> so your service has stopped? Have you debugged the service lifecycle (by putting in methods where the service starts/stops)?

    It seems to me that your service isn't set up properly to be in the foreground when the screen is off. Can you copy the relevant code here (how you start your service + any service cycle logic you use within the service, eg where you call stopSelf etc)?

  • I've already canceled the foreground to the service. But my service is bound to PdService which is in foreground so I don't think that this is the problem.

    Here is the relevant code:

    @Override
    public void onCreate() {
        super.onCreate();
    
        Log.d(TAG, "Service created");
    
        // Bind to PdService to initialize libpd on working thread
        bindService(new Intent(this, PdService.class), pdConnection,
                BIND_AUTO_CREATE);
    
        // Apply wake lock
        applyWakeLock();
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }
    
    private class InitPdTask extends AsyncTask<Integer, Integer, Integer> {
    
        @Override
        protected Integer doInBackground(Integer... params) {
    
            try {
                // Configure audio glue
                int sampleRate = AudioParameters.suggestSampleRate();
                pdService.initAudio(sampleRate, 0, 2, 64);
    
                // Publish progress to install dispatcher on UI thread
                publishProgress(1);
    
                File dir = getFilesDir();
                IoUtils.extractZipResource(
                        getResources().openRawResource(R.raw.patch), dir, true);
                File patchFile = new File(dir, "ResearchPlayer.pd");
                PdBase.openPatch(patchFile);
            } catch (IOException e) {
                Log.e(TAG, e.toString());
                stopSelf();
            }
    
            // Start audio and bang patch PD_START_MESSAGE receiver
            pdService.startAudio(
                    new Intent(BgService.this, MainActivity.class),
                    R.drawable.ic_launcher, "PD foreground", "Running!");
            PdBase.sendBang(PD_START_MESSAGE);
    
            return null;
        }
    
        @Override
        protected void onProgressUpdate(Integer... values) {
            // onProgressUpdate always called on UI thread
            super.onProgressUpdate(values);
    
            if (values[0] == 1) {
                // Create and install dispatcher
                dispatcher = new PdUiDispatcher();
                PdBase.setReceiver(dispatcher);
            }
        }
    }
    
    public void applyWakeLock() {
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        lock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My wake lock tag");
        lock.acquire();
    }
    
    private final ServiceConnection pdConnection = new ServiceConnection() {
    
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
    
            // Get pdService field and initialize libpd on working thread
            pdService = ((PdService.PdBinder) service).getService();
            new InitPdTask().execute(0);
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG, "PdService connection failed!");
        }
    };
    
    @Override
    public void onDestroy() {
        super.onDestroy();
    
        // Stop Pd audio
        pdService.stopAudio();
    
        // Cleaning up audio resources
        unbindService(pdConnection);
    
        // Remove wake lock
        lock.release();
    
        Log.d(TAG, "Service stopped");
    }
    

    What do you think?

    Thank a lot, Tom

  • pbrinkmannpbrinkmann Posts: 686 ✭✭

    Let's do a quick sanity check: Launch the ScenePlayer app that comes with pd-for-android and let it run until the screen turns off. On my devices, the audio continues and sounds perfectly fine. Let's make sure that ScenePlayer works on your device, too.

  • Nagasaki45Nagasaki45 Posts: 13
    edited February 2013

    ScenePlayer works great. Maybe its because of the Bluetooth searches? As I said earlier, there is also Bluetooth search and receiver that work on these service (on UI thread, but I can't feel that they slowing the UI so I leaved it that's way). I will try to cancel the Bluetooth work and send "dumb" values to Pd instead, just for the try.

    Thanks again, I very appericiate your help, Tom

  • kapsykapsy Posts: 30
    edited February 2013

    Hi Tom, if you haven't already, I highly recommend on a seperate clean project doing a quick log check of the methods that your device calls when the screen turns off. My Galaxy S runs:

    02-11 19:55:17.718: D/Pd Debug(13438): onPause() 1360565717722
    02-11 19:55:17.840: D/Pd Debug(13438): onSaveInstanceState() 1360565717843
    02-11 19:55:17.840: D/Pd Debug(13438): onStop() 1360565717845
    

    But a cheaper no-brand tablet I use runs:

    02-11 20:15:03.119: D/Pd Debug(30308): onPause() 1360566903130
    02-11 20:15:03.179: D/Pd Debug(30308): onSaveInstanceState() 1360566903191
    02-11 20:15:03.179: D/Pd Debug(30308): onStop() 1360566903192
    02-11 20:15:03.419: D/Pd Debug(30308): onDestroy() 1360566903429
    02-11 20:15:03.669: D/Pd Debug(30308): onCreate() 1360566903686
    02-11 20:15:04.399: D/Pd Debug(30308): onStart() 1360566904418
    02-11 20:15:04.409: D/Pd Debug(30308): onRestoreInstanceState() 1360566904427
    02-11 20:15:04.409: D/Pd Debug(30308): onResume() 1360566904427
    02-11 20:15:04.449: D/Pd Debug(30308): onPause() 1360566904463
    

    ...I'm sure the Galaxy S3 won't be that bad, but doing a log check like that solved alot of riddles for me.

  • pbrinkmannpbrinkmann Posts: 686 ✭✭

    @kapsy Wow, that's good to know. I knew that off-brand tablets can be weird, but this kind of behavior seems insane. How does libpd hold up on that thing?

    @Tom Since ScenePlayer behaves correctly, I'll just claim that the problem is outside of libpd ;) That said, though, you should definitely move the Bluetooth activity off the UI thread, even if you aren't currently experiencing any noticeable delays. I tweaked one of the Android sample projects for this purpose; maybe you'll find it useful: https://github.com/nettoyeurny/btmidi/blob/master/AndroidMidi/src/com/noisepages/nettoyeur/bluetooth/BluetoothSppConnection.java

  • kapsykapsy Posts: 30
    edited February 2013

    Hi Peter, without derailing the thread - I went again and did a proper comparison of just about every state I could think of in a separate app, logging all of the results. They are here: https://github.com/Kapsy/ResumeCallTests - in the txt files of the root dir if anyone's interested.

    Long story short, every device from what I can tell has a preset rotation for it's lock screen - if that rotation is different to your app (as it is with the tablet I'm using - it's landscape while my app forces to portrait), and the lock screen is enabled, then that's 10 or so calls are made as above...

    When running the app in landscape, we just get:

    02-19 19:17:58.890: D/Debug(2532): onPause() 1361254678896
    02-19 19:17:58.910: D/Debug(2532): onSaveInstanceState() 1361254678916
    02-19 19:17:58.910: D/Debug(2532): onStop() 1361254678917
    

    When running without a lock screen in portrait we also get the same.

    I also get more or less the same running the Galaxy but inversely - lots of calls when going from the landscape app to the lock screen which is portrait. Of course there's no way of controlling the users phone settings so I just rely on pause and resume... :-?

    So that seems more likely the cause to me than just that it's a cheap tablet. Having said that interesting that the tablet makes calls to start the app on power, while the Galaxy waits till the pass code is entered - meaning that the audio starts while still entering the passcode. It runs libpd ok, it's a good test platform because it is quite slow and it forces me to iron out inefficiencies that I wouldn't notice on say an S3 or Nexus. The graphics I'm running seem about 30% faster when not running as a service - which suited my app anyway.

    /end derail...

  • Nagasaki45Nagasaki45 Posts: 13
    edited February 2013

    Hello everybody,

    @kapsy Didn't tried it yet, but I'm not sure if its relevant. I'm running libpd from background service to be able to exit the main activity. The problem still occurs...

    I've also tried to cancel all the Bluetooth work and remove it totally from the service (for debugging) and again, the glitches are there.

    Here is the full code of the background service:

    package com.nagasaki45.arparty;
    
    // lots of imports...
    
    public class BgService extends Service {
    
    public static final String TAG = "ServiceTag";
    public static final String PD_BLUETOOTH_MESSAGE = "bluetoothMessage";
    public static final String PD_START_MESSAGE = "startMessage";
    
    private PdService pdService;
    private PdUiDispatcher dispatcher;
    
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    
    @Override
    public void onCreate() {
        super.onCreate();
    
        Log.d(TAG, "Service created");
    
        // Bind to PdService to initialize libpd on working thread
        bindService(new Intent(this, PdService.class), pdConnection,
                BIND_AUTO_CREATE);
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    
        Log.d(TAG, "onStartCommand()");
    
        return START_STICKY;
    }
    
    private class InitPdTask extends AsyncTask<Integer, Integer, Integer> {
    
        @Override
        protected Integer doInBackground(Integer... params) {
    
            try {
                // Configure audio glue
                int sampleRate = AudioParameters.suggestSampleRate();
                Log.d(TAG, "SampleRate: " + sampleRate);
                pdService.initAudio(sampleRate, 0, 2, 16);
    
                // Publish progress to install dispatcher on UI thread
                publishProgress(1);
    
                File dir = getFilesDir();
                IoUtils.extractZipResource(
                        getResources().openRawResource(R.raw.patch), dir, true);
                File patchFile = new File(dir, "ResearchPlayer.pd");
                PdBase.openPatch(patchFile);
            } catch (IOException e) {
                Log.e(TAG, e.toString());
                stopSelf();
            }
    
            // Start audio and bang patch PD_START_MESSAGE receiver
            pdService.startAudio(
                    new Intent(BgService.this, MainActivity.class),
                    R.drawable.ic_launcher, "PD foreground", "Running!");
            PdBase.sendBang(PD_START_MESSAGE);
    
            // TODO This lines are just for debugging, remove them
            PdBase.sendMessage(PD_BLUETOOTH_MESSAGE, "00:12:03:09:77:45", -30);
            PdBase.sendMessage(PD_BLUETOOTH_MESSAGE, "00:12:02:10:74:85", -60);
    
            return null;
        }
    
        @Override
        protected void onProgressUpdate(Integer... values) {
            // onProgressUpdate always called on UI thread
            super.onProgressUpdate(values);
    
            if (values[0] == 1) {
                // Create and install dispatcher
                dispatcher = new PdUiDispatcher();
                PdBase.setReceiver(dispatcher);
            }
        }
    }
    
    private final ServiceConnection pdConnection = new ServiceConnection() {
    
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
    
            // Get pdService field and initialize libpd on working thread
            pdService = ((PdService.PdBinder) service).getService();
            new InitPdTask().execute(0);
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG, "PdService connection failed!");
        }
    };
    
    @Override
    public void onDestroy() {
        super.onDestroy();
    
        // Stop Pd audio
        pdService.stopAudio();
    
        // Cleaning up audio resources
        unbindService(pdConnection);
    
        Log.d(TAG, "Service stopped");
    }
    }
    

    Maybe the problem is in the Pd patch itself?

    Thanks a lot for your help, Tom

  • Hello again, I mistakenly mark Peter's answer as accepted and can't find a way to cancel the mark. I still need some help here :)

    Tomorrow I will continue to debug the problem. Going to look at the Pd patch itself...

    Again, I really appreciate all of your help. Thanks a lot, Tom

  • kapsykapsy Posts: 30

    Hi Tom - hmm you're right. I'm just a beginner too, but can't see anything wrong with your code. If it were me, I'd is compiling and running the /pd-for-android/PdTest/ app and see if that gives the same problems. It's not a complex app and it runs as a service... If that works fine then you can go from there - any points of difference in your project might be the cause.

  • Hi,

    @kapsy I tried to run PdTest and it runs fine. I've also changed my Pd patch and run it through PdTest and it also running without problems. Think I'm going to try the next approach: Instead of binding to PdService from the Bluetooth service I will try to initialize Pd and bind to PdService from the main activity.

    Another problem that I find in my code is that when I try to change the values of pdService.initAudio() to (-1, -1, -1, -1) I get a IOException.

    Tom

  • @Nagasaki45 So you bind a service from another service? That is a likely culprit, as your other tests with PdTest run fine. I assume you have 2 services because you think of them as 2 separate "modules", so it's best to keep them decoupled and start them both from within your main activity, and not start one from the other.

    @kapsy @pbrinkmann I test (non audio apps) with many different Android devices in my day job and I also study logcats from users (which are sent to our servers) - not 2 devices behave the same way! It can be quite surprising when seeing the log from a new device actually, as some devices really do weird things and sure enough, 95% of the the bugs happen after a screen has been turned off then on. It is even more visible with services. One of the apps I work on has a sticky service that runs all day long on the taxi driver's phone - there is great variation in how this is handled between devices over long periods, and long running services are a nightmare to debug, hence my advice above to decouple both services.

  • kapsykapsy Posts: 30

    Hi everyone,

    @Nagasaki45 - did freewheelnat's solution work for you? Interested to know, as for my next app was considering designing my next app in this way - not such a good idea by the sounds.

    @freewheelnat - interesting to hear - especially regarding services. Unfortunately Androids port-ability is a real double edged sword. Not even getting into latency or OS version differences here!

  • @kapsy I'm still working to figure out if this solution eliminates the problem completelly. It seems it do. I will come back with final answers in few days...

    Meanwhile, thanks a lot for all of your help!

  • Making some more checks I find that the solution above is fine. But I'm still getting some glitches when starting and stopping Bluetooth searches when the screen is off. Those glitches is relatively minor and not so noticeable, I hope I will manage to work with them...

    Thanks a lot, Tom

Sign In or Register to comment.