Smoother Android SeekBar - android

I'm not strictly looking for an implementation of this idea but if someone has already made it, that would be awesome and I'd like to see it. Otherwise:
I'm trying to implement a couple SeekBars in my Android's waveform generator app. I have a couple controls such as: volume, frequency, low pass filter cutoff frequency, resonance, pitch bend, etc.
The problem with my SeekBars are that they sound too step-y and I want it to sound more analog-ish (smoother if you will). In my iOS implementation of the app, the native UISliders did a good job and I didn't hear any step-like movements. However, the SeekBars aren't very smooth and tend to jump value to value (lets say like from 10 to 100 with a max value of 1000).
I was wondering if it might be best if I just design my own custom UI for a smoother slider or if there is one already. Also, is it possible that my audio thread is interrupting the SeekBar's functionality and causing these jumps/step-like behavior?
Things I've tried already:
Lowpass the seekbar progress in the listener's onProgressChanged. This doesn't really work (if it jumped from 5 to 100 for example, this would give me a value in between but that still doesn't give a full smooth-like behavior).
// b = 0.99, a = 0.01
// Follows simple lowpass: yn = (xn * b) + (yn1 * a)
public double lowpass(int xn) {
double yn = (xn * b) + (lastProgress * a);
lastProgress = yn;
return yn;
}
If there is a huge jump (like 5 to 100), I would call a while loop to increment the audio context's variables by 1. The problem with this is if I was trying to do a pitch bend (a 14bit number so that's 16384 values total), it would take too long to get to the target value (the pitch bend does sound cool though). for example (obviously this only accounts for progress going up):
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
int myProgress = seekBar.getProgress();
while (myProgress < progress) {
// This will increase the audio context's frequency variable by one every loop until we've reached our target progress
audioContext.setFrequency(myProgress++);
}
}
Thanks!

First, figure out what is the fastest you want the volume to increase. For this example, I'll use 1 second (1000ms) to change from 0 to 1.0. (0% to 100%)
When you enter the loop, record the current time. Then, for each iteration of your loop, check the time passed and increment the necessary amount.
// example, myLoop(0.25, 0.75, startTime);
double myLoop(double start, double end, long startTime) {
long now = System.currentTimeMillis(); // (100ms since startTime)
double span = end - start; // the span in value, 0 to 1.0 (=0.5)
double timeSpan = (now - startTime) / (span * 1000); // time since started / total time of change (100/500 = 0.2)
return timeSpan * span + start; // (0.2 * 0.5 = 0.1) + 0.25 = 0.35, the value for this loop
}
(untested code)

Related

Is it possible to set dynamic frame-rate with cocos2d-x?

I want to write a text reader which has special effects with cocos2d-x, so the most time the graph will be static. If I use cocos2d-x, it's just heavily consuming battery power.
So is it possible to adjust cocos2d-x's frame rate by coding? And how? I want to reduce frame rate when text's static, and increase frame rate when paging up or down.
Or any good idea for this goal on Android? (Page turning animations and more efficient text rendering.)
You can change frame rate using cocos2d::Director::setAnimationInterval method.
https://github.com/cocos2d/cocos2d-x/blob/1361f2c6195ce338a70b65c17e3d46f38e6bcce2/cocos/base/CCDirector.h#L140-L141
/** Set the FPS value. */
virtual void setAnimationInterval(double interval) = 0;
However, I wonder, if you set framerate low, your C++ code wasn't called immediately when paging up or down because the framerate is low. So you might need to modify onDrawFrame to call Cocos2dxRenderer.nativeRender immediately when user touched the screen.
https://github.com/cocos2d/cocos2d-x/blob/1361f2c6195ce338a70b65c17e3d46f38e6bcce2/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxRenderer.java#L84-L106
#Override
public void onDrawFrame(final GL10 gl) {
/*
* No need to use algorithm in default(60 FPS) situation,
* since onDrawFrame() was called by system 60 times per second by default.
*/
if (sAnimationInterval <= 1.0 / 60 * Cocos2dxRenderer.NANOSECONDSPERSECOND) {
Cocos2dxRenderer.nativeRender();
} else {
final long now = System.nanoTime();
final long interval = now - this.mLastTickInNanoSeconds;
if (interval < Cocos2dxRenderer.sAnimationInterval) {
try {
Thread.sleep((Cocos2dxRenderer.sAnimationInterval - interval) / Cocos2dxRenderer.NANOSECONDSPERMICROSECOND);
} catch (final Exception e) {
}
}
/*
* Render time MUST be counted in, or the FPS will slower than appointed.
*/
this.mLastTickInNanoSeconds = System.nanoTime();
Cocos2dxRenderer.nativeRender();
}
}

