Wav-Sound abspielen

Hey.

Ich habe nen Musikplayer. der MP3’s ohne Probleme abspielen kann, jedoch kann JLayer anscheinend keine WAV. Ich dachte mir kein Problem, das kann java ansich ja eh schon. Aber egal wie ich es versuche: Ich höre nichts:

            AudioInputStream old = AudioSystem.getAudioInputStream(f);
            AudioInputStream ais = AudioSystem.getAudioInputStream(new AudioFormat(44100,
                    16,
                    2,
                    true,
                    false), old);
            Clip clip = AudioSystem.getClip();
            clip.open(ais);
            clip.loop(Clip.LOOP_CONTINUOUSLY);
            clip.start();
            AudioClip aClip = Applet.newAudioClip(f.toURI().toURL());
            aClip.play();
        } catch  (IOException ex) {
            ex.printStackTrace();
        } catch (LineUnavailableException ex) {
            ex.printStackTrace();
        } catch (UnsupportedAudioFileException ex) {
            ex.printStackTrace();
        }```

Die File kann ich problemlos im VLC oä abspielen. Aber egal ob ich clip.open(ais); oder clip.open(old); benutze, ich höre nichts und das Programm läuft auch nichtmal ne Sekunde. Es wir auch keine Exception geworfen, habe bereits 3 Wav-Dateien probiert.

Es gibt einige Wav-Formate, die die JVM nunmal nicht spielt. JLayer kann daran auch nichts ändern, aber dann müsste auch eine Exception fliegen. Das Problem hier dürfte sein, dass das Programm nicht auf den Clip wartet.

import static javax.sound.sampled.AudioFormat.Encoding.PCM_UNSIGNED;

import java.io.File;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineEvent.Type;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.AudioFormat.Encoding;

public class AudioDemo {
	public static void main(String[] args) throws Throwable {
		if(args == null || args.length == 0) {
			args = new String[] {"test.wav"};
		}
		File f = new File(args[0]);
		AudioInputStream ais = ensurePCM(AudioSystem.getAudioInputStream(f));
		Clip c = AudioSystem.getClip();
		c.open(ais);
		c.start();
// ab hier wirds wichtig
		final Thread t = Thread.currentThread();
		c.addLineListener(new LineListener() {
			@Override
			public void update(LineEvent event) {
				if(event.getType() == Type.STOP) {
					synchronized(t) {
						t.notify();
					}
				}
			}
		});
		synchronized(t) {
			try {
				t.wait();
			} catch(InterruptedException e) {
				// do nothing
			}
		}
	}

	private static final AudioInputStream ensurePCM(AudioInputStream source)
	{
		AudioFormat af = source.getFormat();
		Encoding enc = af.getEncoding();
		if(!PCM_SIGNED.equals(enc) && !PCM_UNSIGNED.equals(enc)) {
			int ssb = af.getSampleSizeInBits();
			int c = af.getChannels();
			if(ssb < 8) {
				ssb = 8;
			}
			int minFs = c * ssb / 8;
			int fs = af.getFrameSize();
			if(fs < minFs) {
				fs = minFs;
			}
			af = new AudioFormat(
					AudioFormat.Encoding.PCM_SIGNED,
					af.getSampleRate(),
					ssb * 2,
					af.getChannels(),
					fs * 2,
					af.getSampleRate(),
					af.isBigEndian()
				);
			source = AudioSystem.getAudioInputStream(af, source);
		}
		return source;
	}
}```
BTW.: Das Umformen eines AudioStreams ist nur dann nötig, wenn das Encoding nicht PCM_SIGNED oder PCM_UNSIGNED ist. Zu diesem Zweck empfieht es sich, diese Umformunf als Utility-Methode (hier "ensurePCM()") auszulagern. Gerne darf man sich fragen, warum es eine solche nicht in der API gibt, bzw. warum die API das Soundformat beim Abspielen nicht automatisch anpasst.

