Search code examples
javaaudiojavasound

Java sound control not working


My Java sound-play starts with error but does not play sound.

I've searched the web but it still doesn't work D:

can you please help me understand my problem + fix?

if you need any more details, code, dumps, outputs, data just let me know.

Sound.java:

package com.itaysharon.questematic;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;

import com.itaysharon.questematic.enums.SoundOptions;

public class Sound {

public static Thread dj;

public static synchronized void playSound(final String url, SoundOptions mode) {
    dj = new Thread(new Runnable() {

        @Override
        public void run() {
            try {
                AudioInputStream inputStream = AudioSystem.getAudioInputStream(new File("assets" + File.separator + url));
                AudioSystem.getClip().open(inputStream);
                AudioSystem.getClip().setFramePosition(0);
                switch(mode) {
                    case Stop:
                        AudioSystem.getClip().stop();
                        break;
                    case Play:
                        AudioSystem.getClip().start();
                        break;
                    case Loop:
                        AudioSystem.getClip().loop(Clip.LOOP_CONTINUOUSLY);
                        break;
                }
            } catch (Exception e) {
                System.err.println(e.getMessage());
            }
        }
    });
    if (mode != SoundOptions.Stop) {
        dj.start();
    } else {
        try {
            AudioSystem.getClip().stop();
            AudioSystem.getClip().close();
            dj.interrupt();
        } catch (LineUnavailableException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}
}

SoundOptions.java:

package com.itaysharon.questematic.enums;

public enum SoundOptions {
    Play, Loop, Stop;
}

Solution

  • OK after checking your code, there was quite small things here and there.

    Before I start, maybe your code has worked coincidentally once previously and stopped working afterwards, this is typically when thread is not well programmed.

    The problem is that the thread starts and ends before the audio file get the chance to play, because the thread does not know how long the file is in this case.

    And since I do not have the full logic of how your program behaves, I will keep your code logic and added some few things that solves the problem as an isolated instance. Btw, I have tested the fixed the code and it works great as isolated class in my machine.

    I would mentioned the key points for the solution. It is about letting the thread continue with the audio playing time, till it is ended by the listener.

    audioLineClip.addLineListener(new LineListener() {
        @Override
        public void update(LineEvent event) {
            ... listen when audio is ended and close the line. to end the program.
        }
    });
    

    and we keep our thread waiting till audio is ended

    synchronized (dj) {
        while (true) {
            try {
                dj.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    You could theoretically use Thread.sleep(sometime) alone without synchronized, but since you do not know how long to sleep as you do not know how long the audio file is!

    So your final code would look like this, I have put the solution in the code with explanation including extra minor changes with notes:

    import javax.sound.sampled.*;
    import java.io.File;
    
    /**
     * By Maytham on 07-10-2016.
     */
    public class Sound {
    
        public static void main(String[] args) {
            playSound("8k16bitpcm.wav", SoundOptions.Play);
        }
    
        // 1) make it private
        private static Thread dj;
    
        // 2) make it private and 3) SoundOptions should be final
        private static synchronized void playSound(final String url, final SoundOptions mode) {
            dj = new Thread(new Runnable() {
    
                @Override
                public void run() {
                    try {
                        AudioInputStream inputStream = AudioSystem.getAudioInputStream(
                                new File("assets" + File.separator + url));
    
                        // 4) declare AudioSystem in stead of using AudioSystem repeatedly
                        final Clip audioLineClip = (Clip) AudioSystem.getLine(
                                new Line.Info(Clip.class));
                        audioLineClip.open(inputStream);
                        audioLineClip.setFramePosition(0);
    
                        // 5) our line listener checks when audio is ended and stops the line
                        //this is full example, but you manipulated your way
                        audioLineClip.addLineListener(new LineListener() {
                            @Override
                            public void update(LineEvent event) {
                                LineEvent.Type type = event.getType();
                                if (type == LineEvent.Type.OPEN) {
                                } else if (type == LineEvent.Type.CLOSE) {
                                    System.exit(0);
                                } else if (type == LineEvent.Type.START) {
                                } else if (type == LineEvent.Type.STOP) {
                                    audioLineClip.close();
                                }
                            }
                        });
    
                        switch (mode) {
                            case Stop:
                                audioLineClip.stop();
                                break;
                            case Play:
                                audioLineClip.start();
                                break;
                            case Loop:
                                audioLineClip.loop(Clip.LOOP_CONTINUOUSLY);
                                break;
                        }
                    } catch (Exception e) {
                        System.err.println(e.getMessage());
                    }
                }
            });
            if (mode != SoundOptions.Stop) {
                dj.start();
    
                // 6) this keep the thread until some line listener change status
                synchronized (dj) {
                    while (true) {
                        try {
                            dj.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
    
            } else {
                dj.interrupt();
    
                // 7) you do not need this it is done by line listener
                /*try {
                    AudioSystem.getClip().stop();
                    AudioSystem.getClip().close();
                    dj.interrupt();
                } catch (LineUnavailableException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }*/
    
            }
        }
    }