Störender Lag zwischen 2 Clips

Status
Nicht offen für weitere Antworten.
Hallo

Ich suche zur Zeit verzweifelt nach einer Möglichkeit zu Prüfen, ob ein Clip fast zu Ende ist... Ich habe vor, einen kleines Intro Clip abzuspielen, und direkt im Anschluss einen anderen. Wenn ich das mit dem LineEvent probiere, sieht das bis jetzt so aus:

Java:
public void m_clip_update(LineEvent evt)
  {
    if(m_clip.isRunnging()==false)
    {
      b_clip.loop(b_clip.LOOP_CONTINUOUSLY);
      m_clip.close();
    }
  }

(m_clip ist das Intro, b_clip das eigentliche Stück)
Dabei kommt das Problem auf, dass zwischen dem Intro und des Stücks ein durchaus hörbarer Lag entsteht, weil ich ja eben erst nachdem der Clip zu Ende ist prüfen kann, ob er auch wirklich zu Ende ist, und dann erst den neuen starten kann. Gibt es da irgendeine Möglichkeit das zu umgehen?

Vielen Dank für eure Antworten im Vorraus

MfG
PattXterminator
 

hdi

Top Contributor
Also ich kann jetzt zur API von Sounds usw nix sagen, da ich damit noch nicht gearbeitet habe. Aber du erzählst irgendwas von LineEvents... Du nutzt das Event doch gar nicht :bahnhof:
 
S

Spacerat

Gast
Man kann doch mit der Differenz aus [c]getMircosecondLength()[/c] und [c]getMicrosecondPosition()[/c] feststellen, wann der Clip beendet werden soll. Behält man diesen Wert im Auge, kann man rechtzeitig drauf reagieren und den 2. Clip starten. Das garantiert aber immer noch nicht, das der Clip dann auch nahtlos startet. Will man das ganze nahtlos haben, müsstest du dir sozusagen einen eigenen Clip-Mixer entwickeln, welcher eine SourceDataLine verwendet. Werden vom ersten Clip dann -1 (Clip-Ende) gelesen, kann diese gleich mit den Daten des 2. Clips gefüllt werden. Für den Fall, das du mir grad' nicht folgen kannst, versuch' ich mal ein Beispiel zusammen zu frickeln.
 
Hallo,
danke für die Antworten erstmal

Aber du erzählst irgendwas von LineEvents... Du nutzt das Event doch gar nicht :bahnhof:

Doch, das tue ich. Das LineEvent triggert, wenn der Linienstatus geändert wird. Wenn der Clip zu Ende ist, ist das dann genau der Fall. Und dann gucke ich, ob der Clip mit isRunning()==false auch wirklich zu Ende ist.

Man kann doch mit der Differenz aus [c]getMircosecondLength()[/c] und [c]getMicrosecondPosition()[/c] feststellen, wann der Clip beendet werden soll. Behält man diesen Wert im Auge, kann man rechtzeitig drauf reagieren und den 2. Clip starten.

Ja, aber das Problem an der Sache ist, dass ich dabei die Line permanent überwachen muss. Mein Programm sieht bisher so aus:

Java:
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.Clip;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;


public class ClipPlayer
{


  // Anfang Attribute
  private Clip m_clip;
  private Clip b_clip;
  // Ende Attribute