Danke, mit der Methode kann ichs dann abspielen. Hätte ich aber auch drauf kommen können. BEvor ich jetz aber anfange, einen neue PLayer-Klasse für Wav-Datein zu schreiben bzw den vorhandenen umschreibe, das er erkennt, welche Endung die Audiodatei hat (Momentan hat er nen einfachen Inputstream), kann ich nciht auch einfach JLayer dazu bringen, das er es abspielt? Ich hoffe mal, wer kennt sich damit aus, ansonsten änder ich den Player halt. Bei ner Wav kommt immer die Exception:

	at javazoom.jl.decoder.LayerIDecoder$SubbandLayer1Stereo.read_allocation(Unknown Source)
	at javazoom.jl.decoder.LayerIDecoder.readAllocation(Unknown Source)
	at javazoom.jl.decoder.LayerIDecoder.decodeFrame(Unknown Source)
	at javazoom.jl.decoder.Decoder.decodeFrame(Unknown Source)
	at musikplayer.erweiterungen.player.MyPlayer.decodeFrame(MyPlayer.java:158)
	at musikplayer.erweiterungen.player.MyPlayer.play(MyPlayer.java:89)
	at musikplayer.erweiterungen.player.MyPlayer.play(MyPlayer.java:68)
	at musikplayer.PlayerTest.main(PlayerTest.java:28)```
In der Klasse in der Methode wird auf ein Array mit 15 Inhalten zugegriffen, aber es wird der Index 15 gebraucht, einer zuviel. ICh hab das soweit mal zurückverfolgt und es wird anscheinend der Anfangs erzeugte Bitstream immer weitergegeben, aber am Anfang gibt die bitstream.get_bits(4); Methode 0 aus. Hmpf, muss ich nochmal drüberschauen

Hmm… der musicplayer scheint etwas ausserhalb von JLayer zu sein. Ist das was eigenes? Dann wäre etwas Code hilfreich.

Das ist was eigenes, und zwar mein Musikplayer in dem ich das einbauen möchte. der “MyPlayer” sSt quasi der Advanced-Player aus JLayer, nur um ne Pause/Resume MEthode erweitert, sowie ne Methode zur Lautstärkeregelung, mehr ist da nicht anders. und der Rest von meinem Projekt hat mit dem Test hier nichts am Hut, da ich es erstmal in meiner Testklasse zum laufen bekommen möchte, bevor ich es in mein Hauptprojekt einbaue.

Da ist zumindest soviel anders, dass die JLayer-Methoden nicht mehr funktionieren. Was glaubst du, wo der Fehler wohl steckt? In der JLayer-API oder in deinem Player? Ich tippe ja mal auf letzteres. Von daher kann es hier ohne etwas Code nicht weitergehen.

In der JLayer-APi. Ansonsten würde das hier ja funktionieren:

        File f3 = new File("D:\\Musik\\Musik-Oberordner\\Effekte\	rolololol\	rolololol.wav");
        File f2 = new File("D:\\Musik\\Musik-Oberordner\\Favoriten\\SOFT CELL   TAINTED LOVE HQ official video mp4.mp3");
        final Thread t = Thread.currentThread();
        try {
            AudioInputStream old = AudioSystem.getAudioInputStream(f);
            AudioInputStream ais = AudioSystem.getAudioInputStream(new AudioFormat(44100,
                    16,
                    2,
                    true,
                    false), old);
            System.out.println(ais);
            System.out.println(ais.getFormat().getSampleSizeInBits());
            FileInputStream fis = new FileInputStream(f);
            AdvancedPlayer player = new AdvancedPlayer(fis);
            player.setPlayBackListener(new PlaybackListener() {
                @Override
                public void playbackFinished(PlaybackEvent evt) {
                    synchronized (t) {
                        System.out.println("Finished");
                        t.notify();
                    }
                }

            });
//            player.setGain(100f);
            player.play();
            synchronized (t) {
                try {
                    System.out.println("wait");
                    t.wait();
                } catch (InterruptedException e) {
                    // do nothing
                }
            }

        } catch (JavaLayerException ex) {
            ex.printStackTrace();
        } catch (UnsupportedAudioFileException | IOException ex) {
            ex.printStackTrace();
        }```
