mindoverflow
mindoverflow

Reputation: 424

Return variable from class defined in another thread

I'm programming an audio visualizer. To sync the data that is to be displayed to the music itself, I need to get the clip's/player's current position in the audio file by calling clip.getMicrosecondPosition() / player.getPosition(). This is working fine for the clip. The problem is that I need to define the player object in another thread since calling player.play() halts all execution in the current thread until the file is played until the end. Player is a class from the MP3SPI library (javazoom.jl.player.Player).

Code:

public class AudioTools implements Runnable {

public File file;
public boolean isMP3 = false;
public Player player;
public Clip clip;

public AudioTools(File file) {
    this.file = file;
    determineFilyType();
}   

private void determineFileType() {
    if (file.getAbsolutePath().endsWith(".mp3")) {
        this.isMP3 = true;
    } else if (file.getAbsolutePath().endsWith(".wav")) {
        this.isMP3 = false;
    } else {
        System.err.println("Invalid File Type!");
        System.exit(0);
    }

}

public void play() {
    if (isMP3) {
        Thread t = new Thread(this);
        t.start(); // play MP3 using Player class
    } else {
        playWav(file); // play WAV using Clip class
    }

}

protected void playMP3(File file){
    try {
        player = new Player(new FileInputStream(file));
        player.play();
    } catch (Exception e) {
        System.out.println("Invalid MP3 File!");
        e.printStackTrace();
        System.exit(0);
    }

}

@Override
public void run() {
    playMP3(file);
}

private void playWav(File file) {
    try {
        AudioInputStream ais = AudioSystem.getAudioInputStream(file);
        DataLine.Info dataInfo = new DataLine.Info(Clip.class, null);
        clip = (Clip) AudioSystem.getLine(dataInfo);
        clip.open(ais);
        clip.start();
    } catch (Exception e) {
        e.printStackTrace();
        System.err.println("Invalid .wav file!");
        System.exit(0);
    }
}

}

Main class:

public static void main(String[] args) {

File file = new File(/*PATH_TO_FILE_HERE*/);
AudioTools at = new AudioTools(file);
at.play();
// at.clip.getMicrosecondPosition() works here if loaded file is .wav.
// at.player.getPosition() returns null if loaded file is .mp3.
}

From the main class, I'd like to be able to get the player's current position. Currently, trying this will return null.

Any helpful comments are appreciated. I've asked a similar question before, this edit made the question a bit more clear and shorter.

EDIT: Should contain all code necissary to run the example.

Upvotes: 0

Views: 55

Answers (2)

mindoverflow
mindoverflow

Reputation: 424

Solved using a different approach that isn't even part of the question. I already had an array of the decrypted audio data that I can turn into an AudioInputStream that Clip is able to play.

Upvotes: 0

ddarellis
ddarellis

Reputation: 3960

I am not sure if i completed understood your question but i will try to explain some things to you.

Lets say you have a class Clip where at a random time of 300ms to 1000ms (just for example) plays one second of the clip and sets the variable currentLength which is what you want to get from other thread.

public class Clip implements Runnable {

    private int clipLength;

    private volatile int currentLength;

    public Clip(int clipLength) {
        this.clipLength = clipLength;
    }

    public void run() {

        while (currentLength <= clipLength) {
            try {
                currentLength += 1;
                Thread.sleep(ThreadLocalRandom.current().nextInt(300, 1000 + 1));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    public int getCurrentLength() {
        return currentLength;
    }

    public void setCurrentLength(int currentLength) {
        this.currentLength = currentLength;
    }

}

Also lets say you have a class Player and what it does is that takes the clip object and after some time it requests the current length of it.

public class Player implements Runnable {

    private Clip clip;

    public Player(Clip clip) {
        this.clip = clip;
    }

    public void run() {
        try {
            Thread.sleep(1000);
            System.out.println(clip.getCurrentLength());
            Thread.sleep(2000);
            System.out.println(clip.getCurrentLength());
            Thread.sleep(1000);
            System.out.println(clip.getCurrentLength());
            Thread.sleep(3000);
            System.out.println(clip.getCurrentLength());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Also we have a Main class containg the start of the program where we create a Clip object and pass it to the to runnable class.

public class Main {
    public static void main(String[] args) {
        Clip clip = new Clip(60);
        Thread thread = new Thread(clip);
        thread.start();
        Thread threadPlayer = new Thread(new Player(clip));
        threadPlayer.start();

    }

}

I think this is what you asked for but before implement it to your program you should read about atomic access and volatile keyword and also is good to know about the Java memory model.

Upvotes: 1

Related Questions