  public ClipPlayer(String musik_intro, String musik_theme)
  {
    File clipFileIntro = new File(musik_intro);
    AudioInputStream audioInputStreamIntro = null;
    try
    {
      audioInputStreamIntro = AudioSystem.getAudioInputStream(clipFileIntro);
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
    AudioFormat formatIntro = audioInputStreamIntro.getFormat();
    DataLine.Info infoIntro = new DataLine.Info(Clip.class, formatIntro);
    try
    {
      m_clip = (Clip)AudioSystem.getLine(infoIntro);
      m_clip.open(audioInputStreamIntro);
    }
    catch (LineUnavailableException e)
    {
      e.printStackTrace();
    }
    catch (IOException e)
    {
      e.printStackTrace();
    }

    File clipFileTheme = new File(musik_theme);
    AudioInputStream audioInputStreamTheme = null;
    try
    {
      audioInputStreamTheme = AudioSystem.getAudioInputStream(clipFileTheme);
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
    AudioFormat formatTheme = audioInputStreamTheme.getFormat();
    DataLine.Info infoTheme = new DataLine.Info(Clip.class, formatTheme);
    try
    {
      b_clip = (Clip)AudioSystem.getLine(infoTheme);
      b_clip.open(audioInputStreamTheme);
    }
    catch (LineUnavailableException e)
    {
      e.printStackTrace();
    }
    catch (IOException e)
    {
      e.printStackTrace();
    }

    
    m_clip.addLineListener(new LineListener() {
      public void update(LineEvent evt) {
        m_clip_update(evt);
      }
    });

    System.out.println(m_clip.getFrameLength()+"");
    m_clip.start();
  }

  // Anfang Methoden

  public void stoppe()
  {
    m_clip.stop();
    m_clip.close();
    b_clip.stop();
    b_clip.close();
  }
  
  public void volume(double pVolume)
  {
    FloatControl gainControlTheme = (FloatControl) m_clip.getControl(FloatControl.Type.MASTER_GAIN);
    float dB = (float)(Math.log(pVolume)/Math.log(10.0)*20.0);
    gainControlTheme.setValue(dB);
    FloatControl gainControlIntro = (FloatControl) b_clip.getControl(FloatControl.Type.MASTER_GAIN);
    gainControlIntro.setValue(dB);
  }
  
  public void m_clip_update(LineEvent evt)
  {
    if(m_clip.isRunning()==false)
    {
      b_clip.loop(b_clip.LOOP_CONTINUOUSLY);
      m_clip.close();
    }
  }
  // Ende Methoden
}

Ich erledige die ganze Arbeit schon im Constructor, damit ich dann in meiner GUI ein Objekt pro Stück erzeuge. Wenn ich da jetzt einmal [c]if(m_clip.getMircosecondLength()-m_clip.getMicrosecondPosition()==100)[/c] reinschreibe, überprüft er das einmal wenn ich das Objekt erzeuge, das bringt dann auch nix mehr....


Das garantiert aber immer noch nicht, das der Clip dann auch nahtlos startet. Will man das ganze nahtlos haben, müsstest du dir sozusagen einen eigenen Clip-Mixer entwickeln, welcher eine SourceDataLine verwendet. Werden vom ersten Clip dann -1 (Clip-Ende) gelesen, kann diese gleich mit den Daten des 2. Clips gefüllt werden. Für den Fall, das du mir grad' nicht folgen kannst, versuch' ich mal ein Beispiel zusammen zu frickeln.

Hmm ich hab jetzt noch nicht ganz so viel Ahnung von Sound in Java, also ich kann dir nicht wirklich folgen... ^^

Danke für weitere Antworten schonmal im Vorraus ^^
MfG,
PattXterminator
 
S

Spacerat

Gast
Hmm ich hab jetzt noch nicht ganz so viel Ahnung von Sound in Java, also ich kann dir nicht wirklich folgen...
Jepp... das ist unverkennbar... sorry...
Der Clip bietet dir über die Events nicht die Möglichkeit, die sich dahinter verborgene DataLine "am Leben" zu halten, d.H. er bietet dir nicht die Möglichkeit, die Line weiter zu befüllen, wenn Clip-Ende signalisiert wurde. Deswegen ist er für deine Ansprüche völlig ungeeignet. Deswegen (und jetzt bitte nicht erschrecken...;) ) hier mal 'ne Version, womit man das Ende von Clip1 abwartet und anschliessend Clip2 in die selbe Line looped.
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.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;

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 UnsupportedAudioFileException, IOException, LineUnavailableException
	{
		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 UnsupportedAudioFileException, IOException, LineUnavailableException
	{
		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 {
				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) {
								if(source != clip2) {
									source = clip2;
								} else {
									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
			}
		}
	}
}
Naja... sieht schlimmer aus als es ist. Das Beispiel geht im übrigen von zwei Dateien gleichen Formats aus (also nicht nur wav, sondern auch 22kHz, 16Bit, Mono).
 
Hallo,

ist nicht so schlimm, bin ja selber noch am Lernen ^^ Hab das jetzt mal ausprobiert und funktioniert an sich ganz super. Nur wenn ich jetzt aus meiner GUI die main Methode mit passendem String Array aufrufe, verlangt er immernoch eine UnsupportedAudioFileException...

MfG
PattXterminator
 
S

Spacerat

Gast
Ja OK... Dann mal was "schmutziges"... Einfach mal die ganzen geworfenen Exceptions in einer einzigen zusammenfassen... also aus [c]throws UnsupportedAudioFileException, IOException, LineUnavailableException[/c] schlicht [c]throws Exception[/c] machen. Das ist dann die einzige die abgefangen werden muss. Ansonsten musst du alle der Reihe nach abfangen. D.h., wenn du dem Wunsch der IDE entsprochen hast und die [c]UnsupportedAudioFileException[/c] abgefangen hast, meckert diese nämlich die beiden anderen auch noch an.
 
Hallo,

jetzt meckert er rum, dass er die normale Exception abgefangen haben will... Bei mir sieht das grade so aus, vielleicht mache ich ja was grundlegendes falsch >_<

[JAVA=18]
private ClipStreamer player;
[/code]

[JAVA=990]
String[] so = {"C:/Anime/Out Of ControlBegin.wav", "C:/Anime/Out Of ControlTest.wav"};
player.main(so);
[/code]

Und die Fehlermeldung:
cGUI.java:991:18: unreported exception java.lang.Exception; must be caught or declared to be thrown
player.main(so);
(Die Fehlermeldung verweist auf die öffnende Klammer)

MfG
PattXtermiantor
 
S

Spacerat

Gast
ja, nee... is Klar... die musst du abfangen oder weiter reichen. Ab Zeile 990 müsste das etwa so aussehen:[JAVA=990]String[] so = {"C:/Anime/Out Of ControlBegin.wav", "C:/Anime/Out Of ControlTest.wav"};
try {
player.main(so);
} catch(Exception e) {
e.printStackTrace();
// wenn im Fehlerfall das Programm beendet werden soll...
System.exit(-1);
}[/code]
 
Hallo,

ah achso, jetzt funktioniert alles ^^ Tschuldigung, dass ich so noobish bin... :'D
Und die letzte Frage: Darf ich deinen Code für mein Projekt verwenden?

EDIT:
Es ist für nichtkommerzielle Zwecke und nur für den Privatgebrauch

MfG
PattXterminator
 
Zuletzt bearbeitet:
S

Spacerat

Gast
Natürlich... Sag' ich mal... Hab' die Forenregeln zwar jetzt nicht soo unbedingt im Kopf, aber irgendwo geht daraus hervor, dass man auf eigene, hier veröffentlichte Codeschnipsel keinerlei Rechte (ausgenommen dem Urheberrecht vllt.) mehr hat. Aber nochmal was anderes. Ich würde die Klasse nicht über dessen [c]main()[/c] instanzieren. Ich würde vielmehr den Kostruktor [c]public[/c] machen und mit [c]new[/c] instanzieren. Dann hat man nämlich seine Instanz und kann darauf nach belieben [c]start()[/c] und [c]stop()[/c] aufrufen.
 
Zuletzt bearbeitet von einem Moderator:
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen


Oben