Und zum anderen haben die neuen MEthoden von mri keinen EInfluss auf den Player, solange sie nciht aufgerufen werden:
ORiginal AdvancedPlayer:
```public class AdvancedPlayer
{
	/** The MPEG audio bitstream.*/
	private Bitstream bitstream;
	/** The MPEG audio decoder. */
	private Decoder decoder;
	/** The AudioDevice the audio samples are written to. */
	private AudioDevice audio;
	/** Has the player been closed? */
	private boolean closed = false;
	/** Has the player played back all frames from the stream? */
	private boolean complete = false;
	private int lastPosition = 0;
	/** Listener for the playback process */
	private PlaybackListener listener;

	/**
	 * Creates a new <code>Player</code> instance.
	 */
	public AdvancedPlayer(InputStream stream) throws JavaLayerException
	{
		this(stream, null);
	}

	public AdvancedPlayer(InputStream stream, AudioDevice device) throws JavaLayerException
	{
		bitstream = new Bitstream(stream);

		if (device!=null) audio = device;
		else audio = FactoryRegistry.systemRegistry().createAudioDevice();
		audio.open(decoder = new Decoder());
	}

	public void play() throws JavaLayerException
	{
		play(Integer.MAX_VALUE);
	}

	/**
	 * Plays a number of MPEG audio frames.
	 *
	 * @param frames	The number of frames to play.
	 * @return	true if the last frame was played, or false if there are
	 *			more frames.
	 */
	public boolean play(int frames) throws JavaLayerException
	{
		boolean ret = true;

		// report to listener
		if(listener != null) listener.playbackStarted(createEvent(PlaybackEvent.STARTED));

		while (frames-- > 0 && ret)
		{
			ret = decodeFrame();
		}

//		if (!ret)
		{
			// last frame, ensure all data flushed to the audio device.
			AudioDevice out = audio;
			if (out != null)
			{
//				System.out.println(audio.getPosition());
				out.flush();
//				System.out.println(audio.getPosition());
				synchronized (this)
				{
					complete = (!closed);
					close();
				}

				// report to listener
				if(listener != null) listener.playbackFinished(createEvent(out, PlaybackEvent.STOPPED));
			}
		}
		return ret;
	}

	/**
	 * Cloases this player. Any audio currently playing is stopped
	 * immediately.
	 */
	public synchronized void close()
	{
		AudioDevice out = audio;
		if (out != null)
		{
			closed = true;
			audio = null;
			// this may fail, so ensure object state is set up before
			// calling this method.
			out.close();
			lastPosition = out.getPosition();
			try
			{
				bitstream.close();
			}
			catch (BitstreamException ex)
			{}
		}
	}

	/**
	 * Decodes a single frame.
	 *
	 * @return true if there are no more frames to decode, false otherwise.
	 */
	protected boolean decodeFrame() throws JavaLayerException
	{
		try
		{
			AudioDevice out = audio;
			if (out == null) return false;

			Header h = bitstream.readFrame();
			if (h == null) return false;

			// sample buffer set when decoder constructed
			SampleBuffer output = (SampleBuffer) decoder.decodeFrame(h, bitstream);

			synchronized (this)
			{
				out = audio;
				if(out != null)
				{
					out.write(output.getBuffer(), 0, output.getBufferLength());
				}
			}

			bitstream.closeFrame();
		}
		catch (RuntimeException ex)
		{
			throw new JavaLayerException("Exception decoding audio frame", ex);
		}
		return true;
	}

	/**
	 * skips over a single frame
	 * @return false	if there are no more frames to decode, true otherwise.
	 */
	protected boolean skipFrame() throws JavaLayerException
	{
		Header h = bitstream.readFrame();
		if (h == null) return false;
		bitstream.closeFrame();
		return true;
	}

	/**
	 * Plays a range of MPEG audio frames
	 * @param start	The first frame to play
	 * @param end		The last frame to play
	 * @return true if the last frame was played, or false if there are more frames.
	 */
	public boolean play(final int start, final int end) throws JavaLayerException
	{
		boolean ret = true;
		int offset = start;
		while (offset-- > 0 && ret) ret = skipFrame();
		return play(end - start);
	}

	/**
	 * Constructs a <code>PlaybackEvent</code>
	 */
	private PlaybackEvent createEvent(int id)
	{
		return createEvent(audio, id);
	}

	/**
	 * Constructs a <code>PlaybackEvent</code>
	 */
	private PlaybackEvent createEvent(AudioDevice dev, int id)
	{
		return new PlaybackEvent(this, id, dev.getPosition());
	}

	/**
	 * sets the <code>PlaybackListener</code>
	 */
	public void setPlayBackListener(PlaybackListener listener)
	{
		this.listener = listener;
	}

	/**
	 * gets the <code>PlaybackListener</code>
	 */
	public PlaybackListener getPlayBackListener()
	{
		return listener;
	}

	/**
	 * closes the player and notifies <code>PlaybackListener</code>
	 */
	public void stop()
	{
		listener.playbackFinished(createEvent(PlaybackEvent.STOPPED));
		close();
	}
}```
Meine MyPlayer:
```public class MyPlayer implements Closeable{

    /**
     * The MPEG audio bitstream.
     */
    private Bitstream bitstream;
    /**
     * The MPEG audio decoder.
     */
    private Decoder decoder;
    /**
     * The AudioDevice the audio samples are written to.
     */
    private AudioDevice audio;
    /**
     * Has the player been closed?
     */
    private boolean closed = false;
    /**
     * Has the player played back all frames from the stream?
     */
    private boolean complete = false;
    private int lastPosition = 0;
    /**
     * Listener for the playback process
     */
    private MyPlaybackListener listener;
    private volatile boolean paused = false;

    /**
     * Creates a new <code>Player</code> instance.
     *
     * @param stream
     * @throws JavaLayerException
     */
    public MyPlayer(InputStream stream) throws JavaLayerException {
        this(stream, null);
    }

    public MyPlayer(InputStream stream, AudioDevice device, int gainValue) throws JavaLayerException {
        bitstream = new Bitstream(stream);
        if (device != null) {
            audio = device;
        } else {
            audio = FactoryRegistry.systemRegistry().createAudioDevice();
        }
        
        audio.open(decoder = new Decoder());
    }

    public boolean play() throws JavaLayerException {
        return play(Integer.MAX_VALUE);
    }

    /**
     * Plays a number of MPEG audio frames.
     *
     * @param frames	The number of frames to play.
     * @return	true if the last frame was played, or false if there are more
     * frames.
     * @throws JavaLayerException
     */
    @SuppressWarnings("SleepWhileInLoop")
    public boolean play(int frames) throws JavaLayerException {
        boolean ret = true;

        // report to listener
        if (listener != null) {
            listener.playbackStarted(createEvent(PlaybackEvent.STARTED));
        }

        while (frames-- > 0 && ret) {
            ret = decodeFrame();
            while (paused) {
                try {
                    Thread.sleep(20);
                } catch (InterruptedException ex) {
                }
            }

        }

//        if (!ret) {
        // last frame, ensure all data flushed to the audio device.
        AudioDevice out = audio;
        if (out != null) {
//				System.out.println(audio.getPosition());
            out.flush();
//				System.out.println(audio.getPosition());
            synchronized (this) {
                complete = (!closed);
                close();
            }

            // report to listener
            if (listener != null) {
                listener.playbackFinished(createEvent(out, PlaybackEvent.STOPPED));
            }
        }
//        }
        return ret;
    }

    /**
     * Cloases this player. Any audio currently playing is stopped immediately.
     */
    @Override
    public synchronized void close() {
        AudioDevice out = audio;
        if (out != null) {
            closed = true;
            audio = null;
            // this may fail, so ensure object state is set up before
            // calling this method.
            out.close();
            lastPosition = out.getPosition();
            try {
                bitstream.close();
            } catch (BitstreamException ex) {
            }
        }
    }

    /**
     * Decodes a single frame.
     *
     * @return true if there are no more frames to decode, false otherwise.
     * @throws JavaLayerException
     */
    protected boolean decodeFrame() throws JavaLayerException {
        try {
            AudioDevice out = audio;
            if (out == null) {
                return false;
            }

            Header h = bitstream.readFrame();
            if (h == null) {
                return false;
            }

            // sample buffer set when decoder constructed
            SampleBuffer output = (SampleBuffer) decoder.decodeFrame(h, bitstream);

            synchronized (this) {
                out = audio;
                if (out != null) {
                    out.write(output.getBuffer(), 0, output.getBufferLength());
                }
            }

            bitstream.closeFrame();
        } catch (RuntimeException ex) {
            throw new JavaLayerException("Exception decoding audio frame", ex);
        }
        return true;
    }

    /**
     * skips over a single frame
     *
     * @return false	if there are no more frames to decode, true otherwise.
     * @throws JavaLayerException
     */
    protected boolean skipFrame() throws JavaLayerException {
        Header h = bitstream.readFrame();
        if (h == null) {
            return false;
        }
        bitstream.closeFrame();
        return true;
    }

    public final boolean setGain(float newGain) {
        if (audio instanceof JavaSoundAudioDevice) {
            JavaSoundAudioDevice jsAudio = (JavaSoundAudioDevice) audio;
            if (jsAudio.isSourceNull()) {
                try {
                    jsAudio.createSource();
                } catch (JavaLayerException ex) {
                    ex.printStackTrace();
                }
            }
            return jsAudio.setLineGain(newGain);
        }
        return false;
    }

    /**
     * Plays a range of MPEG audio frames
     *
     * @param start	The first frame to play
     * @param end	The last frame to play
     * @return true if the last frame was played, or false if there are more
     * frames.
     * @throws JavaLayerException
     */
    public boolean play(final int start, final int end) throws JavaLayerException {
        boolean ret = true;
        int offset = start;
        while (offset-- > 0 && ret) {
            ret = skipFrame();
        }
        return play(end - start);
    }

    /**
     * Constructs a <code>PlaybackEvent</code>
     */
    private MyPlaybackEvent createEvent(int id) {
        return createEvent(audio, id);
    }

    /**
     * Constructs a <code>PlaybackEvent</code>
     */
    private MyPlaybackEvent createEvent(AudioDevice dev, int id) {
        return new MyPlaybackEvent(this, id, dev.getPosition());
    }

    /**
     * sets the <code>PlaybackListener</code>
     *
     * @param listener
     */
    public void setPlayBackListener(MyPlaybackListener listener) {
        this.listener = listener;
    }

    /**
     * gets the <code>PlaybackListener</code>
     *
     * @return
     */
    public MyPlaybackListener getPlayBackListener() {
        return listener;
    }

    /**
     * closes the player and notifies <code>PlaybackListener</code>
     */
    public void stop() {
        listener.playbackFinished(createEvent(PlaybackEvent.STOPPED));
        complete = true;
        close();
    }

    public int getPosition() {
        int position = lastPosition;

        AudioDevice out = audio;
        if (out != null) {
            position = out.getPosition();
        }
        return position;
    }

    public synchronized boolean isComplete() {
        return complete;
    }

    public boolean isPaused() {
        return paused;
    }

    public int getLastPosition() {
        return lastPosition;
    }

    public synchronized void pause() {
        if (!complete) {
            System.out.println("Pause");
            paused = true;
        }
    }

    public synchronized void resume() {
        if (paused) {
            System.out.println("Play");
            paused = false;
        }
    }
    
    public boolean isClosed() {
        return closed;
    }
    
}```

