I wrote a music player using OpenSL ES. It works fine besides one warning message coming out of libOpenSLES library. Here is the message.
03-05 00:10:15.367: W/libOpenSLES(12055): Missed SL_PLAYEVENT_HEADATNEWPOS for position 7000; current position 724009
...
03-05 00:10:27.226: W/libOpenSLES(12055): Missed SL_PLAYEVENT_HEADATNEWPOS for position 329015; current position 816013
It comes when I seek a media track. Sometimes I can seek with no warning, sometimes the message appears in the log.
Implementation is very simple. At initialization I pick up the seek control.
SLObjectItf decoder;
SLSeekItf seek;
...
SLresult result = (*decoder)->GetInterface(decoder, SL_IID_SEEK, &seek);
Then later, when user changes track position, I call SetPosition method as following.
SLresult result = (*seek)->SetPosition(seek, position, SL_SEEKMODE_ACCURATE);
Both calls return success result, and position change works all the time too. The only issue is the warning message mentioned above.
Any ideas why this message comes and how to avoid it?
Update:
Although the half of bounties were automatically assigned, the question is not yet answered. We don't know what causes the issue and how to avoid it.
A quick google of part of that log message found the following code snip, with your log print in the middle here, (complete source):
// nextVirtualMarkerMs will be set to the position of the next upcoming virtual marker
int32_t nextVirtualMarkerMs;
if (mObservedPositionMs <= virtualMarkerMs && virtualMarkerMs <= positionMs) {
// we did pass through the virtual marker, now compute the next virtual marker
mDeliveredNewPosMs = virtualMarkerMs;
nextVirtualMarkerMs = virtualMarkerMs + mPositionUpdatePeriodMs;
// re-synchronize if we missed an update
if (nextVirtualMarkerMs <= positionMs) {
SL_LOGW("Missed SL_PLAYEVENT_HEADATNEWPOS for position %d; current position %d",
nextVirtualMarkerMs, positionMs);
// try to catch up by setting next goal to current position plus update period
mDeliveredNewPosMs = positionMs;
nextVirtualMarkerMs = positionMs + mPositionUpdatePeriodMs;
}
notify(PLAYEREVENT_PLAY, (int32_t) SL_PLAYEVENT_HEADATNEWPOS, true /*async*/);
If you search pass a certain point there's some catching up to be done (jump) and perhaps an update was missed, that's what the log is stating, that we need to resynchronize the marker position.
Update
The whole code snip above is for calculating the oneshot (a stable temporary state) pause time if interpret the code correct, definition of oneshot, row 116:
// deferred (non-0 timeout) handler for SL_PLAYEVENT_*
// As used here, "one-shot" is the software equivalent of a "retriggerable monostable
// multivibrator" from electronics. Briefly, a one-shot is a timer that can be triggered
// to fire at some point in the future. It is "retriggerable" because while the timer
// is active, it is possible to replace the current timeout value by a new value.
// This is done by cancelling the current timer (using a generation count),
// and then posting another timer with the new desired value.
And you get the log message because of jumping to often for the code to catch up, I think. So try to not jump that often? By the way it's a warning log, it will catch up the next time in this code path.
Related
I try to access the accelerometer from the NDK. So far it works. But the way events are written to the eventqueue seems a little bit strange.
See the following code:
ASensorManager* AcquireASensorManagerInstance(void) {
typedef ASensorManager *(*PF_GETINSTANCEFORPACKAGE)(const char *name);
void* androidHandle = dlopen("libandroid.so", RTLD_NOW);
PF_GETINSTANCEFORPACKAGE getInstanceForPackageFunc = (PF_GETINSTANCEFORPACKAGE) dlsym(androidHandle, "ASensorManager_getInstanceForPackage");
if (getInstanceForPackageFunc) {
return getInstanceForPackageFunc(kPackageName);
}
typedef ASensorManager *(*PF_GETINSTANCE)();
PF_GETINSTANCE getInstanceFunc = (PF_GETINSTANCE) dlsym(androidHandle, "ASensorManager_getInstance");
return getInstanceFunc();
}
void init() {
sensorManager = AcquireASensorManagerInstance();
accelerometer = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_ACCELEROMETER);
looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
accelerometerEventQueue = ASensorManager_createEventQueue(sensorManager, looper, LOOPER_ID_USER, NULL, NULL);
auto status = ASensorEventQueue_enableSensor(accelerometerEventQueue,
accelerometer);
status = ASensorEventQueue_setEventRate(accelerometerEventQueue,
accelerometer,
SENSOR_REFRESH_PERIOD_US);
}
That's how I initialize everything. My SENSOR_REFRESH_PERIOD_US is 100.000 - so 10 refreshs per second. Now I have the following method to receive the events of the event queue.
vector<sensorEvent> update() {
ALooper_pollAll(0, NULL, NULL, NULL);
vector<sensorEvent> listEvents;
ASensorEvent event;
while (ASensorEventQueue_getEvents(accelerometerEventQueue, &event, 1) > 0) {
listEvents.push_back(sensorEvent{event.acceleration.x, event.acceleration.y, event.acceleration.z, (long long) event.timestamp});
}
return listEvents;
}
sensorEvent at this point is a custom struct which I use. This update method gets called via JNI from Android every 10 seconds from an IntentService (to make sure it runs even when the app itself is killed). Now I would expect to receive 100 values (10 per second * 10 seconds). In different tests I received around 130 which is also completly fine for me even it's a bit off. Then I read in the documentation of ASensorEventQueue_setEventRate that it's not forced to follow the given refresh period. So if I would get more than I wanted it would be totally fine.
But now the problem: Sometimes I receive like 13 values in 10 seconds and when I continue to call update 10 secods later I get the 130 values + the missing 117 of the run before. This happens completly random and sometimes it's not the next run but the fourth following or something like that.
I am completly fine with being off from the refresh period by having more values. But can anyone explain why it happens that there are so many values missing and they appear 10 seconds later in the next run? Or is there maybe a way to make sure I receive them in their desired run?
Your code is correct and as i see only one reason can be cause such behaviour. It is android system, for avoid drain battery, decreases frequency of accelerometer stream of events in some time after app go to background or device fall asleep.
You need to revise all axelerometer related logic and optimize according
Doze and App Standby
Also you can try to work with axelerometer in foreground service.
I am trying to include TBMP into my Mahjong game and this issue has been stuck with me for a few months now, and I am no closer to resolving it.
I have four players in a game and the game progressing counterclockwise. So imagine you have four players at a table:
Player 3
---------------------------
Player 4 | | Player 2
---------------------------
Player 1
Now Player 1 takes a turn, the data is pushed via JSON into the game data, and Player 2 gets notified that it is his turn. Player 3 and 4 also get a notification that a match update has occurred, but when they query the game data, it returns the stale game information, unless it becomes their turn, then they get the accurate and current game data.
I would like all players to be updated after every turn, rather than getting all the updates in one go when it becomes their turn.
I register an update listener as follows:
mTurnBasedMultiplayerClient.registerTurnBasedMatchUpdateCallback(mMatchUpdateCallback);
I have a function that processes the update as follows:
private TurnBasedMatchUpdateCallback mMatchUpdateCallback = new TurnBasedMatchUpdateCallback() {
#Override
public void onTurnBasedMatchReceived(#NonNull TurnBasedMatch turnBasedMatch) {
int turnStatus = turnBasedMatch.getTurnStatus();
// OK, it's active. Check on turn status.
switch (turnStatus) {
case TurnBasedMatch.MATCH_TURN_STATUS_MY_TURN:
MainActivity.mTurnData =
MahjongTurn.unpersist(turnBasedMatch.getData());
setLocalGameValues();
state = GameState.Playing;
return;
case TurnBasedMatch.MATCH_TURN_STATUS_THEIR_TURN:
MainActivity.mTurnData =
MahjongTurn.unpersist(turnBasedMatch.getData());
setLocalGameValues();
state = GameState.MultiWait;
return;
}
}
#Override
public void onTurnBasedMatchRemoved(#NonNull String matchId) {
game.showToast("A match was removed.");
state = GameState.MultiWait;
return;
}
};
Anyway, the listener is registered correctly and I can see that my listener gets called, but the call to turnBasedMatch.getData() only returns the correct game data when it is also the player's turn.
So Player 4 gets the updates from Player 1, Player 2 and Player 3 only when it becomes his turn. He gets notified that there is an update to the match, but has no way of knowing what the current game data looks like.
Is this the way it is meant to work? Am I doing something wrong?
One workaround for this is to store all the turns inside the message data. This way, I can treat the game data like a Message Queue and replay everything up to the current turn. This is not ideal, but it works.
i want to play sound either using mediaplayer or sound pool. and i want to do something when my sound/music reach some position such as
function void hello(){ // you reach position 1.5 seconds }
mediaplayer.setOnCertainPosition(1500, hello()); // not actual code
currently what i found is create loop function for every xxx millisecond and do
mediaplayer.getCurrentPosition()
from this answer
is there any better code?
note: i found about audiotrack but not sure how it works and how to use it
We're making a game in Android Studio and we got stuck. The resource (mana) used for specific spells should recover on time, e.g. 1 mana point per 5 minutes. We don't really get how to make it recover while the game is off. Is there a method to check current date/time and count the amount of mana replenished? Converting date and time to String and comparing it with the new date/time seems to be an "exciting" work to do, but we would bypass these mechanics if there is a way.
Thank you in advance.
The best way to do this in the background is to register a receiver in your manifest. This means the receiver will keep listening for broadcasts even if the app is off.
What you need is this particular action when registering your receiver Intent.ACTION_TIME_TICK
There is a more detailed answer about this matter here Time change listener
Another solution is to use the Calendar class in java. With it you can get the exact minutes passed from a point in the past to this moment. This way you don't have to worry about parsing dates and similar. I can't provide you specific examples because me myself have not used the Calendar class very much, but I'm sure you can find lots of stuff in the official documentation and on stackoverflow about it.
No need to work with Date objects, the simple usage of System.currentTimeMillis() should work. Here's a basic outline:
long mLastManaRefreshTime = System.currentTimeMillis();
void refreshMana()
{
long timeDelta = System.currentTimeMillis() - mLastManaRefreshTime;
mLastManaRefreshTime = System.currentTimeMillis();
float totalManaToRefresh = (float)AMOUNT_TO_REFRESH_IN_ONE_MINUTE * ((float)timeDelta / 60000f);
mMana += totalManaToRefresh;
if (mMana > MAX_MANA)
mMana = MAX_MANA;
}
This method is of course just an outline. You will need to call this once every update cycle. It will calculate how much time passed since the last time refreshMana was called, and replenish the required amount.
If you need this to work while the game is off, you can save the mLastManaRefreshTime to a SharedPreferences object and reload it when the game loads up again.
With System.currentTimeMillis() you can a current time-stamp in milliseconds.
You could save the latest time-stamp in your Preferences with every 5 min tick of the running game. For the other case, when your App comes back from a state where it does not do this (i.e. called the first time, woken up etc.).
Something like this:
int manacycles = ((int) (((System.currentTimeMillis() - oldtimestamp) / 1000) / 60) ) % 5;
would give you the number of Mana points you would have to add.
Alternately you could do the same thing with the Calendar class.
Also keep in mind players could cheat this way by simply changing their time. If your game is online you could get the time from the internet, with something like this:
try {
TimeTCPClient client = new TimeTCPClient();
try {
// Set timeout of 60 seconds
client.setDefaultTimeout(60000);
// Connecting to time server
// Other time servers can be found at : http://tf.nist.gov/tf-cgi/servers.cgi#
// Make sure that your program NEVER queries a server more frequently than once every 4 seconds
client.connect("nist.time.nosc.us");
System.out.println(client.getDate());
} finally {
client.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
I am writing a code for an app that will "beep" when the current speed is more than the user set warning speed limit. The code shown below is written inside onLocationChanged(), but for some logical reason, it beeps only once and then stops, which tells me that it goes through the loop once and after which the pastTime and curTime loses track and the if condition is not logically true after and hence skipping the loop. The reason I wanted a delay of 5 seconds is to have enough time delay between the beeps and not have them overlap. I intialized pasTime with 0 at the very beginning of the activity. Any suggestion on a fix for this is appreciated. Also curTime = c.getTimeInMillis() each time there is a location change in the location listener.
if (Activity2.mySpeedmph > mySpeed_max & curTime > pastTime+5000)
{
player = MediaPlayer.create(Activity2.this, R.raw.beep);
player.setLooping(false);
player.setVolume(100,100);
player.start();
pastTime = curTime;
}
This is a very specific problem and I couldn't find anything related to this. I know its a very simple issue for an expert.
Not meaning to be condescending here (this answer sounds like your classic IT helpdesk "Have you tried turning it off and on again?"), just covering all bases.
Are you updating curTime? What you're describing is what you'd see if pastTime was originally set far in the past and curTime is set once outside the loop and not updated.
Failing that, you probably need to take some samples of the variables involved in that if statement (before the if) to see what values they're set to.
Or, force one of the conditions to be true beforehand (such as with Activity.mySpeedmph = mySpeed_max + 1; so as to check the operation of the other condition.