Player causes runtime error with some sources

playing, pausing, and stopping on some sources causes a runtime error.

https://xeno-canto.org/690951/download does not cause a runtime error.

https://xeno-canto.org/802220/download causes a runtime error.

After setting the source, fire in sequence play, pause, stop. After that sequence, the player will not play first source. player.stop after this sequence results in a runtime error.

Code uses player.isplaying to determine if play/pause button fires pause or start.


Demo code MIT App Inventor Gallery

Actual code contains catches for player and screen error. Neither trap anything.

Android 12

Suggestions?

You need to introduce a clock timer to allow for larger files to download, and be accepted as a source before trying to play them. Your first file is @800kb, your second file is @4mb. Or resize your larger files.

Thanks. If I understand you correctly there may be a download completed event that can be caught or a cancel action to abandon the in-progress download? Does that involve some file download logic outside the use of http source URLs and setting the source to a downloaded local file? Using timers is dicey at best.

Brainstorm. I discovered that resetting the source seems to clean up the issue. Just stopping without resetting the source breaks things. Stopping, then setting player.source again gets rid of the problem. Weird, but good. Seems like the implementation under the covers does something extra when the source is set. I am hoping no one optimizes for repeating a source string;-)

The safe way to stop the player when the source is over the network is to set player.source as part of the code that issues the stop (likely attached to a stop button). Setting the source seems to make the stop method redundant, but good to have a belt with suspenders sometimes. Play/pause/play cycles work without issue.

I uploaded this sound: https://xeno-canto.org/802220/download to my webspace / Google Drive and play it from there. No issues at all (with play/pause & stop).

Of course, setting the Player.Source stops the sound. Anything else wouldn't make sense either, because if I change the source and then want to play it, the current player has to be released / reset (i.e. stopped) before the new sound can be played from the beginning (on the same Player).

The Player.Stop method only stops the sound, but doesn't change (erase) the Player.Source.

To what extent should this be a solution? And for what?
This will only confuse other users, so I will remove the solution marker.

Direct URL links to the MP3 sources or Google Drive direct links should be used for the audio player (like https://drive.google.com/uc?export=download&id=127iC...).

Here is my experience.

The URLs I use for the player source are supplied in a response from an API call to a Xeno-Canto web service (https://xeno-canto.org/{int}/download). The player source is set to this URL. When the player client code makes an HTTP request for this resource, the service response is an mp3 file. In the case of the above URL, the response is the file XC802220 - American Redstart - Setophaga ruticilla.mp3. So I think this response is the same as a request using the name of the file.

There are many thousands of mp3 files (bird songs and bird calls) with lengths that range from a few seconds to over five minutes. The many files I have not had problems with are under around 2 minutes in length. When I get to about five minutes in length I almost always have a runtime error (at my network speed and using the Xeno-Canto service). I expect the runtime error would be worse on a slow connection (out in the field away from fast wi-fi) or slow servers on the service implementation side.

Consistent with TAMAI2's comments, runtime errors correlate with the size (play length) of the mp3 file. Beyond a certain size (or download time) there seems to be the possibility of a race condition that can cause the implementation to raise the runtime error. Putting the file on a faster service (your webspace / Google Drive) could very well avoid the race condition in more cases. If the way you have things configured results in the file being local to your machine, you might have a very hard time experiencing the runtime error.

TAMAI2 recommends implementing a delay to avoid the race condition. Since download time is often indeterminate, a timer is not a robust solution.

My solution so far is to reset the player source (to the same source that was playing so start plays the same file) when the user presses the stop. Based on your comment that the reset deletes the file, then I expect the file is closed and deleted and the race condition goes away (at the cost of losing a cached file).

After I changed the code to always reset the player source I have experienced zero runtime errors.

I am making many, possibly unsupported, inferences in how the underlying code works. I am basically black box testing. I may be wrong in my conclusions, but I don't think so. This feels like a bug in the player.stop implementation, but with a workaround. I would rather not confuse other developers, but if someone runs into this runtime error, then they may want to exercise this workaround.

Please show your working blocks that overcome the file size issue (by reloading the source)

I will keep an open mind, but what you write about bears no logic.

1 Like

In the real app, the value of global filename was set from the webservice call before the playpausebutton is made active. The value does not change when stopbutton is clicked so that the same sound can be played if the playpausebutton is subsequently clicked. The player can be paused, restarted, stopped, started as many times as the user cares to, with the same source value.

The value of fname is not changed unless the user leaves the detail and goes back to the list and picks a different (or the same) entry.

In my example app (referenced above) what happens is effectively what would happen if the same entry were clicked in the list of two set in the example app. That is, the player source would again be set to the same URL value.

What are you finding to be illogical? Perhaps I can clarify something specific.

BTW, in the stopbutton.Click block SoundPlayer.stop might not be needed.

You have obfuscated things by using a variable (and not showing the value set), and also you did not show the original setting of the sound source, and the value set.

The generally found scenario with larger sound files, is that the setting of the source does not have enough time internally to set itself before the download is completed, thus when a call to play the source is made the source is often not ready, generating the error.

I think that may be the race condition I reference. Make sure you understand that the file has begun to play and will continue to play until paused or stopped. It is then the runtime error is possible.

I'll change the example application to be more straightforward if that helps. It will take me a bit to go back to it. In the meantime, setting the source to https://xeno-canto.org/802220/download and cycling through play/pause/stop/stop gets me a runtime error. If I insert setting the source to the same https://xeno-canto.org/802220/download in the stop logic, I never get a runtime error.

As I said before, I have no problem playing/pausing/stopping a 50MB sound if the sound is a direct link to the MP3 file like this:

Depending on the size of the sound file and the particular audio format (mp3, wav, m4a, ogg etc.) it may take a little (a few seconds) to buffer. On my test devices it only takes 1-2 seconds (for a 50MB mp3/wav) file.

Incidentally, the sound component is only intended for sounds with a maximum duration of 5 seconds.

it is an instance of player. I was injudicious with the name in these latest blocks. not so with the example project in the link.

.:question:.

ExoPlayer handles streams better than regular MediaPlayer. It has e.g. play method: "playWhenReady", and the caching is asynchronous, it doesn't block the UI.

Which ExoPlayer are we talking about here? About the outdated ExoPlayer version that Kodular still uses or any extension?

The fact that ExoPlayer differs from MediaPlayer in many ways is well known and has been discussed here and in the Kodular forum several times. For example also in the power user forum (note: only visible for PUs):

https://community.appinventor.mit.edu/t/streaming-radio-drop-out-technical-advice/1213/6?u=anke

To get rid of this error (in your case / with your streaming URL):