DIe Veränderungen sing eig nur das hier:

public MyPlayer(InputStream stream, AudioDevice device, int gainValue) throws JavaLayerException {
bitstream = new Bitstream(stream);
if (device != null) {
audio = device;
} else {
audio = FactoryRegistry.systemRegistry().createAudioDevice();
}
setGain(gainValue);
audio.open(decoder = new Decoder());
}
public boolean play(int frames) throws JavaLayerException {
//…
ret = decodeFrame();
while (paused) {
try {
Thread.sleep(20);
} catch (InterruptedException ex) {
}
}
//…
}
public final boolean setGain(float newGain) {
if (audio instanceof JavaSoundAudioDevice) {
JavaSoundAudioDevice jsAudio = (JavaSoundAudioDevice) audio;
if (jsAudio.isSourceNull()) {
try {
jsAudio.createSource();
} catch (JavaLayerException ex) {
ex.printStackTrace();
}
}
return jsAudio.setLineGain(newGain);
}
return false;
}
public synchronized void pause() {
if (!complete) {
System.out.println(“Pause”);
paused = true;
}
}

public synchronized void resume() {
    if (paused) {
        System.out.println("Play");
        paused = false;
    }
}```

Ändert also nicht wirklich was daran, finde ich. Und der originale packt es ja auch nicht. Gleiche Exception

Das klappt ja schon mal deswegen nicht, weil der JLayer-Player ein wenig anders funktioniert - du musst nicht warten, bis der Track abgespielt ist. Wenn du wartest, wird der Player anscheinend gestoppt und feuert einen entsprechenden Event. Sicher bin ich mir da zwar nicht, aber so könnte es sein. Ich habe auch irgendwie das Gefühl, dass der JLayer-Player Probleme mit recht kurzen Clips hat und auch sonst ist er nicht das Gelbe vom Ei.
Lt. StackOverflow kann man in diesem einen Pause-Mechanismus der ganz anderen Art implementieren, dazu benötigt man den PlaybackListener in welchem man nach einem Stop-Event die Frameposition speichert und beim Resume den Player an genau dieser Stelle wieder startet. Lautstärkeregelung und das alles (Pitch, Balance…) funktioniert damit wohl auch nur recht schwer, so dass es meines Erachtens nach schon Sinn macht, sich seinen eigenen Player komplett über die Java eigene Sound-API zusammen zu frickeln, mit Controls und SourceDataLines. :wink:

Ansonsten würde ich erst gar nicht versuchen, einen komplett neuen Player zu programmieren, solange man den alten erweitern kann. Das spart einem im Allgemeinen sehr viel Arbeit.

Ja das warten beim JLayer ist sinnlos, im Hauptprogramm tue ich es auch nicht, ist hier nur ein Überbleibsel gewesen, da davor dort der Clip war. UNd das warten ansich ändert nichts am abspiel-Algo selbst.
Zum Pause-Algo: Ja das wäre möglich, aber meine Lösung finde ich deutlich einfacher gelöst und mit sehr wenig AUfwand auch anwendbar.
Zum lautstärke etc: Ja, das war sogar unmöglich bis ich die JavSoundAudioDevice zum Teil selbst neu geschrieben habe und in der Library ersetzt. Hat mich auch paar Tage gekostet. Nun kann ich dort die Lautstärke mit setLineGain() setzen.

Und selbst den Player schreiben. Habe ich auch schon oft nachgedacht drüber, aber allein schon als ich gesehen habe wie kompliziert JLayer aufgebaut ist, damit man MP3-abspielen kann, hat mich das abgeschreckt, zumal ich von der Materie ansich noch wenig Ahnung habe.

habe im Prinzip einfach die 2 Stellen, an denen der Player gestartet wird etwas verändert:

                        player.play();
                    } catch (JavaLayerException ex) {
                        myLogger.log(ex);
                        System.out.println("Trying Clip");
                        tryClip();
                    }```
