Re: App slows down -after- it begins

The place to discuss anything and everything about running your LiveCode on Android

Moderators: FourthWorld, heatherlaine, Klaus, kevinmiller, robinmiller

rumplestiltskin
Posts: 223
Joined: Wed Jun 21, 2006 7:33 pm
Contact:

Re: App slows down -after- it begins

Post by rumplestiltskin » Tue Oct 28, 2014 3:56 am

I have a simple metronome app I'm fooling with in order to get the basics of making an Android app actually work. My app works fine except that, once the app begins to work, it slows down. I'm just playing one of two sounds. Here's part of my code:

Code: Select all

on bounce3
   put 60 / field "bpm" into theTime
  set the loc of image "sphere.png" to 50,285
    if the label of button "cd1" is "X" then
      play specialFolderPath ("engine") & "/Click.wav"
   else 
      play specialFolderPath ("engine") & "/ClickSoft.wav"
   end if
   wait theTime sec with messages -- MUST have "with messages" in order to register a tap on this button to stop
   set the loc of image "sphere.png" to (the width of this stack - 50)/2,285
   if the label of button "cd2" is "X" then
      play specialFolderPath ("engine") & "/Click.wav"
   else 
      play specialFolderPath ("engine") & "/ClickSoft.wav"
   end if
   wait theTime sec with messages
   set the loc of image "sphere.png" to (the width of this stack - 50),285
   if the label of button "cd3" is "X" then
      play specialFolderPath ("engine") & "/Click.wav"
   else 
      play specialFolderPath ("engine") & "/ClickSoft.wav"
   end if
   wait theTime sec with messages
   if the short name of me is "Stop" then send bounce3 to me
