Audio + Timer

mmz27

Mitglied
Hallo zusammen

Mein Programm soll ein Bild auf dem Bildschirm anzeigen und 200ms später einen Ton abspielen. Dies habe ich wie folgt versucht zu lösen:

Java:
//load sound file
java.net.URL url = getClass().getResource("Beep.wav");
clip = Applet.newAudioClip(url);

....

//place picture
centreLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("bild.gif")));

//start timer for sound file
Date runTime = new Date(System.currentTimeMillis() + 200);
playSoundTimer = new java.util.Timer();
playSoundTimer.schedule(new TimerTask() {
	public void run() {
		clip.play();  
	}
}, runTime);

Nun ist das Problem, dass dieses Sound File nicht exakt 200ms nach dem Bild abgespielt wird sondern mit einem Delay von 50-250ms. Mir ist durchaus bewusst, dass dies eine sehr kurze Zeit ist, meine Anwendung ist zeitkritisch und soll so exakt wie möglich sein.

Hat jemand eine Idee, wie ich das genauer machen kann?

Danke & Viele Grüsse
 
Java:
//Date runTime = new Date(System.currentTimeMillis() + 200);
playSoundTimer = new java.util.Timer();
playSoundTimer.schedule(new TimerTask() {
	public void run() {
		clip.play();  
	}
}, new Date(System.currentTimeMillis() + 200));

Vielleich ist das ja schon etwas besser? Weil System.currentTimeMillis() läuft ja auch immer weiter...
 
Versuch doch alternativ mal auf nanotime() zu wechseln und statt dem Timer einen Busywait zu verwenden.
Da solltest Du besser treffen. Dann kannst Du mal schauen, wie es mit verschieden großen Sleep(...) aussieht.
 
Danke für die schnellen Antworten.

Ich habe das mit wait versucht zu implementieren, was aber leider den gleichen delay zur Folge hatte.

Ich habe mal anstelle von clip.play(), das setIcon(null) gesetzt und danach am Oszilloskop mit einer Photodiode die Zeit gemessen, bei der das Bild angzeigt wird. Diese Zeit ist genau 200ms. Das heisst, dass das abspielen der Sounddatei zum delay führt.

Gibt es alternativen Methoden, um ein *.wav abzuspielen?

Vielen Dank!

Java:
 //load sound file
java.net.URL url = getClass().getResource("Beep.wav");
clip = Applet.newAudioClip(url);
 
....
 
//place picture
centreLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("bild.gif")));
 
//start timer for sound file
playSoundTimer = new java.util.Timer();
playSoundTimer.schedule(new TimerTask() {
    public void run() {
        //clip.play();  
        centreLabel.setIcon(null);
    }
}, new Date(System.currentTimeMillis() + 200));
 
Mit Clip wirst das kaum hinbekommen, denk' ich aber auf jeden Fall sollte es mit 'nem eigenen StreamerThread funktionieren, welcher den Clip - ist hier jetzt 'ne ganz schlechte Wortwahl, AudioStream wäre besser - widerholt, lückenlos aneinanderreiht. Einfach wird's nicht.
 
@muckelzwerg: sooo schwierig muss es nun doch nicht werden. Am ende fragt er noch, wie er JOAL in ein 64-Bit Betriebssystem einbinden kann :lol:. Für sein Vorhaben sollte die std. "javax.sound.sampled"-API genügen.
 
Zuletzt bearbeitet von einem Moderator:
Hm. Ist schon eine Weile her, aber damals war das ziemlich einfach.
Weiß nicht, ob sowas noch aktuell ist:
Java OpenAL tutorial


mmz27, ich fände es RICHTIG toll, wenn Du bei Gelegenheit mal ein kleines Tutorial über die Zeitmessung mit Oscar und Photodiode machen könntest.
Das können einige Leute bestimmt mal gebrauchen.
 
@muckelzwerg: Also dein Tutorial dort mitsamt OpenAL-Anbindung ist mir völlig unbekannt. Mir scheint, dass man in Sachen Hardwarebindung in Java immer mehr dazu neigt, die jeweiligen C-Libraries per JNI in Java zu "spiegeln", was meines Erachtens auch durchaus Sinn macht. Gängige Bindungen dieser Art sin

1. LWJGL: für OpenAL, OpenGL und OpenCL. Kommt allem am nächsten unterstützt aber meines Wissens keine Lightweight-Components wie z.B. Swing ist dafür aber für 32-Bit und 64-Bit verfügbar.

2. JOAL: für OpenAL. Ist ein wenig mehr objektorientierter, ist aber für Windows z.B. nur in 32-Bit verfügbar.