Die exception fliegt, wenn man wav abspielen will. Danach wird der Clip gestartet und versucht das abzuspielen. Das möchte ich jedoch wieder rauslöschen, da ich das sehr unschön finde und  mich auch einige Arbeit gekostet hat, da der Player und CLip jeweils anders zu handhaben sind und dadurch einige andere Stellen teils gravierende BUgs ausgelöst haben, die ich bis jetz noch nciht gelöst habe. Entferne ich das ganze wieder funktionierts wieder. Also das kann nciht die lösung sein.. Mal schauen, evtl mach ich den Player dann wirklich selbst

Warum sollte man sich einen Player entwickeln, der wiederum von nicht hauseigenen Librarys abhängig ist?

Schau dir das mal an:

https://www.dropbox.com/s/4r62j797lgr92tu/AudioPlayer.jar?dl=1

Die Klasse AudioClip ist weitgehend dokumentiert bis auf die Umstände, warum ich da so ein Heck-Meck um die Lautstärke-Einstellungen mache, aber dieser Heck-Meck ist leider nötig, weil die interne SoundAPI Pegelwerte (in Dezibell) statt lineare (zwischen 0% und 100%) verlangt. :wink:

Die Klasse Test verdeutlicht, wie weit sich der restliche Programieraufwand mit einem korrekt implementierten Player reduziert und die Klasse AudioPlayer liefert einen (denke ich) ausbaufähigen Player mit GUI.

Getestet habe ich das Ganze schon mal erfolgreich mit MP3s (Vorrausgesetzt die entsprechenden JavaZoom-Pakete sind installiert) und WAVs.

Okay, alles klar, schaue ich mir morgen dann mal genauer an.
Aber davor noch eine Frage:
wieso gibt mir:

import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;

public class NoJavazoom {

