I'am doing simple libgdx game. I have lag (game stop for 0. 5 sec) when i use sound.play()
edit this bug apear on android 4.0 on 2.3 everything is running fine.
method. I play sound by this code:
if(CollisionDetector.detect(touchArea, hoodie.getTouchArea())){
GameScreen.totalScore++;
setPosition();
System.out.println("played");
Assets.eatSound.play();
}
And i use this method to load sound:
static long waitForLoadCompleted(Sound sound,float volume) {
long id;
while ((id = sound.play(volume)) == -1) {
long t = TimeUtils.nanoTime();
while (TimeUtils.nanoTime() - t < 100000000);
}
return id;
}
What am i doing wrong? Or what can i do to fix this lag ?
edit:
I have just tried to do thread with sound.play() but it also doesn't work:
new Thread(new Runnable() {
#Override
public void run() {
// do something important here, asynchronously to the rendering thread
// post a Runnable to the rendering thread that processes the result
Gdx.app.postRunnable(new Runnable() {
#Override
public void run() {
// process the result, e.g. add it to an Array<Result> field of the ApplicationListener.
eatSound2.play();
}
});
}
}).start();
My Sound asset class looks like this but i still have lag with sound.
package com.redHoodie;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.utils.Disposable;
public class SoundEffect implements Disposable {
private static final int WaitLimit = 1000;
private static final int ThrottleMs = 100;
Sound eatSound;
Sound endSound;
public SoundEffect(){
eatSound = Gdx.audio.newSound(Gdx.files.internal("eatSound.ogg"));
endSound = Gdx.audio.newSound(Gdx.files.internal("sadend.wav"));
checkedPlay(eatSound);
}
protected long checkedPlay (Sound sound) {
return checkedPlay(sound, 1);
}
protected long checkedLoop (Sound sound) {
return checkedLoop(sound, 1);
}
protected long checkedPlay (Sound sound, float volume) {
int waitCounter = 0;
long soundId = 0;
boolean ready = false;
while (!ready && waitCounter < WaitLimit) {
soundId = sound.play(volume);
ready = (soundId != 0);
waitCounter++;
try {
Thread.sleep(ThrottleMs);
} catch (InterruptedException e) {
}
}
return soundId;
}
protected long checkedLoop (Sound sound, float volume) {
int waitCounter = 0;
long soundId = 0;
boolean ready = false;
while (!ready && waitCounter < WaitLimit) {
soundId = sound.loop(volume);
ready = (soundId != 0);
waitCounter++;
try {
Thread.sleep(ThrottleMs);
} catch (InterruptedException e) {
}
}
return soundId;
}
#Override
public void dispose() {
// TODO Auto-generated method stub
}
}
I had the same problem. It was because my .mp3 file was too short. Mine was 0.167 seconds long. I added 1.2 seconds of silence with Audacity, and it fixed the problem.
Lately I run into the same issue (except I'm using wav instead mp3 files). My app was lagging when I play many (like 10 or 20) sounds at the same time (same render method). "Solved" this by playing only 1 sound at the time. Generally it's hard to distinct many sounds at the same time. Also on desktop it works fine, but problem appears on android (9 or 8).
If someone still facing this issue as me there is the alternative solution with one limitation: no option to use sound id.
You can change default LibGDX behavior and use AsynchronousAndroidAudio by overriding this method in your AndroidLauncher class:
#Override
public AndroidAudio createAudio(Context context, AndroidApplicationConfiguration config) {
return new AsynchronousAndroidAudio(context, config);
}
See the official documentation for more info and also the pull request
Also, if for any reasons you need sound id you can take this implementation as an example and find a workaround for your project.
Fix is available starting from LibGDX 1.9.12
Related
I tried making a synth and it works and I can play music with them. However the first synth that I made had delay and you couldn't play fast songs. So I tried again using sourceDataline.flush() method to speed it up. Well it somewhat fixes it but delay is to much. I tried also reducing sample rate but delay is to much.
Edit: turns out you can comment the line keyStateInterface.setFlush(false);
it improves the delay however you still can't play fast songs
here is the code:
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
public class SoundLine implements Runnable{
KeyStateInterface keyStateInterface;
public SoundLine(KeyStateInterface arg){
keyStateInterface=arg;
}
#Override
public void run() {
AudioFormat audioFormat = new AudioFormat(44100,8,1,true,false);
try {
SourceDataLine sourceDataLine = AudioSystem.getSourceDataLine(audioFormat);
sourceDataLine.open(audioFormat);
sourceDataLine.start();
SynthMain synthMain = new SynthMain();
int v = 0;
while (true) {
int bytesAvailable = sourceDataLine.available();
if (bytesAvailable > 0) {
int sampling = 256/(64);
byte[] bytes = new byte[sampling];
for (int i = 0; i < sampling; i++) {
//bytes[i] = (byte) (Math.sin(angle) * 127f);
float t = (float) (synthMain.makeSound((double)v,44100,keyStateInterface)* 127f);
bytes[i] = (byte) (t);
v += 1;
}
if(keyStateInterface.getFlush()){
sourceDataLine.flush();
}
sourceDataLine.write(bytes, 0, sampling);
//if(!keyStateInterface.isCacheKeysSame())sourceDataLine.flush();
//System.out.println(bytesWritten);
} else {
Thread.sleep(1);
}
//System.out.println(bytesAvailable);
//System.out.println();
//if((System.currentTimeMillis()-mil)%50==0)freq+=0.5;
}
}catch (Exception e){
}
}
}
public class SynthMain {
double[] noteFrequency = {
466.1637615181,
493.8833012561,
523.2511306012,
554.3652619537,
587.3295358348,
622.2539674442,
659.2551138257,
698.4564628660,
739.9888454233,
783.9908719635,
830.6093951599,
880.0000000000,
932.3275230362,
987.7666025122,
1046.5022612024,
1108.7305239075,
1174.6590716696,
1244.5079348883,
1318.5102276515,
1396.9129257320,
1479.9776908465,
1567.9817439270,
1661.2187903198,
1760.0000000000,
1864.6550460724,
1975.5332050245,
2093.0045224048,
2217.4610478150,
2349.3181433393,
2489.0158697766,
2637.0204553030,
2793.8258514640,
2959.9553816931,
3135.9634878540,
3322.4375806396,
3520.0000000000,
3729.3100921447,
};
boolean[] keys = new boolean[noteFrequency.length];
public double makeSound(double dTime,double SampleRate,KeyStateInterface keyStateInterface){
if(keyStateInterface.getSizeOfMidiKey()>0){
keyStateInterface.setFlush(true);
for(int i=0;i<keyStateInterface.getSizeOfMidiKey();i++) {
KeyRequest keyRequest = keyStateInterface.popMidiKey();
if(keyRequest.getCommand()==-112){
if(keyRequest.getVelocity()>0)keys[keyRequest.getArg1()] = true;
if(keyRequest.getVelocity()<1)keys[keyRequest.getArg1()] = false;
System.out.println(keyRequest.getVelocity());
}
}
}else{
keyStateInterface.setFlush(false);
}
//System.out.println("makeSound");
double a = 0.0;
for(int i=0;i<keys.length;i++){
if(keys[i]){
a+=Oscillate(dTime,noteFrequency[i],(int)SampleRate);
}
}
return a*0.4;
}
public double Oscillate(double dTime,double dFreq,int sampleRate){
double period = (double)sampleRate / dFreq;
return Math.sin(2.0 * Math.PI * (int)dTime / period);
}
}
import java.util.ArrayList;
import java.util.Stack;
public class KeyState implements KeyStateInterface{
boolean isFlush;
ArrayList<KeyRequest> keyRequest = new ArrayList<KeyRequest>();
ArrayList<KeyRequest> midiKeyRequest = new ArrayList<KeyRequest>();
#Override
public void pushKey(int keyCode, boolean press) {
keyRequest.add(new KeyRequest(KeyRequest.KEY,keyCode,press));
}
#Override
public void pushMidiKey(int command, int arg1, int velocity) {
midiKeyRequest.add(new KeyRequest(KeyRequest.MIDI_KEY,command,arg1,velocity));
}
#Override
public KeyRequest popKey() {
KeyRequest t = keyRequest.get(keyRequest.size());
return t;
}
#Override
public KeyRequest popMidiKey() {
KeyRequest t = midiKeyRequest.get(keyRequest.size());
midiKeyRequest.remove(keyRequest.size());
return t;
}
#Override
public int getSizeOfKey() {
return keyRequest.size();
}
#Override
public int getSizeOfMidiKey() {
return midiKeyRequest.size();
}
#Override
public boolean getFlush() {
boolean v = isFlush;
isFlush = false;
return v;
}
#Override
public void setFlush(boolean arg) {
isFlush=arg;
}
}
I haven't dug deep into your code, but perhaps the following info will be useful.
The SourceDataLine.write() method uses a blocking queue internally. It will only progress as fast as the data can be processed. So, there is no need to test for available capacity before populating and shipping bytes.
I'd give the SDL thread a priority of 10, since most of it's time is spent in a blocked state anyway.
Also, I'd leave the line open and running. I first got that advice from Neil Smith of Praxis Live. There is a cost associated with continually rebuilding it. And it looks to me like you are creating a new SDL for every 4 bytes of audio data. That would be highly inefficient. I suspect that shipping somewhere in the range of 256 to 8K on a line that is left open would be a better choice, but I don't have hard facts to back that up that opinion. Neil wrote about having all the transporting arrays be the same size (e.g., the array of data produced by the synth be the same size as the SDL write).
I've made a real-time theremin with java, where the latency includes the task of reading the mouse click and position, then sending that to the synth that is generating the audio data. I wouldn't claim thay my latency down to a precision that allows "in the pocket" starts and stops to notes, but it still is pretty good. I suspect further optimization possible on my end.
I think Neil (mentioned earlier) has had better results. He's spoken of achieving latencies in the range of 5 milliseconds and less, as far back as 2011.
so, here is my today problem:
First of all, please note that I do NOT have the Matlab parallel toolbox available.
I am running java code witch interact with Matlab. Sometime Matlab directly call some java functions, sometimes it is the opposite. In this case, we use a notification system which comes from here:
http://undocumentedmatlab.com/blog/matlab-callbacks-for-java-events
We then address the notification in proper callbacks.
Here is a simple use case:
My user select a configuration file using the java interface, loaded into Matlab.
Using an interface listener, we notify Matlab that the configuration file has been selected, it then run a certain number of functions that will analyzes the file
Once the analysis is done, it is pushed into the java runtime, which will populate interface tables with the result. This step involve that matlab will call a java function.
Finally, java request the interface to be switched to an arbitrary decided tab.
This is the order of which things would happen in an ideal world, however, here is the code of the listener actionPerformed method:
#Override
public void actionPerformed(ActionEvent arg0) {
Model wModel = controller.getModel();
Window wWindow = controller.getWindow();
MatlabStructure wStructure = new MatlabStructure();
if(null != wModel) {
wModel.readMatlabData(wStructure);
wModel.notifyMatlab(wStructure, MatlabAction.UpdateCircuit);
}
if(null != wWindow) {
wWindow.getTabContainer().setSelectedComponent(wWindow.getInfosPannel());
}
}
What happen here, is that, when the notifyMatlab method is called, the code does not wait for it to be completed before it continues. So what happen is that the method complete and switch to an empty interface page (setSelectedComponent), and then the component is filled with values.
What I would like to, is for java to wait that my notifyMatlab returns a "I have completed !!" signal, and then pursue. Which involves asynchrounous code since Matlab will code java methods during its execution too ...
So far here is what I tried:
In the MatlabEventObject class, I added an isAcknowledge member, so now the class (which I originaly found in the above link), look like this (I removed all unchanged code from the original class):
public class MatlabEventObject extends java.util.EventObject {
private static final long serialVersionUID = 1L;
private boolean isAcknowledged = false;
public void onNotificationReceived() {
if (source instanceof MatlabEvent) {
System.out.println("Catched a MatlabEvent Pokemon !");
MatlabEvent wSource = (MatlabEvent) source;
wSource.onNotificationReceived();
}
}
public boolean isAcknowledged() {
return isAcknowledged;
}
public void acknowledge() {
isAcknowledged = true;
}
}
In the MatlabEvent class, I have added a future task which goal is to wait for acknowledgement, the methods now look like this:
public class MatlabEvent {
private Vector<IMatlabListener> data = new Vector<IMatlabListener>();
private Vector<MatlabEventObject> matlabEvents = new Vector<MatlabEventObject>();
public void notifyMatlab(final Object obj, final MatlabAction action) {
final Vector<IMatlabListener> dataCopy;
matlabEvents.clear();
synchronized (this) {
dataCopy = new Vector<IMatlabListener>(data);
}
for (int i = 0; i < dataCopy.size(); i++) {
matlabEvents.add(new MatlabEventObject(this, obj, action));
((IMatlabListener) dataCopy.elementAt(i)).testEvent(matlabEvents.get(i));
}
}
public void onNotificationReceived() {
ExecutorService service = Executors.newSingleThreadExecutor();
long timeout = 15;
System.out.println("Executing runnable.");
Runnable r = new Runnable() {
#Override
public void run() {
waitForAcknowledgement(matlabEvents);
}
};
try {
Future<?> task = service.submit(r);
task.get(timeout, TimeUnit.SECONDS);
System.out.println("Notification acknowledged.");
} catch (Exception e) {
e.printStackTrace();
}
}
private void waitForAcknowledgement(final Vector<MatlabEventObject> matlabEvents) {
boolean allEventsAcknowledged = false;
while(!allEventsAcknowledged) {
allEventsAcknowledged = true;
for(MatlabEventObject eventObject : matlabEvents) {
if(!eventObject.isAcknowledged()) {
allEventsAcknowledged = false;
}
break;
}
}
}
}
What happen is that I discover that Matlab actually WAIT for the java code to be completed. So my waitForAcknowledgement method always wait until it timeouts.
In addition, I must say that I have very little knowledge in parallel computing, but I think our java is single thread, so having java waiting for matlab code to complete while matlab is issuing calls to java functions may be an issue. But I can't be sure : ]
If you have any idea on how to solve this issue in a robust way, it will be much much appreciated.
I'm trying to use OpenGL directly from Java using JNA on Mac OSX (I have done it successfully with Windows and Linux). I've browsed thru JOGL source but they use CALayers which I don't understand yet. I would like to just simply use NSOpenGLView if possible and place it over top the AWT Canvas. I find the NSWindow using JNA and add the NSOpenGLView I created and it seems to work except when I call [nsOpenGLContext setView] or [nsOpenGLView lockFocus] I get an 'invalid drawable' error. I learned from Rococoa how to use ObjectiveC from Java.
Here is some sample code:
private static boolean createMac(GL gl, Component c) {
NSAutoreleasePool pool = new NSAutoreleasePool();
pool.alloc();
pool.init();
gl.nsopenglview = new NSOpenGLView();
gl.nsopenglview.alloc();
Pointer ptr = Native.getWindowPointer(findWindow(c));
NSObject nsComponent = new NSObject();
nsComponent.obj = ptr;
Pointer cClass = nsComponent._class();
NSView view = new NSView();
view.alloc();
boolean isView = view.isKindOfClass(cClass);
// JFLog.log("test=" + isView);
if (isView) {
view.dealloc();
view.obj = ptr; //do NOT dealloc this (usually NSWindowViewAWT)
gl.nswindow = view.window();
} else {
view.dealloc();
gl.nswindow = new NSWindow();
gl.nswindow.obj = ptr;
}
NSOpenGLPixelFormat fmt = new NSOpenGLPixelFormat();
fmt.alloc();
fmt.initWithAttributes(new int[] {
NSOpenGLPFAWindow,
// NSOpenGLPFAAccelerated, //is not available on my test system
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAColorSize,24,
NSOpenGLPFADepthSize,16,
0 //zero terminate list
}
);
if (fmt.obj == null) {
JFLog.log("NSOpenGLPixelFormat initWithAttributes failed");
return false;
}
if (gl.nsopenglview != null) {
gl.nsopenglview.initWithFrame(new NSRect(c.getBounds()), fmt);
}
NSView content = gl.nswindow.contentView();
JFLog.log("content view=" + content.obj);
content.addSubview(gl.nsopenglview);
JFLog.log("layered=" + content.wantsLayer());
//use created context
gl.nsopenglcontext = gl.nsopenglview.openGLContext();
//create some resize/move listeners
final GL _gl = gl;
final Component _c = c;
c.addComponentListener(new ComponentListener() {
public void componentResized(ComponentEvent e) {
_gl.nsopenglview.setFrame(new NSRect(_c.getBounds()));
}
public void componentMoved(ComponentEvent e) {
_gl.nsopenglview.setFrame(new NSRect(_c.getBounds()));
}
public void componentShown(ComponentEvent e) {}
public void componentHidden(ComponentEvent e) {}
});
if (api == null) {
api = new GLFuncs();
gl.glLibrary = NativeLibrary.getInstance("OpenGL");
try {
Field fields[] = api.getClass().getFields();
for(int a=0;a<fields.length;a++) {
String name = fields[a].getName();
try {
fields[a].set(api, gl.glLibrary.getFunction(name));
} catch (Throwable t) {
JFLog.log("OpenGL:Warning:Function not found:" + name);
}
}
} catch (Exception e) {
JFLog.log(e);
}
}
pool.release();
return true;
}
I can't use the drawRect function in NSOpenGLView so I just lockFocus, use gl commands and unlockFocus when done. But the NSOpenGLContext doesn't have a view assigned and trying to assign the one I created generates the 'invalid drawable'.
Any ideas?
If you want a full working demo goto http://javaforce.sf.net and download v7.15.0, run ant in /jf and then in /projects/jtest3d and then execute run.sh (click the GLCanvas test).
I got it working! The problem was in Rococoa (or possibly a bug in JNA). Their NSRect structure does not pass to [NSOpenGLView initWithFrame] or [NSWindow initWithContentRect] properly. If I pass the 4 fields directly (x,y,width,height) to the function instead of the Structure itself then it works. Also I used [NSObject performSelectorOnMainThread] to make sure I do all GUI stuff on the main thread.
So it is possible to use OpenGL using pure JNA from Java. No native code needed.
This should be available in my javaforce.sf.net in v7.16 which I'll release in a while.
Thanks.
Thanks in advance for your help!
I am building an Arkanoid/Breakout style game, and am running into some issues on account of body removal (at least that's where I suspect that the issue is). When I run the game, most of the time everything works just fine, but every now and then, the game crashes. during this crash, all of the sprites attached to bodies disappear, and any sprite without a body is left on the screen. There are no LogCat errors, but there is a drastic slowdown in FPS. Also, I noticed that sometime the contacts are registered twice before the code in the contact listener is fired (you can see where I implement the log file output in the ContactListener below).
In the game, instead of bricks, I am using bolts, and the code that I used to generate the bolts is listed below. After that, I show how I am using a ContactListener, and after that I give the code for removing the body/sprite combination. Any help or suggestions are greatly appreciated!
Thanks,
Adam
Creating the body/sprite items that will be removed when they are contacted.
//BOLTS
final FixtureDef boltFixtureDef = PhysicsFactory.createFixtureDef(1, 1.0f, 0.1f);
final Body[] boltBody=new Body[BOLT_NUMBER];
final Sprite[] boltSprite=new Sprite[BOLT_NUMBER];
final PhysicsConnector[] facePhysicsConnector= new PhysicsConnector[BOLT_NUMBER];
String[] bodyNames=new String[BOLT_NUMBER];
for(int i=0; i<BOLT_NUMBER; i++){
boltSprite[i] = new Sprite(-100,-100, sceneManager.mBolt, sceneManager.activity.getVertexBufferObjectManager());
boltSprite[i].setCullingEnabled(true);
boltBody[i] = PhysicsFactory.createBoxBody(mPhysicsWorld, boltSprite[i], BodyType.KinematicBody, boltFixtureDef);
mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(boltSprite[i], boltBody[i], true, true));
bodyNames[i]="bolt"+Integer.toString(i);
boltBody[i].setUserData(bodyNames[i]);
facePhysicsConnector[i] = mPhysicsWorld.getPhysicsConnectorManager().findPhysicsConnectorByShape(boltSprite[i]);
boltBody[i].setTransform(xLocations[i]/PMR, yLocations[i]/PMR, 0);}
for(int i=0; i<BOLT_NUMBER; i++){
scene.attachChild(boltSprite[i]);}
The ContactListener:
final ContactListener contact = new ContactListener(){
#Override
public void postSolve(Contact contact, ContactImpulse impulse) {
String a = (String) contact.getFixtureA().getBody().getUserData();
String b = (String) contact.getFixtureB().getBody().getUserData();
if(a != null && b != null) {
for(int i=0; i<BOLT_NUMBER; i++){
if(a.equals("wrench") && b.equals(facePhysicsConnector[i].getBody().getUserData()) ||
a.equals(facePhysicsConnector[i].getBody().getUserData()) && b.equals("wrench")) {
//boltCollision[i]=true;
Log.i("contact","bolt contact" + i);
myDestroyer(facePhysicsConnector[i], true);
Log.i("contact","bolt POSTcontact" + i);}
else if((a.equals("wrench") && b.equals("paddle")) ||
(a.equals("paddle") && b.equals("wrench"))) {
Log.i("contact","paddle contact");
SpringNoise=true;}
else if((a.equals("wrench") && b.equals("door")) ||
(a.equals("door") && b.equals("wrench"))) {
Log.i("contact","door contact");
WoodNoise=true;}
}}
}
#Override
public void endContact(final Contact contact) {}
#Override
public void beginContact(Contact contact) {}
#Override
public void preSolve(Contact contact, Manifold oldManifold) {}
};
And here is the code to remove the body/sprite combo:
private void myDestroyer(final PhysicsConnector facePhysicsConnector2, final Boolean bodyToo){
if(killingInProcess == false){
killingInProcess = true;
//final PhysicsConnector facePhysicsConnector2 = mPhysicsWorld.getPhysicsConnectorManager().findPhysicsConnectorByShape(myShape);
sceneValue.engine.registerUpdateHandler(new IUpdateHandler(){
#Override
public void onUpdate(float pSecondsElapsed) {
sceneValue.engine.unregisterUpdateHandler(this);
sceneValue.engine.runOnUpdateThread(new Runnable(){
#Override
public void run() {
Log.i("contact","removing bolt");
mPhysicsWorld.unregisterPhysicsConnector(facePhysicsConnector2);
Vibrator v = (Vibrator) sceneValue.activity.getSystemService(Context.VIBRATOR_SERVICE);
//myFixture.getBody().destroyFixture(myFixture);
if(bodyToo == true){
mPhysicsWorld.destroyBody(facePhysicsConnector2.getBody());
}
facePhysicsConnector2.getShape().registerEntityModifier(new ParallelEntityModifier(new RotationModifier(3,180,0),
new ScaleModifier(3, 1, 0)));
sceneValue.mBlockSound.play();
v.vibrate(75);
//mScene.detachChild(mySprite);
System.gc();
killingInProcess = false;
}
});
}
#Override
public void reset() {
}
});
}}
I'm not sure about the crashing problem, but I do know why the removal of bodies happens too late.
The method postSolve is called from the UpdaeThread. But instead of removing the body there, you register another update handler (So it's code runs in the next game update). And in this code, you run another code in runOnUpdateThread so it runs again in the next game update. You skipped 2 updates for no reason :) This might create problems (It probably does)
I suggest you to fix this, this look up for another problems (This may serious and it's easily solved)
I'm not sure, but I think that the problem might be in your removing of sprites. I met the problem and now I'm trying to kill bodies in update handler like this(method onUpdate in IUpdateHandler):
final PhysicsConnector physicsConnector = physicsWorld
.getPhysicsConnectorManager().findPhysicsConnectorByShape(
body);
body.clearEntityModifiers();
physicsWorld.unregisterPhysicsConnector(physicsConnector );
physicsWorld.destroyBody(physicsConnector.getBody());
scene.detachChild(body);
activity.getEngine().unregisterUpdateHandler(this);
Im trying to implement fade in effect to my mp3 player.
I´m using
FloatControl volumeControl = (FloatControl) line.getControl(FloatControl.Type.MASTER_GAIN);
because FloatControl.Type.VOLUME throws an exception(Unavaliable Control) I dont know why.
I need some help with the algorithm, because its not working ok.
Heres the code:
public class FloatControlFader
{
public static void fadeIn(final FloatControl control, final float from,
final float to, final int seconds)
{
final float vps = ((to-from) / (seconds*10));//Volume incrased/100millisecond
control.setValue(from);
Thread t = new Thread(new Runnable(){
public void run() {
for(int i=0; i < seconds*10; i++)
{
try
{
Thread.sleep(100);
}
catch (InterruptedException ex)
{
}
System.out.println(control.getValue()); //for DEBUG
control.setValue(control.getValue() + vps);
}
}
});
t.start();
}
}
Would appreciate any help, thanks!
Remember the human ear does not hear linearly so increasing by a steady X vps is not going to sound like a smooth fade. You need to put a log function in there. Then you need to map the linear increases to log values. This is of course all assuming the volume control units are not in decibels. If you're increasing by db then you're fine.