3. JOGL: für OpenGL. Ist ebenso wie JOAL auch ein wenig mehr objektorientierter und unterstützt auch Lightweight-Components. Seit Version 2.0 wurde es aber gravierend verändert. 2.0 ist afaik noch im Beta-Stadium und das schon recht lange.

4. JOCL: für OpenCL. Damit habe ich noch keine Erfahrungen gemacht.

Ich selbst verwende inzwischen LWJGL und verzichte dabei auf Lightweights. Der Vorteil hier: Man kann auch Tutorials durcharbeiten, welche für andere Sprachen, z.B. "C" oder "C#" konzipiert wurden, ohne gross den Code ändern zu müssen. So nun aber genug davon. Back to Topic 😉
 
WIe gesagt, das ist schon ein Weilchen her. Letztlich hab ich dann JASS für Java und RTAudio für C++ verwendet.
Aber ich hatte mit OpenAL zwischendurch keine Probleme, an die ich mich erinnere.
Was Du jetzt mit JOGL und JOCL willst, versteh ich nicht. mmz27 hat doch nach alternativen Abspielmöglichkeiten gefragt.
 
Was Du jetzt mit JOGL und JOCL willst, versteh ich nicht.
Hatte ich nur der Vollständigkeit halber erwähnt. Never mind.
Ich glaube auch kaum, dass irgend etwas davon bzw. jedwede andere Nicht-Standard-API auch nur geringfügig eine Alternative für den TS darstellt. Wie es aussieht, will er etwas mit Applets machen und da empfiehlt sich das Einbinden von externen APIs die obendrein noch JNI-Libs einbinden müssen keineswegs. Ich versuch' mich grad' an einer Alternative mit dem oben besagtem "javax.sound.sampled"-API.
 
Sorry für'n Doppelpost.
@TS: Ich bin hier auf ein älteres Beispiel mit 2 Audioclips gestossen, die direkt nacheinander abgespielt werden sollten. Die beiden Clips wurden hier jeweils in einen AudioStream gespeichert, welche durch die Verschachtelung in einem BufferedInputStream markierbar und auch zurücksetzbar werden. Mit ein wenig Javakenntnissen sollte es kein Problem sein, das Beispiel für deine Zwecke umzuschreiben.
Java:
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.SourceDataLine;

public class ClipStreamer
{
	private final AudioInputStream clip1, clip2;
	private Thread runner;
	private final byte[] buffer;
	private final SourceDataLine line;

	public static void main(String[] args)
	throws Exception
	{
		if(args == null || args.length < 2) {
			args = new String[] {"chimes.wav", "win.wav",};
		}
		new ClipStreamer(args[0], args[1]).start();
	}

	private ClipStreamer(String clip1, String clip2)
	throws Exception
	{
		BufferedInputStream c1 = new BufferedInputStream(new FileInputStream(new File(clip1)));
		BufferedInputStream c2 = new BufferedInputStream(new FileInputStream(new File(clip2)));
		this.clip1 = AudioSystem.getAudioInputStream(c1);
		this.clip2 = AudioSystem.getAudioInputStream(c2);
		AudioFormat af = this.clip1.getFormat();
		buffer = new byte[32768];
		line = AudioSystem.getSourceDataLine(af);
		line.open(af, buffer.length);
	}

	public synchronized void start()
	{
		if(runner == null) {
			try {
				clip1.mark(clip1.available());
				clip2.mark(clip2.available());
			} catch(IOException e) {
				e.printStackTrace();
				return;
			}
			runner = new Thread()
			{
				@Override
				public void run()
				{
					AudioInputStream source = clip1;
					int bytesRead;
					while(!isInterrupted()) {
						try {
							bytesRead = source.read(buffer, 0, buffer.length);
							if(bytesRead <= 0) {
								source = (source != clip2)? clip2 : clip1;
								source.reset();
								source.mark(source.available());
							} else {
								line.write(buffer, 0, bytesRead);
							}
						} catch (IOException ioe) {
							interrupt();
						}
					}
					line.flush();
					line.stop();
					runner = null;
				}
			};
			line.start();
			runner.start();
		}
	}

	public synchronized void stop()
	{
		if(runner != null) {
			runner.interrupt();
			while(runner != null) {
				// wait
			}
		}
	}
}
 
Zuletzt bearbeitet von einem Moderator:
Wenn Du es damit nicht lösen kannst, dann schau Dir vielleicht wirklich mal jass an.
Jass demos
Unter Umständen liegt es ja vielleicht an der Puffergröße oder was Ähnlichem.
 
Zuletzt bearbeitet:

Zurück
Oben