    public static void main(String[] args) {
        try {
            File f = new File("D:\\Musik\\Musik-Oberordner\\Favoriten\\SOFT CELL   TAINTED LOVE HQ official video mp4.mp3");
            AudioInputStream old = AudioSystem.getAudioInputStream(f);
            AudioInputStream ais = AudioSystem.getAudioInputStream(new AudioFormat(old.getFormat().getSampleRate(),
                    16,
                    2,
                    true,
                    false), old);
            System.out.println(ais);
            System.out.println(ais.getFormat().getSampleSizeInBits());
            System.out.println(old);
        } catch (UnsupportedAudioFileException | IOException ex) {
            ex.printStackTrace();
        }
    }```
das hier aus:
```javazoom.spi.mpeg.sampled.convert.DecodedMpegAudioInputStream@4c873330
16
javax.sound.sampled.AudioInputStream@119d7047```

Wieso wird hier javazoom verwendet, obwohl ichs nicht angegeben habe ?

Das ist schnell beantwortet. Das MP3-SPI wurde von den selben Leuten entwickelt, die auch JLayer entwickelt haben. Klar, dass die bei anderen Entwicklungen auch auf ihre Libs zurückgreifen, wenn dies erforderlich ist. Kannst dir ja mal die Mühe machen und dir den Quelltext zu Triton, JLayer und MP3SPI ansehen, dann siehst du die Zusammenhänge.

Hab e mir den Player von dir mal in ner eigenen Testklasse ausprobiert und muss sagen, er funktioniert tadellos. Finds nur leicht umständlich das ich auf 0.0001f Genauigkeit meine Lautstärke angeben muss, da ansonsten nicht viel Unterschied zu hören ist^^ Aber das übernimmt später eh mein JSlider, also nur halb so wild. UNd deine pause() und resume() Methoden haben gebuggt, habe das mal gefixxt. Bei resume musst du auf position > 0 prüfen, nicht <0, da du in Pause ja die momentane Position setzt und die nie unter 0 ist^^
Dazu musste ich den CLip sehr umständlich mit

 AudioInputStream old = AudioSystem.getAudioInputStream(f2);
            AudioFormat fmt = old.getFormat();
            AudioClip clip = AudioClip.create(AudioSystem.getAudioInputStream(new AudioFormat(fmt.getSampleRate(), 16, fmt.getChannels(), fmt.getEncoding() == AudioFormat.Encoding.PCM_SIGNED, false), old));```
instanziieren, da er mir ansonsten dauernd BigEndian und 24+bit Formate geliefert hat, die er nicht abspielen kann. aber ansonsten perfekt.
Habe mich zu dem Anlass entschlossen schwerer Herzens das Projekt aufzugeben und komplett neu zu machen, da es ansonsten zu verworren gewesen wäre, den alten Player zu ersetzen. Und zudem war es sehr unübersichtlich. Bin grad schon recht gut dabei, aber bis ich dazu komme, den Player zu nutzen, dauerts noch etwas^^
Zumal ich grad schon an etwas sehr einfachem Hänge:
``` private void addFiles(File[] audioFiles) {
        if (audioFiles != null && audioFiles.length > 0) {

            List<Song> songs = new ArrayList<>();
            for (File f : audioFiles) {
                try {
                    songs.add(new Song(f));
                } catch (UnsupportedAudioFileException | IOException ex) {
                    System.err.println("Fehler!");
                }
            }
            addFiles(songs);
        }
    }

    private void addFiles(List<Song> songs) {
        songs.stream().forEach((song -> {
            int anzahlSongs = songList.size();
            if (anzahlSongs >= musikDateienTabelle.getRowCount()) {
                ((DefaultTableModel) getTableModel()).addRow(new Vector<>());
            }
            getTableModel().setValueAt(song.getTitle(), anzahlSongs, musikDateienTabelle.getColumn("Titel").getModelIndex());
            getTableModel().setValueAt(Formatter.formatMillis(song.getDuration() / 1000), anzahlSongs, musikDateienTabelle.getColumn("Dauer").getModelIndex());
            getTableModel().setValueAt(song.getInterpret(), anzahlSongs, musikDateienTabelle.getColumn("Interpret").getModelIndex());
            getTableModel().setValueAt(song.getAlbum(), anzahlSongs, musikDateienTabelle.getColumn("Album").getModelIndex());
            songList.add(song);
        }));
    }

    private void sort(SongComparator comparator) {
        if (songList.size() > 0) {
            songList.sort(comparator);
            addFiles(songList);
        }
    }```
Über nen Filechooser bekommt erste Methode das Array und leitet an die 2. Weiter. die wiederum fügt meiner Tabelle (Eine langersehnte Verbesserung meines alten Players, die ich schon lange haben wollte) die Dateien hinzu. Das funktioniert auch wunderbar. Allerdings habe ich eine Combobox, die die Tabelle sortieren soll. Allerdings fliegt dabei, mit herkömmlicher foreach-Schleife oder Streams, immer ne Exception:
```Exception in thread "AWT-EventQueue-0" java.util.ConcurrentModificationException
	at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1380)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
	at com.youtube.mrschesam.musikplayer.gui.Gui.addFiles(Gui.java:486)
	at com.youtube.mrschesam.musikplayer.gui.Gui.sort(Gui.java:503)
	at com.youtube.mrschesam.musikplayer.gui.Gui.sortOptionsActionPerformed(Gui.java:452)
	at com.youtube.mrschesam.musikplayer.gui.Gui.access$500(Gui.java:56)
	at com.youtube.mrschesam.musikplayer.gui.Gui$6.actionPerformed(Gui.java:179)
	at javax.swing.JComboBox.fireActionEvent(JComboBox.java:1258)
	at javax.swing.JComboBox.setSelectedItem(JComboBox.java:586)
	at javax.swing.JComboBox.setSelectedIndex(JComboBox.java:622)
	at javax.swing.plaf.basic.BasicComboPopup$Handler.mouseReleased(BasicComboPopup.java:852)
	at java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:290)
	at java.awt.Component.processMouseEvent(Component.java:6535)
	at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
	at javax.swing.plaf.basic.BasicComboPopup$1.processMouseEvent(BasicComboPopup.java:501)
	at java.awt.Component.processEvent(Component.java:6300)
	at java.awt.Container.processEvent(Container.java:2236)
	at java.awt.Component.dispatchEventImpl(Component.java:4891)
	at java.awt.Container.dispatchEventImpl(Container.java:2294)
	at java.awt.Component.dispatchEvent(Component.java:4713)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
	at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4525)
	at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
	at java.awt.Container.dispatchEventImpl(Container.java:2280)
	at java.awt.Window.dispatchEventImpl(Window.java:2750)
	at java.awt.Component.dispatchEvent(Component.java:4713)
	at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
	at java.awt.EventQueue.access$500(EventQueue.java:97)
	at java.awt.EventQueue$3.run(EventQueue.java:709)
	at java.awt.EventQueue$3.run(EventQueue.java:703)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
	at java.awt.EventQueue$4.run(EventQueue.java:731)
	at java.awt.EventQueue$4.run(EventQueue.java:729)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)```
Gui.486 ist dabei die Zeile `songs.stream().forEach((song -> {`, aber ich sehe da kein Problem, schließlich ist die Liste ja in Ordnung.

Edit: Ich bin dumm.. Ich kann ja während ich an einer Liste rumiteriere keine Veränderung an dieser vornehmen... NAja Bin noch müde^^

so ein stattliches Posting zusammengeschraubt, Problem auf die richtige Codestelle zurückgeführt
und zumindest Zusammenhang zur Liste von Songs hergestellt, aber da dann Ende des Gedankengangs?

‘Liste in Ordnung’ wird sich letztlich als falsche Aussage erweisen, aber warum überhaupt so ein sicheres Gefühl dazu,
einfach den Fakten folgen, von außen freilich dann unbeeinflusst doch leichter:

es gibt eine ConcurrentModificationException, das sollte jede eigene Überzeugung erstmal zertrümmern, Neubewertung schadet nie,
wenn nicht die Welt Kopf steht dann bedeutet das, dass in der Liste etwas verändert wurde,

-> was ist das für eine Liste, wo kommt sie her, wer kennt sie noch und fügt was ein,
nebenläufige Threads wie meist hier sicher keine Frage, höchstwahrscheinlich und einfach ist immer der Schleifencode der Methode selber anzuschauen,
dort ja auch reichlich Verdächtige (getTableModel()).addRow() fügt irgendwas irgendwo ein, schließlich auch noch songList.add(song); )

