Java - Error when trying to use mp3plugin for play

2020-05-01 06:22发布

问题:

I am writing a music visualiser and am trying to play mp3 files in java using an mp3plugin (http://pscode.org/lib/mp3plugin.jar). However I've come across an error, probably due to my lack of experience using this plugin and coding music in general.

I have tested this code with a wide range of mp3 files, all with the same problem, I believe I can rule out with some confidence that the mp3 files are at fault.

My code below:

AudioInputStream audioIn;
try {
    JS_MP3FileReader m = new JS_MP3FileReader();
    audioIn = m.getAudioInputStream(new File(ex.trackname));
    //audioIn = AudioSystem.getAudioInputStream(new File(ex.trackname));
    // Get a sound clip resource.
    Clip clip = AudioSystem.getClip();
    // Open audio clip and load samples from the audio input stream.
    clip.open(audioIn);
    clip.start();
} catch (Exception e) {
    e.printStackTrace();
}

The error I get:

javax.sound.sampled.LineUnavailableException: line with format MPEG1L3 48000.0 Hz, unknown bits per sample, stereo, unknown frame size, unknown frame rate,  not supported.
at com.sun.media.sound.DirectAudioDevice$DirectDL.implOpen(Unknown Source)
at com.sun.media.sound.DirectAudioDevice$DirectClip.implOpen(Unknown Source)
at com.sun.media.sound.AbstractDataLine.open(Unknown Source)
at com.sun.media.sound.DirectAudioDevice$DirectClip.open(Unknown Source)
at com.sun.media.sound.DirectAudioDevice$DirectClip.open(Unknown Source)
at Logic.Run$1.run(Run.java:101)
at java.lang.Thread.run(Unknown Source)

(note the class instance I wrote that runs this code is called Run)

My project structure below, including the plugin file:

回答1:

I use JavaSound directly to play mp3 audios and it works quite well

Heres a full working code (have fun):

package XXXXXXXXXXXXXX
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.sampled.*;


public class AudioPlayer
{
    private int bufferSize = 4096; // Tamanho de buffer padrão 4k
    private volatile boolean paused = false;
    private final Object lock = new Object();
    private SourceDataLine line;
    private int secondsFade = 0;
    private ArrayList<AudioPlayerListener> _listeners = new ArrayList<AudioPlayerListener>();

    public void stop()
    {
        if(line != null)
        {
            line.stop();
            line.close();
        }
    }

    public boolean isPaused()
    {
        return this.paused;
    }


    public void pause()
    {
        if(!this.isPaused())
            paused = true;
    }

    public void resume()
    {
        if(this.isPaused())
        {
            synchronized(lock){
                lock.notifyAll();
                paused = false;
            }
        }
    }

    public void play(File file) throws UnsupportedAudioFileException, IOException, LineUnavailableException, InterruptedException
    {
        AudioInputStream encoded = AudioSystem.getAudioInputStream(file);
        AudioFormat encodedFormat = encoded.getFormat();
        AudioFormat decodedFormat = this.getDecodedFormat(encodedFormat);
        Long duration = null;
        AudioInputStream currentDecoded = AudioSystem.getAudioInputStream(decodedFormat, encoded);
        line = AudioSystem.getSourceDataLine(decodedFormat);
        line.open(decodedFormat);
        line.start();
        boolean fezFadeIn = false;
        boolean fezFadeOut = false;
        byte[] b = new byte[this.bufferSize];
        int i = 0;
        Map properties = null;
        try 
        {
            properties = AudioUtil.getMetadata(file);
            duration = (Long) properties.get("duration");
        } 
        catch (Exception ex)
        {
            duration = 0L;
        }

        duration = duration < 0 ? 0 : duration;

        synchronized(lock)
        {
            //Parametro que ativa ou não o fade de acordo com o tamanho do áudio
            long paramFade = (secondsFade*2+1)*1000000;
            //long paramFade = 0;
            //Logger.getLogger(this.getClass().getName()).info("Arquivo: "+file+", DURACAO DO AUDIO: "+duration+", paramfade: "+paramFade);
            while(true)
            {
                if(secondsFade > 0 && !fezFadeIn && duration >= paramFade)
                {
                    fezFadeIn = true;
                    fadeInAsync(this.secondsFade);
                }

                if( secondsFade > 0 &&
                        duration > paramFade &&
                        !fezFadeOut &&
                        line.getMicrosecondPosition() >= duration - ((this.secondsFade+1)*1000000) )
                {
                    this.fireAboutToFinish();
                    fadeOutAsync(this.secondsFade);
                    fezFadeOut = true;
                }

                if(paused == true)
                {
                    line.stop();
                    lock.wait();
                    line.start();
                }

                i = currentDecoded.read(b, 0, b.length);
                if(i == -1)
                    break;

                line.write(b, 0, i);
            }
        }

        if(  !fezFadeOut && line.isOpen() )
            this.fireAboutToFinish();

        line.drain();
        line.stop();
        line.close();
        currentDecoded.close();
        encoded.close();
    }

    public synchronized void fadeInAsync(final int seconds)
    {
        if(line != null && line.isOpen())
        {
            Thread t = new Thread(new Fader(true, this, secondsFade));
            t.start();
        }
    }

    public synchronized void fadeOutAsync(final int seconds)
    {
        if(line != null && line.isOpen())
        {
            Thread t = new Thread(new Fader(false, this, secondsFade));
            t.start();            
        }
    }

    public void setVolume(double value) 
    {
        if(!line.isOpen())
            return;
        // value is between 0 and 1
        value = (value<=0.0)? 0.0001 : ((value>1.0)? 1.0 : value);
        try
        {
            float dB = (float)(Math.log(value)/Math.log(10.0)*20.0);
            ((FloatControl)line.getControl(FloatControl.Type.MASTER_GAIN)).setValue(dB);
        }
        catch(Exception ex)
        {

        }
    }

    public boolean isPlaying()
    {
        return (line != null && line.isOpen());
    }


    protected AudioFormat getDecodedFormat(AudioFormat format)
    {
        AudioFormat decodedFormat = new AudioFormat(
                AudioFormat.Encoding.PCM_SIGNED,  // Encoding to use
                format.getSampleRate(),           // sample rate (same as base format)
                16,               // sample size in bits (thx to Javazoom)
                format.getChannels(),             // # of Channels
                format.getChannels()*2,           // Frame Size
                format.getSampleRate(),           // Frame Rate
                false                 // Big Endian
        );
        return decodedFormat;    
    }


    public int getBufferSize()
    {
        return bufferSize;
    }


    public void setBufferSize(int bufferSize)
    {
        if(bufferSize <= 0)
            return;
        this.bufferSize = bufferSize;
    }

    /**
     * @return the secondsFade
     */
    public int getSecondsFade() {
        return secondsFade;
    }

    /**
     * @param secondsFade the secondsFade to set
     */
    public void setSecondsFade(int secondsFade) {
        if(secondsFade < 0 || secondsFade > 10)
            throw new IllegalArgumentException("Erro ao configurar cross-fade com valor em segundos: "+secondsFade);
        this.secondsFade = secondsFade;
    }

    public void addAudioPlayerListener(AudioPlayerListener a)
    {
        this._listeners.add(a);
    }

    public void removeAudioPlayerListener(AudioPlayerListener a)
    {
        this._listeners.remove(a);
    }

    private void fireAboutToFinish()
    {
        for(AudioPlayerListener a : this._listeners)
            a.aboutToFinish(this);
    }
}


class Fader implements Runnable
{
    private boolean fadeIn;
    private int seconds=0;
    private final AudioPlayer player;
    private float increaseParam;

    public Fader(boolean fadeIn, AudioPlayer player, int secondsToFade) 
    {
        this.fadeIn = fadeIn;
        this.seconds = secondsToFade;
        this.player = player;
        if(fadeIn)
            increaseParam = 0.01F;
        else
            increaseParam = -0.01F;
    }

    @Override
    public void run() 
    {
        try
        {
            encapsulateRun();
        }
        catch(Exception ex)
        {
            if(fadeIn)
                player.setVolume(1.0F);
            else
                player.setVolume(0.0F);
        }
    }

    private void encapsulateRun() throws Exception
    {
        synchronized(player)
        {            
            float per;
            if(fadeIn)
            {
                Logger.getLogger(getClass().getName()).info("Fazendo fade in");
                per = 0.0F;
            }
            else
            {
                Logger.getLogger(getClass().getName()).info("Fazendo fade out");
                per = 1.0F;
            }
            player.setVolume(per);
            if(fadeIn)
            {
                while(per < 1.00F)
                {
                    per = per + increaseParam;
                    player.setVolume(per);
                    Thread.sleep(10*seconds);
                }
            }
            else
            {
                while(per > 0.00F)
                {
                    per = per + increaseParam;
                    player.setVolume(per);
                    Thread.sleep(10*seconds);
                }
            }
        }
    }

}

It also has support for Fade in and out mp3 All you need is to add 3 jars to your project. I have tested with those versions:

jl.1.0.1.jar (or newer) mp3spi1.9.5.jar (or newer)

found at http://www.javazoom.net/projects.html

tritonus_share.jar

found at http://www.tritonus.org/

AudioUtil.java

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioSystem;
public class AudioUtil
{
    private static ArrayList<FileMap> cache = new ArrayList<FileMap>();
    private static int _CACHE_SIZE = 5;

    public static Map<String, Object> getMetadata(File filename) throws Exception
    {
        FileMap fm = new FileMap(filename, null);
        int index = cache.indexOf(fm);
        if(index >= 0)
            return cache.get(index).getMap();

        AudioFileFormat format = AudioSystem.getAudioFileFormat(filename);

        Map<String, Object> mapa = new HashMap<String, Object>();
        mapa.putAll(format.properties());


        if(mapa.get("author") == null && filename.getName().contains(" - "))
        {
            mapa = new HashMap<String, Object>();
            String[] s = filename.getName().split(" - ");
            mapa.put("author", s[0]);
            s[1] = s[1].substring(0, s[1].length()-4);
            mapa.put("title", s[1]);
        }

        if(mapa.get("author") == null)
        {
            mapa.put("author", "Desconhecido");
            mapa.put("title", "Desconhecido");
        }

        Object o = format.properties().get("duration");

        if(o == null)
            mapa.put("duration", 0);

        fm.setMap(mapa);
        cache.add(fm);
        while(cache.size() > _CACHE_SIZE)
            cache.remove((int)0);

        return mapa;
    }
}

class FileMap
{
    private File file;
    private Map<String, Object> map;

    public FileMap(File file, Map<String, Object> map) 
    {
        this.file = file;
        this.map = map;
    }

    public File getFile() {
        return file;
    }

    public void setFile(File file) {
        this.file = file;
    }

    public Map<String, Object> getMap() {
        return map;
    }

    public void setMap(Map<String, Object> map) {
        this.map = map;
    }

    @Override
    public boolean equals(Object obj) 
    {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final FileMap other = (FileMap) obj;
        if (this.file != other.file && (this.file == null || !this.file.equals(other.file))) 
        {
            return false;
        }
        return true;
    }

}