end bounce3
There are three buttons that get (or lose) a label "X" when tapped. This tells the app whether to consider that beat a "downbeat" (which plays a hard "click" sound" or a normal beat (which plays a soft "click" sound). The user has entered a "beats per minute" number. When the button in which the code resides is tapped, the code runs. The code is recursive so the three beats repeat. I do have add'l code (which I have not shown in this post) which changes the name of the button from "Start" to "Stop" (and begins the operation) and then, when the user taps on it once more, the name changes back to "Start" which stops the recursion.

The problem I am encountering is that the speed of the clicks starts out at the user-specified beats per minute but, within a measure or two, begins to slow down maybe 20% or so. If I don't include the "with messages" then there's no way to stop the metronome. On my Mac, there is no apparent slowdown when running in the IDE or in a standalone OSX app.

I'm running the app directly on my Nexus 5 phone without tethering. The phone is running KitKat 4.4.4. The standalone app was made for Android 3.1 (the highest available given how I installed the SDK, I guess). I'm running LC7 on OSX 10.9.5 (Mavericks). The "click" and "clicksoft" .wav sounds are 9kb and 25KB and are .05 and .16 seconds, respectively; so these are tiny sounds that are quite short in length. (I may have some slight inaccuracy due to the addition of the audio files in the beats per minute but I can work these out mathematically. My issue here is the unexplained slowdown. It I stop the run and the start it again, it begins at the normal speed and slows down after a few measures. (Garbage collection?)

Suggestions are always welcomed.

Thanks,
Barry
Last edited by rumplestiltskin on Wed Nov 12, 2014 7:00 am, edited 1 time in total.

Ledigimate
Livecode Opensource Backer
Livecode Opensource Backer
Posts: 132
Joined: Mon Jan 14, 2013 3:37 pm

Re: App slows down it begins

Post by Ledigimate » Tue Oct 28, 2014 8:42 am

You can subdivide bounce3 into three seperate handlers. Consider the following code:

Code: Select all

on bounce3
   put 60 / field "bpm" into theTime
   playBeat1 theTime
end bounce3

on playBeat1 theTime
   set the loc of image "sphere.png" to 50,285
   if the label of button "cd1" is "X" then
      play specialFolderPath ("engine") & "/Click.wav"
   else
      play specialFolderPath ("engine") & "/ClickSoft.wav"
   end if
   if the short name of me is "Stop" then send "playBeat2 theTime" to me in theTime seconds
end playBeat1

on playBeat2 theTime
   set the loc of image "sphere.png" to (the width of this stack - 50)/2,285
   if the label of button "cd2" is "X" then
      play specialFolderPath ("engine") & "/Click.wav"
   else
      play specialFolderPath ("engine") & "/ClickSoft.wav"
   end if
   if the short name of me is "Stop" then send "playBeat3 theTime" to me in theTime seconds
end playBeat2

on playBeat3 theTime
   set the loc of image "sphere.png" to (the width of this stack - 50),285
   if the label of button "cd3" is "X" then
      play specialFolderPath ("engine") & "/Click.wav"
   else
      play specialFolderPath ("engine") & "/ClickSoft.wav"
   end if
   if the short name of me is "Stop" then send "playBeat1 theTime" to me in theTime seconds
end playBeat3
* EDIT:
Also, the play command is not supported on some Android devices. Consider using the mobilePlaySoundOnChannel function, e.g.

Code: Select all

mobilePlaySoundOnChannel specialFolderPath ("engine") & "/Click.wav", "current", "now"
010100000110010101100001011000110110010100111101010011000110111101110110011001010010101101010100011100100111010101110100011010000010101101001010011101010111001101110100011010010110001101100101

rumplestiltskin
Posts: 223
Joined: Wed Jun 21, 2006 7:33 pm
Contact:

Re: App slows down -after- it begins

Post by rumplestiltskin » Tue Oct 28, 2014 9:51 pm

Please excuse the mis-naming of the topic; somehow I left out the word -after-.

Your advice about using mobilePlaySoundOnChannel instead of play brings up a question. When looking at the dictionary entry for mobilePlaySoundOnChannel, I see this:

When queuing a sound using next, the engine will 'pre-prepare' the sound long before the current sound is played, this ensures minimal latency between the current sound ending and the next one beginning.

I can't use "next" as there is a timing (beats per minute) that won't permit using "next" but this "pre-prepare" business has me thinking that something in my code is clogging things up. As a test, I set the beats per minute to 60 and started the metronome running. Within about 8 seconds it became obvious that the clock on the wall (with a ticking second hand) was moving ahead of the app's timing.

Then I decided to take the length of each sound into the calculations so, with 60 beats per minute (1 beat per second) I played the sound (which took .15 seconds) and had the script subtract that from the 1 second so the app only waited .85 second. The same problem manifested itself and became noticeable after about 8 seconds.

So is it simply the overhead of LiveCode's engine that's causing the problem?

jacque
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 7393
Joined: Sat Apr 08, 2006 8:31 pm
Contact:

Re: App slows down -after- it begins

Post by jacque » Wed Oct 29, 2014 7:55 pm

rumplestiltskin wrote:So is it simply the overhead of LiveCode's engine that's causing the problem?
That and more, there is also the background processes of the OS going on. Apple (at least) warns not to use the ticks for things that require exact timings because they can be delayed by OS operations. So that, combined with the time required to execute the lines of code in the script, can cause delays.

Here is a way to calculate a timer to the exact second (or as close to exact as we can get):

Code: Select all

send "setTime" to me in (1 - (the long seconds mod 1)) seconds
This comes from Geoff Canyon's excellent clock script. You can modify it to send the message in 3 seconds or whatever you need. As it is now, it calculates the amount of time up to the next second and uses that figure to trigger the next message.
Jacqueline Landman Gay | jacque at hyperactivesw dot com
HyperActive Software | http://www.hyperactivesw.com

rumplestiltskin
Posts: 223
Joined: Wed Jun 21, 2006 7:33 pm
Contact:

Re: App slows down -after- it begins

Post by rumplestiltskin » Wed Nov 12, 2014 7:00 am

The "send in time" helps somewhat but my app is still slowing down after a few cycles. I only have two different sounds I'm playing; both are very short (around .10 and .15 sec) and only one is played at a time; which one plays is dependent upon a user choice at the beginning of the "run"; the sounds may alternate but only one plays at a time. I have a button that turns off the sound (programmatically) and, in this case, there is no slowdown. So I see three possibilities:

1. The PLAY command is too processor intensive on Android for repeated audio clips that require fairly precise timing. (320 audio clips per minute is a possibility.)
2. The .wav files are too "rich" (or are they too compressed??). They are 16bit but what would be an alternative? Uncompressed audio?
3. Maybe some sort of pre-loading is possible so the audio files do not have to be read from storage repeatedly? (Or does Android cache these small audio files and this line item is moot?)

I was looking for a built-in "click" or "beep" sound but the dictionary indicates the Notification sound is what plays when I use the BEEP command and that won't work well as it's way too long.

If I have to live with the slow-down, it occurs after the third or fourth cycle so, after that, at least the audio spacing is consistent (albeit slower than the user setting).

Suggestions are welcome. By the way, everything else about my app is working perfectly. I even managed to get a scrolling field working well but with a technique that bypasses the need to create a mobileScroller. No inertia but it's w-a-y easier than what the LC documentation recommends.

Barry

EDIT: I'm looking at the mobilePlaySoundOnChannel command and it would be very difficult to know when to preload which sound but it appears I may use more than one channel. If so, would something like this work:

-- PRELOAD AUDIO IN PRE-OPEN STACK
mobilePlaySoundOnChannel "Click.wav", "channelOne", "next"
mobilePlaySoundOnChannel "ClickSoft.wav", "channelTwo", "next"

Then, when it's time to play the audio
--code here to determine which audio file to play
mobilePlaySoundOnChannel "Click.wav", "channelOne", "now"
(or the other file on channelTwo if that's the proper file to play)

If I'm reading the dictionary correctly, the files remain pre-loaded and won't have to be read again. Verdad?

Thanks

rumplestiltskin
Posts: 223
Joined: Wed Jun 21, 2006 7:33 pm
Contact:

Re: App slows down -after- it begins

Post by rumplestiltskin » Wed Nov 12, 2014 5:01 pm

Oops; Looks like I muffed the parameters. Probably should be this:

-- PRELOAD AUDIO IN PRE-OPEN STACK
mobilePlaySoundOnChannel specialFolderPath ("engine") & "/Click.wav" , "channelOne" , "next"
mobilePlaySoundOnChannel specialFolderPath ("engine") & "/ClickSoft.wav" , "channelTwo" , "next"

Then, when it's time to play the audio
--code here to determine which audio file to play
mobilePlaySoundOnChannel specialFolderPath ("engine") & "/Click.wav" , "channelOne" , "now"
(or the other file on channelTwo if that's the proper file to play)

(But, when I tried this, the app stops prior to playing any sound. It's not frozen as I can click the "stop" button in the app, turn off the sound with a button, and run the cycle again when it all works fine (but without sound). Do I need to use an mp3 file? My audio has been ".wav" up to this point.

rumplestiltskin
Posts: 223
Joined: Wed Jun 21, 2006 7:33 pm
Contact:

Re: App slows down -after- it begins

Post by rumplestiltskin » Wed Nov 12, 2014 6:42 pm

D-oh! I goofed up a line of code. Not sure why it would have stopped the app but it did. I fixed the line and now it works. But even the mobilePlaySoundOnChannel command (with each sound cached and playing in its own channel according to what I gather from the dictionary) results in the same slowdown.

The next step is to see if I can make the sounds shorter and smaller (less "rich") in some manner (lower bitrate, etc.) but I'm beginning to suspect that, in spite of decent hardware (I'm using a Nexus 5 that, in all other respects, seems to be a fine device), rapidly playing short audio clips requiring exact timing may be beyond AndroidOS's capability.

I'll report back once I try to use smaller sounds (or maybe uncompressed sounds).

Barry

jacque
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 7393
Joined: Sat Apr 08, 2006 8:31 pm
Contact:

Re: App slows down -after- it begins

Post by jacque » Wed Nov 12, 2014 7:40 pm

Did you alter your "send" command to calculate to the next interval as I posted above? I don't think the problem is in the sound playback, I think it's with script execution and OS overhead. Try altering the amount of time the "send" command waits to account for that, by calculating the long seconds until the next time interval.
Jacqueline Landman Gay | jacque at hyperactivesw dot com
HyperActive Software | http://www.hyperactivesw.com

rumplestiltskin
Posts: 223
Joined: Wed Jun 21, 2006 7:33 pm
Contact:

Re: App slows down -after- it begins

Post by rumplestiltskin » Wed Nov 12, 2014 8:43 pm

jacque wrote:Did you alter your "send" command to calculate to the next interval as I posted above? I don't think the problem is in the sound playback, I think it's with script execution and OS overhead. Try altering the amount of time the "send" command waits to account for that, by calculating the long seconds until the next time interval.
Probably not as you advised. Here's my code as it relates to the audio:

Code: Select all

on playSound
   global tSoundPath1, tSoundPath2
   if the label of button "Sound" is "Sound On" then exit playSound
   if the label of button ("cd"& field "Counter") is "X" then
      mobilePlaySoundOnChannel tSoundPath1 , "channelOne" , "now"
   else 
      mobilePlaySoundOnChannel tSoundPath2 , "channelTwo" , "now"
   end if
end playSound
...and here's the code that calls that handler:

Code: Select all

on bounceOne
   set the loc of graphic "sphere" to theSphereWidth/2,260 -- always the first location
   put 1 into field "Counter"
   playSound
   send bounceTwo to me in theTime sec
end bounceOne
the playSound handler may be called as many as 350 times per minute but, even at 80 times per minute, the slowdown occurs after around three cycles (a "cycle" being anywhere from three to nine playSound handlers being executed). So, if we used 60 times per minute (1/second), a "cycle" would consist of anywhere from 3 to 9 playSound handlers; and after three cycles (more or less), you can detect the number of handlers (audio clips and the "bounce..." handlers) slowing down.

There are nine "bounce" handlers (bounceOne through bounceNine) that are called using the "send" command. This seems to work perfectly (with no sound playing) but, once I turn the sound back on, the delays begin after about three cycles.

Do you suggest using something like this:

Code: Select all

on bounceOne
   set the loc of graphic "sphere" to theSphereWidth/2,260 -- always the first location
   put 1 into field "Counter"
   send playSound to me -- NOTE THE CHANGE IN THIS LINE
   send bounceTwo to me in theTime sec
end bounceOne
According to the dictionary, leaving off the "in time" part of the command sends it immediately. Not sure how that would affect the synchronization of the movement with the playing of the audio.

rumplestiltskin
Posts: 223
Joined: Wed Jun 21, 2006 7:33 pm
Contact:

Re: App slows down -after- it begins

Post by rumplestiltskin » Wed Nov 12, 2014 9:25 pm

Well, that didn't work, either. I changed calling the playSound handler directly with

Code: Select all

send playSound to me
...in each of the "bounce..." handlers
Still experiencing the delay.

I'll offer what may (or may not) be a clarification: I am chaining from bounceOne to bounceTwo to bounceThree. At that point, the app checks to see what number of "bounces" (so to speak) are required based upon what the user has spec'd. In each succeeding "bounce..." handler, it checks whether we've reached the max# and, should it not be that max#, it continues the chain; otherwise, it stops calling add'l "bounce..." handlers, returns to the initial handler in the button, and cleans up (setting button name from "Stop" to "Start", enabling other buttons, etc.).

I even tried setting all the checkboxes (that select which of the two sounds are played) all to one sound. No improvement.

One thing I can point to which I believe to be a good thing: Even though there's the slowdown, the movement of the graphic and the playing of the audio always remain in sync.

And, as a reminder, when I turn off the audio, the movement of the graphic always stays at the set speed.

jacque
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 7393
Joined: Sat Apr 08, 2006 8:31 pm
Contact:

Re: App slows down -after- it begins

Post by jacque » Wed Nov 12, 2014 10:50 pm

Both "send playsound to me" and simply "playsound" will play the sound immediately. You don't need the "send" command unless you want to wait for a period of time before the message is sent, or unless the command lives in a different script.

What your script needs to do is subtract the amount of time the script execution takes from the target time when you want to play the next sound. That's what the line of code I posted does. It uses the long seconds (which includes a very small fractional second) to find out how much time is left until the next exact second occurs. Then it subtracts that from the target time, which gives a fractional number of seconds, and uses that for the "wait" command. This is about as accurate as we can get in LC, and unless your handlers take more than a second to execute it should work.

If your sound is short enough, I'm not sure you need to mess with sound channels, though it won't hurt. Once a sound is playing it shouldn't affect the remainder of the script execution. I don't think it's the sound itself that's the problem, it's the fixed wait time between playbacks.

So your script needs to use the calculation to find out how much time is left until the next targeted second and use that number for the "send" command that plays the sound. In other words, the variable theTime is a target timeframe; subtract the fractional calculation from that. Fiddle around with something like this:

Code: Select all

put (1 - (the long seconds mod 1)) into tFraction
send "playSound" to me in theTime - tFraction seconds
Edit: You'll probably need to calculate the time differential after the current sound has loaded, since that takes some time to execute.
Jacqueline Landman Gay | jacque at hyperactivesw dot com
HyperActive Software | http://www.hyperactivesw.com

rumplestiltskin
Posts: 223
Joined: Wed Jun 21, 2006 7:33 pm
Contact:

Re: App slows down -after- it begins

Post by rumplestiltskin » Thu Nov 13, 2014 12:57 am

Jacque,

I'm a bit confused about your advice. Here's why: The two sounds I wish to play (and I don't know which one the user will select until he clicks the "go" button) are very short, on the order of 1/10 of a second. If my handlers -don't- play any sounds but just change the loc of the graphic to various places around on screen, there is no delay. If the user has spec'd 240 movements per minute (but it could be less), that's cycling through 3, 4, 5, 7 or 9 locations (the user specs this prior to tapping the "go" button") but, still, 240 locations per minute. Divide that up and that's 4 locations per second or .25 second per location. So now let's turn the sound on. If the sound is .10 second long, how does a "send" command benefit the program? If the sound is shorter than the pause between changes in location, where is the delay? Certainly not from the sound, right? I'm not using "wait until the sound is done" because it isn't necessary (nor desired). With the mobilePlaySoundOnChannel "mySound" , "channelOne" , "now" command, there should be no waiting.

If my script (and program logic) calls for the playing of the sound immediately before the movement of the graphic, why do I need to script a delay into the call of that playSound handler?

Maybe I need to schedule the -next- handler (bounceTwo, for example) before calling the playSound handler? In this case, bounceOne calls bounceTwo with the send bouceTwo to me in {whatever number} sec and the next line of the bounceOne handler calls the playSound handler immediately?

jacque
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 7393
Joined: Sat Apr 08, 2006 8:31 pm
Contact:

Re: App slows down -after- it begins

Post by jacque » Thu Nov 13, 2014 9:52 pm

If my script (and program logic) calls for the playing of the sound immediately before the movement of the graphic, why do I need to script a delay into the call of that playSound handler?
It isn't a delay, it's a reduction. I should mention that I haven't done actual benchmarking so I'm partly guessing, but accounting for CPU overhead is the first thing I'd try if I were having this problem.

There is a difference between moving an image, which requires only a single screen redraw of already-cached data, and playing a sound, which requires more CPU overhead. In this case the file is loading from disk for each execution which takes some additional time. After reading the disk to get the file into RAM, the engine has to set up the sound playback mechanism (more time,) load the data into it, and then start it playing. LC is very fast, so all this will only take a few milliseconds, but after repeating that action many times the delays will accumulate. That sounds like what you're seeing -- things work okay for a few iterations and then start to bog down.

The delay occurs in the playSound handler because that's where all the file loading and playback action happens. One would think that using a sound channel to preload the sound would eliminate some of the problem because the file would already be retrieved and loaded. It does prevent gaps when playing sounds back to back, because the next one is already ready to go. But no matter when the preloading happens, there will be a brief delay while it executes.

For the sake of argument, let's say that loading a sound takes 30 milliseconds, which is imperceptible by itself. Here is what I think may be happening:

TIMER STARTS AT THEORETICAL ZERO
First playsound executes -- 30 ms elapse
Script schedules playSound in 1 second -- timer scheduled for 1030 ms from 0:0
Second playsound executes (30 ms late) -- takes 30 ms
Schedule playSound in 1 second -- timer scheduled for 2060 ms from 0:0

This continues until the elapsed time from the start of the timer gets to be about half a second, which becomes noticeable. Each new iteration adds 30 ms to the elapsed time until it's way off.

That's the theory, anyway. The fix:

TIMER STARTS AT THEORETICAL ZERO
First playsound executes -- 30 ms elapse
Schedule playSound in 1 second minus 30 ms -- timer scheduled for 1000 ms from 0:0
Second playsound executes -- takes 30 ms
Schedule playSound in 1 second minus 30 ms -- timer scheduled for 2000 ms from 0:0

This eliminates any potential time creep by forcing the timer to play at exactly the targeted time interval.
Jacqueline Landman Gay | jacque at hyperactivesw dot com
HyperActive Software | http://www.hyperactivesw.com

rumplestiltskin
Posts: 223
Joined: Wed Jun 21, 2006 7:33 pm
Contact:

Re: App slows down -after- it begins

Post by rumplestiltskin » Thu Nov 13, 2014 11:05 pm

Jacque,

Thanks for your explanation. I'll give your suggestion a shot (assuming I can work out the math).

Without the exact math coming into it, I think you're suggesting this:

Code: Select all

on bounceOne
   set the loc of graphic "sphere" to theSphereWidth/2,260 -- always the first location
   put 1 into field "Counter"
   send playSound to me in theTime sec -- theTime is calculated as 60 / number of beats per minute
   send bounceTwo to me in theTime sec
end bounceOne

Code: Select all

on playSound
  figure out which sound to play and play it immediately
end playSound
If I don't allow for the length of time the sound plays, does it really matter? I'd never have a short enough time where the subsequent sound causes the truncation of the prior sound. (Or have I missed your point entirely?)

Thanks,
Barry

Edit: bounceTwo through bounceNine contain essentially the same code as bounceOne except they increment field "Counter", change the location of the graphic, and determine if the end of the cycle has been reached (at 3, 4, 5, 7 or 9).

jacque
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 7393
Joined: Sat Apr 08, 2006 8:31 pm
Contact:

Re: App slows down -after- it begins

Post by jacque » Fri Nov 14, 2014 1:11 am

You were on the right track here:
Then I decided to take the length of each sound into the calculations so, with 60 beats per minute (1 beat per second) I played the sound (which took .15 seconds) and had the script subtract that from the 1 second so the app only waited .85 second. The same problem manifested itself and became noticeable after about 8 seconds.
But the length of the sound is immaterial, once it starts playing there is no more delay. The delay, as you thought, is in the engine overhead when prepping an audio file for playback. So what you need to subtract is not the length of the sound, but the elapsed time the overhead uses.

Just for simplicity, see if your original handler runs any better with a few changes:

Code: Select all

on bounce3
  put 60 / field "bpm" into theTime
  set the loc of image "sphere.png" to 50,285
  if the label of button "cd1" is "X" then
    play specialFolderPath ("engine") & "/Click.wav"
  else 
    play specialFolderPath ("engine") & "/ClickSoft.wav"
  end if
  put (1 - (the long seconds mod 1)) into tFraction
  wait (theTime - tFraction) sec with messages
  set the loc of image "sphere.png" to (the width of this stack - 50)/2,285
  if the label of button "cd2" is "X" then
    play specialFolderPath ("engine") & "/Click.wav"
  else 
    play specialFolderPath ("engine") & "/ClickSoft.wav"
  end if
  put (1 - (the long seconds mod 1)) into tFraction
  wait (theTime - tFraction) sec with messages
  set the loc of image "sphere.png" to (the width of this stack - 50),285
  if the label of button "cd3" is "X" then
    play specialFolderPath ("engine") & "/Click.wav"
  else 
    play specialFolderPath ("engine") & "/ClickSoft.wav"
  end if
  put (1 - (the long seconds mod 1)) into tFraction
  wait (theTime - tFraction) sec with messages
  if the short name of me is "Stop" then send bounce3 to me in 0
end bounce3
I don't have a way to easily test this myself right now, so let us know how it goes. The fractional calculation may need adjustment. If the engine overhead takes longer than the interval between beats per minute then there may not be a way to play the sounds fast enough with this method.

Edit: I added "with 0" to the last line to avoid too much nesting and a possible overflow. Adding a wait time of zero (or even -1) sends the message immediately but still allows the handler to exit normally.
Jacqueline Landman Gay | jacque at hyperactivesw dot com
HyperActive Software | http://www.hyperactivesw.com

Post Reply