wo kommt denn der Parameter her, was ist das für eine Liste?
der StackTrace zeigt dass vorher Methode sort() dranwar, kurzes Spiel:
addFiles(songList);

die Methode addFiles() wird also mit dem Instanzattribut songList ausgeführt,
dort wird jeder Eintrag in DIESELBE Liste wieder einfügt,
sei froh dass du die billige ConcurrentModificationException bekommst, sonst evt. Endlosschleife, immer neu hinein und neu hineinzufügen


wenn du schon ein Instanzattribut songList hast, dann ist es besser zu vermeiden, dieses als Attribut an eine andere Methode der eignen Klasse zu übergeben,
addFiles() kann doch genauso mit songList arbeiten, machst du dort ja auch,

dass du songList als zwei verschiedene Dinge benutzt ist ein Dilemma was du erstmal fachlich klären musst, was soll denn passieren?
eine Liste kann doch nicht gleichzeitig neue (zu übertragene) Einträge wie auch alte (bereits übertragene) Einträge enthalten

addFiles(File[]) erstellt dagegen seine eigene ArrayList, da mag es funktionieren, aber sort() ist bisher anders,
was genau planst du?


int anzahlSongs = songList.size();
sieht auch etwas komisch aus, die Bedingung dazu ist ziemlich unabhängig davon, welchen Song du gerade aus der Parameter-Liste bearbeitest

songList zu sortieren ohne das TableModel zu ändern kann auch böse enden,

verzichte vielleicht lieber auf eine eigene songList, lasse allein das TableModel eine Liste der Songs verwalten,
dazu bietet sich ein eigenes TableModel mit getValueAt()-Methode an,

schau dir hier an
JTable (Tutorial)© – Byte-Welt Wiki
wie ein TableModel für Vehicel-Objekte erstellt wird,
die Objekte können direkt genutzt werden, Liste (oder da veraltet Vector) vorgehalten, Sortierung in Model möglich,

auch ‘musikDateienTabelle.getColumn(“Titel”).getModelIndex()’ usw. fällt weg

  1. Ja, da hast du Recht… Ich habe diese Änderung mit „position = -1“ (also invalidieren nach Start und Stop) erst nach den ganzen Tests vorgenommen. Resume darf natürlich erst dann vorgenommen werden, wenn position >= 0 ist.