using LinearAcceleration and time passed to get distance traveled - android [duplicate]

This question already has answers here:
How to use Accelerometer to measure distance for Android Application Development
(2 answers)
Closed 9 years ago.
I know i am opening up a can of worms with trying to get the linear motion of a device using the accelerometer, but please just humor me.
I am trying to figure out the right formula to take the Sensor.TYPE_LINEAR_ACCELEROMETER (which i believe is normal accelerometer data minus gravity) and essentially say "this much time has passed and the i have accelerated x amount since last time, so i have traveled d amount.
should be something like distanceTraveledOnX = linearAccerationOfX * TimePassed;
easy enough in the real world right? if i have been going 1 mile a minute for 10minutes then i have traveled 10 miles.. speed * time = distance
problem is im not sure what the linearAcceleration is using for unit of measure. I know my timePassed is in NanoSeconds as i am saying (in my onSensorChanged)
currentTime = System.nanoTime();//var of type (double)
timePassed = currentTime - lastTime;
lastTime = currentTime;
can someone please help me figure out the formula for translating the linearAcceleration value to a nanoSecond measurement..
thanks
EDIT
here is the code im currently using but im always getting 0 :
public void onSensorChanged(SensorEvent evt) {
if (type == Sensor.TYPE_LINEAR_ACCELERATION) {
newTime = System.currentTimeMillis()/1000;
float oldVelocity = lastTime1-lastTime0;
float newVelocity = newTime- lastTime1;
if(oldVelocity<1)oldVelocity =1;
newX = lastX1 + ((lastX1 - lastX0)/oldVelocity)*newVelocity +(evt.values[0]/2)*(newVelocity*newVelocity);
lastX0 = lastX1;
lastX1 = newX;
lastTime0 = lastTime1;
lastTime1 = newTime;
Log.v("SENSOR MAN LINEAR", "new X:"+newX);
}
}
This stuff is high school physics, and if you don't know the difference between acceleration and velocity, you'll need to review it before you have any hope here.
I can tell you this much: the linear acceleration readings from a cell phone or tablet aren't remotely precise or accurate enough to do what you want without constant correction (via gps or other methods). There is an entire field of study trying to solve this problem. I've attended conferences on it.
That said, you also need to take into account that the orientation of your device will also change, unless this is some sort of special application, e.g. the device is trapped onto a sled which can only move in one direction.
Let's assume that case, and assume that the device is strapped to your sled with the right side of the device (+X axis) aligned in the direction of travel. Let's also assume that the initial position of the sled is known (call it X0) when the program starts, and that the initial velocity is zero.
Your code looks approximately like this:
double x0; // previous position, meters
double x; // current position
double v0; // previous velocity, meters/second
double v; // current velocity
long t0; // previous time, nanoseconds
long t; // current time
public void onStart() {
x0 = getInitialPosition();
x = x0;
v0 = 0;
v = v;
t0 = System.getCurrentTime() * 1000000;
// Enable sensors; left as an exercise for the reader
}
public void onSensorChanged(SensorEvent event) {
// Assume linear acceleration is the only active sensor
double accel = event.values[0]; // X axis is our axis of acceleration
t = event.timestamp;
double dt = (t - t0) * .000001;
v = v0 + accel * dt;
x = x0 + v * dt;
t0 = t;
v0 = v;
x0 = x;
}
This is by no means a complete solution. Doing this right involves differential equations which I'm not equipped to explain here (translation: I've forgotten everything I learned in college). However, if your acceleration value is accurate enough, and your time slice is short enough, this is viable.
If you need to solve this in more than one direction, it's only slightly more complicated provided that the device never changes orientation. If it does, then you also need to capture the rotation sensor and learn about quaternions and rotation matrices.
And even if you do everything right, errors will still accumulate, so now you want some sort of correction factor based on GPS, known geometry of the environment (e.g. if you're indoors and the software has a map of the building, it can make corrections when you turn a corner), and other environmental clues such as WiFi hotspots in known locations.
You might want to read up on Kalman filters at this point.
Executive summary: this is a HARD problem in the general case, and if you solve it, there's probably fame and fortune waiting for you.
Well, the correct form, known from school, is
finalXPosition = (linearAcceleration*timePassed^2)/2+ initialVelocity*timePassed+initialXPosition
finalVelocity = initialVelocity*timePassed
chaining these chunks you'll get your theoretical values.
In practice, best results are achieved by regular calibration of initialXPosition and initialVelocity through GPS.
simple example to receive calibrated horizontal acceleration in onSensorChanged:
class Integrator {
private float position = 0f;
private float velocity = 0f;
public void setGpsPosition (float gpsPosition) {
position = gpsPosition;
}
public void setGpsVelocity (float gpsVelocity) {
velocity = gpsVelocity;
}
public void onAccelerationChangeHandler(float acceleration, float timePassed) {
position += acceleration*timePassed*timePassed/2f + velocity*timePassed;
velocity += acceleration*timePassed;
}
public float getCurrentPosition() {
return position;
}
}
usage for x-acceleration:
long lastTime = 0;
public void onSensorChanged(SensorEvent evt) {
if (evt.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) {
long newTime = System.currentTimeMillis();
OnAccelerationChangeHandler(evt.values[0], (newTime-lastTime)/1000);
lastTime = newTime;
}
Please, note that outside a minute scale the error makes this all meaningless w/o gps correction. Understand, that if you are walking at constant speed the sensor won't give you anything at all.

Fire events at specific at specific progress as the seek bar progresses Android

I followed this tutorial to create an audio player.
Now I want to add some events that fires events when the player reaches a specific progress.
For example, at 00:32 of the current audio, display a Toast.
The interface should look like this :
The white dots represents the events in this case.
Any idea?
Calculate the total duration of the supported audio file, using getDuration() method, use getCurrentPosition() method to find out if the seekbar has progressed your desired point. Fire events accordingly.
I found it guys, here is my code (that worked perfectly)
private ImageView CreateWhiteDot(int time) {
ImageView WhiteDot = new ImageView(getApplicationContext());
float x = 0;
float y = 0;
WhiteDot.setImageResource(R.drawable.stop_dot_inactive);
x = sbStopPlayerSlider.getX() + 15;
x += ((sbStopPlayerSlider.getWidth() - 30) * time * 1000)
/ mpAudioPlayer.getDuration();
WhiteDot.setX(x);
y = sbStopPlayerSlider.getTranslationY()
+ sbStopPlayerSlider.getHeight() / 2;
WhiteDot.setY(y);
return WhiteDot;
}

jump method in android game

I'm new in Android Game Development and I started a simple Game in that a Droid can jump over incoming boxes.
I want to call my droid.jump() method in the surface view with an onTouchEvent (just by a simple tap on the screen)
I created a class called Droid:
public class Droid {
// Log Tag for Debugging
public static final String LOG_TAG = "_1Projekt";
private Bitmap bitmap; // the actual bitmap
private int x; // the X coordinate
private int y; // the Y coordinate
private boolean touched; // if droid is touched/picked up
private Speed speed; // the speed with its directions
private long mLastTime;
public Droid(Bitmap bitmap, int x, int y) {
this.bitmap = bitmap;
this.x = x;
this.y = y;
this.speed = new Speed();
}
.......
...
And the jump() method is my problem. I would like to have a smooth jump but I don’t know how to calculate this with the current System Time.
My idea was that the droid should update it Y position every –TimePeriod- and should start with a fast velocity and then decrease it to 0 to get a smooth jump.
But I don’t know how to calculate this in my while loop.
My current jump():
public void jump() {
Log.d(LOG_TAG, "Jumping");
long now = System.currentTimeMillis();
int elapsedTime = 100;
int jump = y-30;
while(y > jump)
{
if(System.currentTimeMillis() > now + elapsedTime)
{
now = now + elapsedTime;
elapsedTime -=3;
y = y-1;
}
}
}
Up to know I only implemented the "up" part of Jump.
Thank you for your answers! Greetings DroidDude
You may want to look here (the third post):
Before the mainloop, have
//Get the current time
timeStep = System.currentTimeMillis();
And then do your stuff. Then before the loop goes back to start, have
// Hold to lock at FPS
while(System.currentTimeMillis()-timeStep < 1000/60);
Where 60 is the frames per second to run.
This method also allows you to get the difference in times after the
while loop in order to find out how much time it took to render one
frame. Using this, you could have a variable that you can multiply all
your increments by to get frame-independent speed.
For example, if you divide the number of milliseconds it took to
handle the frame by 16.666, the number will equal 1 when the program
runs at about 60 FPS. When the frame rate is lower, it takes more
milliseconds to render the frame and that factor gets bigger.
Be sure to put it BEFORE repaint however. This is the way video games
are timed also. An example is shown in my video game base applet code
snippet under JAVA.

Android: MediaPlayer setVolume function

about the params
Set what to make the player no sound and full sound
Thanks
This function is actualy wonderful. Thanks to it you can create a volume scale with any number of steps!
Let's assume you want 50 steps:
int maxVolume = 50;
Then to set setVolume to any value in this range (0-49) you do this:
float log1=(float)(Math.log(maxVolume-currVolume)/Math.log(maxVolume));
yourMediaPlayer.setVolume(log1,log1); //set volume takes two paramater
Nice and easy! And DON'T use AudioManager to set volume! It will cause many side effects such as disabling silent mode, which will make your users mad!
Following user100858 solution I just post my exact code that works:
private final static int MAX_VOLUME = 100;
...
...
final float volume = (float) (1 - (Math.log(MAX_VOLUME - soundVolume) / Math.log(MAX_VOLUME)));
mediaPlayer.setVolume(volume, volume);
soundVolume is the volume you would like to set, between 0 and MAX_VOLUME.
So between 0 and 100 in this example.
For Android MediaPlayer.setVolume,
searching the web seems to show 0.0f for no sound, 1.0f for full sound.
The other answers here are not correct--or at least, they're not configured properly.
Perform the following test, using their code (e.g. that of Tomasz or ssuukk):
1) Set 100 as the "max volume"/number of steps, and submit the volume 50.
It returns: 0.150514997831991
2) Set 1000 as the "max volume"/number of steps, and submit the volume 500.
What does it return? The same value, 0.150514997831991, right?
Nope. Instead, it's: 0.100343331887994
In other words, the existing answers change how they scale the input volume-percent (i.e. the transformation curve) based on how many volume-steps you set.
I've spent the last few hours looking into this issue; enough that I don't feel like going into too much detail explaining the issue. Instead I'll just post the large code/comment block in my program concerning it. (it's in C#, for Xamarin Android, but the functionality should be the same for Java)
public enum VolumeScaleType
{
//Energy, // what MediaPlayer possibly treats passed values as
Amplitude, // what MediaPlayer most likely treats passed values as
Loudness // what people treat everyday volume values as (as in "that sounded 2 times as loud")
}
// MediaPlayer
/*public static void SetVolume_IncorrectSOApproach(this MediaPlayer s, double volume, VolumeScaleType volumeType = VolumeScaleType.Loudness)
{
const int maxVolume = 100;
var volume_toScale = volume * maxVolume;
double volume_scalar = volumeType == VolumeScaleType.Amplitude ? volume : (1 - (Math.Log(maxVolume - volume_toScale) / Math.Log(maxVolume)));
s.SetVolume((float)volume_scalar, (float)volume_scalar);
}*/
public static void SetVolume_MyPossiblyCorrectApproach(this MediaPlayer s, double volume, VolumeScaleType volumeType = VolumeScaleType.Loudness)
{
// Links:
// 1) http://en.wikipedia.org/wiki/Decibel
// 2) http://trace.wisc.edu/docs/2004-About-dB
// 3) http://hyperphysics.phy-astr.gsu.edu/hbase/sound/loud.html
// 4) http://www.animations.physics.unsw.edu.au/jw/dB.htm
// 5) http://www.soundmaskingblog.com/2012/06/saved_by_the_bell
// 6) http://www.campanellaacoustics.com/faq.html
// 7) http://physics.stackexchange.com/questions/9113/how-sound-intensity-db-and-sound-pressure-level-db-are-related
// 8) http://www.sengpielaudio.com/calculator-loudness.htm (note: page uses terms 'power/intensity' and 'pressure' differently; power/intensity: for whole shell at distance, pressure: field-quantity?)
// basic idea: you can think of one decibel (of gain), + or -, as *translating into* the given changes-in/multipliers-for energy, amplitude, or loudness
// (i.e. one decibel provides a specific amount to multiply energy, amplitude, and loudness values, such that they remain aligned realistically)
// note: the 'one decibel' unit is set up to correspond roughly to a change in loudness just substantial enough to be noticeable
// note: the 'quietest perceivable sound' example (standard) base has these absolute values: 'e' is 1 pico-watt per square-foot, 'a' is 20 micropascals, 'l' is the quietest-perceivable-loudness
// references (for q.p.s. base) | db (gain) | energy | amplitude | loudness
// ===============================================================================================
// actual silence | -inf | 0 | 0 | 0
// (a seeming silence) | -20 | e / 100 | a / 10 | 0 (would be l / 4, if 'l' weren't already for the quietest-perceivable-sound)
// (a seeming silence) | -10 | e / 10 | a / 3.16227/sqrt(10) | 0 (would be l / 2, if 'l' weren't already for the quietest-perceivable-sound)
// quietest perceivable sound | 0 | e | a | l
// ? | 1 | e * 1.258925 | a * 1.122018 | l * 1.071773
// rustling leaves | 10 | e * 10 | a * 3.16227/sqrt(10) | l * 2
// whisper, or rural nighttime | 20 | e * 100 | a * 10 | l * 4
// watch ticking | 30 | e * 1000 | a * 31.622/sqrt(100) | l * 8
// quiet speech, or rural daytime | 40 | e * 10000 | a * 100 | l * 16
// dishwasher in next room | 50 | e * 100000 | a * 316/sqrt(100000) | l * 32
// ordinary conversation | 60 | e * 1000000 | a * 1000 | l * 64
// ===============================================================================================
// assuming MediaPlayer.SetVolume treats passed values as Amplitude
Func<double, double> convertLoudnessToAmplitude = loudness=>Math.Pow(10, Math.Log(loudness, 4));
var volume_amplitude = volumeType == VolumeScaleType.Amplitude ? volume : convertLoudnessToAmplitude(volume);
s.SetVolume((float)volume_amplitude, (float)volume_amplitude);
// assuming MediaPlayer.SetVolume treats passed values as Energy
//Func<double, double> convertLoudnessToEnergy = loudness=>Math.Pow(100, Math.Log(loudness, 4));
//var volume_energy = volumeType == VolumeScaleType.Energy ? volume : convertLoudnessToEnergy(volume);
//s.SetVolume((float)volume_energy, (float)volume_energy);
}
Conclusion
The documentation is sparse, so I can't know for sure if I have the right scaling-system/type-of-unit the SetVolume method expects.
Assuming it expects an Amplitude value, the code above may be the correct volume setting code for it. (taking desired Loudness, linear, as an input, and outputting/setting the Amplitude value needed for the built-in SetVolume method)
I'm not sure it's correct, though, and am too tired to confirm. If anyone has further thoughts, feel free to add them. (3+ hours is enough to spend on an issue like this, in one day)
Edit
After listening carefully, and comparing the loudness-fade effect by:
Just submitting the desired loudness to the SetVolume method.
Exponentiating (basically) the desired-loudness before sending it in, to make it an Amplitude (or the like) value that the SetVolume method says it expects.
I find that option 1 seems to be closer to a linear loudness fade-in! In other words... from actually listening and comparing the basic approach, with the various transformation approaches shown here, it seems the documentation is wrong and the SetVolume method does in fact just expect the loudness value on a linear scale. (perhaps they've updated it to work more intuitively in one of the recent API versions, but haven't updated the docs?)
If so, that sure makes it easy. That's what I'm going with for now. (though I'll keep the exponentiation/scale-fixing approach as a program setting, I suppose, just to have an excuse to keep some result of all that time invested!)
The recommended answer is wrong, as stated Venryx. Log math doesn't work that way (you have to subtract, not divide logs to make them work how you want).
No matter, it looks like Android Volume setting is now proportionate to Loudness linearly... so 0.5 is 50% as loud as 1.0, and 0.1 is 10%, etc. No need for complicated Log math to convert decibels to loudness. Just set it linearly as is intuitive to most people.
I have tried Android MediaPlayer.setVolume, but this function is useless.
I think we should use the function below
AudioManager mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, maxVolume * mLastProgress / 10, 0);
Everything I have seen here has fallen short of my expectations. The main problem I had was that on a scale of 0 to 50, 25 was never in the middle but rather much closer to the maximum sound. The log functions proposed here made almost no difference for me.
To read more on the math, see this answer.
Variables
Linear input value = x // User-specified input value
Linear scale min,max = x1,x2 // My pre-determined range of 0-50 on my UI
Log scale min,max = y1,y2 // Normalizes the log result to between 0-1
Log value result = z // The output to pass to the setVolume() method
Formula where change decelerates as value goes up (simplest form)
Problem with this approach is that this is the opposite of what we want with android because it seems to already be doing this by default. It's already incrementing too fast when the values are still low when you pass linear values and this accentuates this effect even further.
x1 + (log(x) - log(x1)) / (log(x2) - log(x1)) * (y2 - y1) = z
Results of this function
Formula where change accelerates as value goes up
This is the approach that works for me; flipping the input to keep the same rate of change, but inversed. With this, I get just about a perfect mid-volume at around 25 and it's a very smooth hearing experience from 0 all the way to 50.
y2 - (x1 + (log(x2 + x1 - x) - log(x1)) / (log(x2) - log(x1)) * (y2 - y1)) = z
Results of this function
This code breaks down volume into 10 equal sections and increase or descrease volume.
Button decreaseVolButton = (Button) findViewById(R.id.decrease_volumn);
Button increaseVolButton = (Button) findViewById(R.id.increase_volumn);
final MediaPlayer mediaPlayer = MediaPlayer.create(MainActivity.this, R.raw.sample);
decreaseVolButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
volume = (float) (volume - 0.1);
mediaPlayer.setVolume(volume, volume);
}
});
increaseVolButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
volume = (float) (volume + 0.1);
mediaPlayer.setVolume(volume, volume);
}
});
If you want to set the volume to no sound then pass (0f,0f)
If you want to set the volume to full sound then pass (1f,1f)
Why making it so complicated? I am using this simple formula:
public float getVolume() {
float currVolume = (float) sp.getInt("volume", 10);
float maxVolume = 15.0f;
float result = currVolume / maxVolume;
return result;
}
and setting this value in media player, like:
player.setVolume(getVolume(), getVolume());
Since volume scales linearly, needless for complicated log function. Adjust the maxVolume, it's 100 steps in the sample code below, accordingly to the step you prefer and it should work. Hope this would help.
MediaPlayer myPlayer = MediaPlayer.create(MainActivity.this, R.raw.myAudioResource);
final float maxVolume = 100.0f;
float currentVolume = 5.0f;
myPlayer.setVolume(currentVolume/maxVolume, currentVolume/maxVolume);
myPlayer.start();
Well, I made the following code and it slightly works:
public class MainActivity extends Activity {
float volumeLevel = 0.5f;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
volumeUp = findViewById(R.id.volUp);
volumeDown = findViewById(R.id.volDown);
song = MediaPlayer.create(this, R.raw.audioFile);
volumeUp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
volumeLevel = volumeLevel + 0.1f;
song.setVolume(volumeLevel,volumeLevel);
}
});
volumeDown.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
volumeLevel = volumeLevel - 0.1f;
song.setVolume(volumeLevel,volumeLevel);
}
});
}
}

Categories

Resources