2. Ich nehme mal an, dass du innerhalb deiner fehlerhaften for-each-Schleife keine Dateien hinzufügen, sondern eher entfernen willst. In diesem Fall nutzt man kein for-each sondern einen Iterator und eine while-Schleife („while(it.hasNext()) {…“). Wenn man dann während der Iteration über die Elemente ein element aus der Collection entfernen will geht das ganz simpel mit „it.remove()“. Vergiss das wieder… :wink:

Hatte ja den Edit reingetan, aber anscheinend zu spät^^ Bin dann auch drauf gekommen, das da was gewaltig nicht stimmt und hab dafür dann eine Kopie der Liste in Sort erstellt und diese dann übergeben:

        if (songList.size() > 0) {
            songList.sort(comparator);
            List<Song> sortedList = new ArrayList<>(songList);
            songList.clear();
            anzahlSongs = 0;
            addFiles(new ArrayList<>(sortedList));
        }
    }```
 Aber die Idee, das ganz ohne die Liste zu machen, nur mit dem Table Model ist interessant, nur ich weiß nicht wie ich das anstellen soll. In der Tabelle werden ja in 4 Spalten nebeneinander 4 Attribute von meinem Song angezeigt (Momentan Dauer, Titel, Album und Interpret). Wie soll ich darauf meinen Song wiederbekommen? Am besten ohne die anderen, nicht angezeigten, Informationen zu verlieren? Mir fiel dazu nur eine Liste ein, die neben der Tabel alle Lieder speichert.
Hab das ganze dann so stehen:
```private void addFiles(File[] audioFiles) {
        if (audioFiles != null && audioFiles.length > 0) {

            List<Song> songs = new ArrayList<>();
            for (File f : audioFiles) {
                try {
                    songs.add(new Song(f));
                } catch (UnsupportedAudioFileException | IOException ex) {
                    ex.printStackTrace();
                }
            }
            addFiles(songs);
        }
    }

    private void addFiles(List<Song> songs) {
        songs.stream().forEach((song -> {
            int anzahlSongs = songList.size();
            if (anzahlSongs >= musikDateienTabelle.getRowCount()) {
                ((DefaultTableModel) getTableModel()).addRow(new Vector<>());
            }
            getTableModel().setValueAt(song.getTitle(), anzahlSongs, musikDateienTabelle.getColumn("Titel").getModelIndex());
            getTableModel().setValueAt(Formatter.formatMillis(song.getDuration() / 1000), anzahlSongs, musikDateienTabelle.getColumn("Dauer").getModelIndex());
            getTableModel().setValueAt(song.getInterpret(), anzahlSongs, musikDateienTabelle.getColumn("Interpret").getModelIndex());
            getTableModel().setValueAt(song.getAlbum(), anzahlSongs, musikDateienTabelle.getColumn("Album").getModelIndex());
            songList.add(song);
        }));
    }

    private void sort(SongComparator comparator) {
        if (songList.size() > 0) {
            songList.sort(comparator);
            List<Song> sortedList = new ArrayList<>(songList);
            songList.clear();
            addFiles(new ArrayList<>(sortedList));
        }
    }```

Wie soll ich das ganze dann ohne die Liste lösen?
Edit: Habe den Link übersehen. Schaue mir den mal an.

das Tablemodel dort ist schon alt und etwas unnötig detailliert zu Events + Listener, dies hier ginge auch, auch mit einer Methode sort(),

agiert etwas grob mit fireTableDataChanged() bei jeder kleinen Änderung, aber daran sollte normalerweise nichts bemerkbar oder problematisch sein

Vehicel vs. Vehicle ist auch noch ein starkes Stück im Wiki…

    private String[] columnNames = new String[]  {"Name", "Fahrgäste", "Räder", "Besitzt Motor"};
    private Class[] columnClasses = new Class[] {String.class, Integer.class, Integer.class, Boolean.class};
    private List<Vehicle> vehicles = new ArrayList<>();

    public void addVehicle(Vehicle vehicle) {
        this.vehicles.add(vehicle);
        fireTableDataChanged();
    }

    public void sort() {
        Collections.sort(this.vehicles);
        fireTableDataChanged();
    }

    @Override
    public int getColumnCount() {
        return this.columnNames.length;
    }

    @Override
    public int getRowCount() {
        return this.vehicles.size();
    }

    @Override
    public String getColumnName(int column) {
        return this.columnNames[column];
    }

    @Override
    public Class getColumnClass(int column) {
        return this.columnClasses[column];
    }

    @Override
    public Object getValueAt(int row, int column)    {
        Vehicle v = this.vehicles.get(row);
        switch (column) {
            case 0:
                return v.getName();
            case 1:
                return Integer.valueOf(v.getPlaces());
            case 2:
                return Integer.valueOf(v.getWheels());
            case 3:
                return v.hasMotor() ? Boolean.TRUE : Boolean.FALSE;
            default:
                return null;
        }
    }

    @Override
    public boolean isCellEditable(int row, int column)  {
        return false;
    }

    @Override
    public void setValueAt(Object aValue, int row, int column)  {
        // nicht beachten
    }
}

wenn dort die Liste abgefragt, kann das die zentrale Speicherung sein,
längerfristrig vielleicht Logikschicht von GUI zu trennen, aber zumindest in der GUI keine zweite große Liste zu verwalten