2015-11-16 9 views
15

Ich habe ein Problem mit der Erstellung von AudioInputStream von Socket. Hier sind die wichtigen Teile:"Endless" AudioInputStream von Socket

public class SoundStream extends Thread { 
    private int port; 
    private String IP; 
    private Socket socket; 

    private SoundObject soundObject; 

    private OpenAL openAL; 
    private Source source; 

    private boolean run = true; 

    public SoundStream(int port, String IP, SoundObject soundObject) { 
     this.soundObject = soundObject; 
     this.port = port; 
     this.IP = IP; 
    } 

    public void run() { 
     try { 
      this.socket = new Socket(this.IP, this.port); 
      this.openAL = new OpenAL(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     this.mainCycleMethod(); 
    } 

    private void mainCycleMethod() { 
     while (run) { 
      this.soundObject.blockAndWait(); 
      switch (this.soundObject.getAndResetEvent()) { 
       case 0: 
        this.run = false; 
        this.close(); 
        break; 
       case 1: 
        this.setPitch(); 
        break; 
       case 2: 
        this.closeSource(); 
        this.play(); 
        break; 
       case 3: 
        this.pause(true); 
        break; 
       case 4: 
        this.pause(false); 
        break; 
      } 
     } 
    } 

    private BufferedInputStream getInputStream() throws Exception { 
     return new BufferedInputStream(socket.getInputStream()); 
    } 

    private void setPitch() { 
     if(this.source != null) { 
      try { 
       this.source.setPitch(this.soundObject.getPitch()); 
      } catch (ALException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

    private void play() { 
     try { 
      AudioInputStream audioInputStream = new AudioInputStream(this.getInputStream(), this.soundObject.getAudioFormat(), AudioSystem.NOT_SPECIFIED); 
//   AudioInputStream audioInputStream_tmp = AudioSystem.getAudioInputStream(this.getInputStream()); 
//   AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(this.soundObject.getAudioFormat(), audioInputStream_tmp); 
      this.source = openAL.createSource(audioInputStream); 
      this.source.setGain(1f); 
      this.source.play(); 
     } catch (Exception ex) { 
      ex.printStackTrace(); 
     } 
    } 

    private void close() { 
     this.closeSource(); 
     this.openAL.close(); 
     try { 
      this.socket.close(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

    private void closeSource() { 
     if(this.source!=null) { 
      this.source.close(); 
     } 
    } 

    private void pause(boolean pause) { 
     if(this.source != null) { 
      try { 
       if (pause) { 
        this.source.pause(); 
       } else { 
        this.source.play(); 
       } 
      } catch (ALException ex) { 
       ex.printStackTrace(); 
      } 
     } 
    } 
} 


public class SoundObject extends AbstractEventObject { 
    public AudioFormat getAudioFormat() { 
     boolean signed = false; 
     //true,false 
     boolean bigEndian = false; 
     //true,false 
     return new AudioFormat(this.frequency, this.bits, this.channels, signed, bigEndian); 
    } 
. 
. 
. 
. 
} 

Dieser Code wirft UnsupportedAudioFileException auf dieser Linie:

AudioInputStream audioInputStream_tmp = AudioSystem.getAudioInputStream(this.getInputStream()); 

Jedoch, wenn ich diesen Code verwenden:

AudioInputStream audioInputStream = new AudioInputStream(this.getInputStream(), this.soundObject.getAudioFormat(), 100000); 

es den Ton spielt, sondern erst nach Es lädt diese 100000 Sample-Frames in den Audioeingangsstrom. Nachdem es alle 100000 Frames abgespielt hat, wird es beendet.

Ich denke, dass ich dieses Problem lösen würde, wenn ich das AudioFormat direkt als ein Parameter während der ersten AudioInputStream-Inizialisierung übergeben könnte, aber es scheint nicht möglich zu sein. Ich empfange die Audioformatspezifikationen vom Server.

Ich denke, dass eine mögliche Lösung, die eine Datenleitung zu schaffen wäre, die ich als parametr passieren kann Konstruktor AudioInputStream. Ich bin mir jedoch nicht sicher, wie ich die Daten vom Socket direkt in Dataline bekommen kann. Ich kenne eine Lösung, die eine Endlosschleife verwendet, in der sie die Daten liest und sie in die Dataline schreibt. Aber es scheint verschwenderisch zu sein. Gibt es einen direkteren Ansatz?

Ich hoffe, es ist möglich, mit Java-openAL Bibliothek zu lösen, weil ich die Geschwindigkeit ändern muß, und ich hoffe, dass ich es selbst nicht zu tun.

Dank

+1

Als ersten Schritt können Sie 'AudioInputStream verwenden audioInputStream = new AudioInputStream (this.getInputStream(), this.soundObject.getAudioFormat(), AudioSystem.NOT_SPECIFIED);' und sehen, was passiert. – Roman

+0

Es löst mein Problem nicht, weil es von openAL.createSource (audioInputStream) -Methode blockiert wird.Es wartet wahrscheinlich darauf, dass der gesamte InputStream beendet wird. Danke –

+0

Was ist 'openAL'? Können Sie den vollständigen Quellcode anzeigen (wahrscheinlich ein [mcve])? – Roman

Antwort

2

Ich habe endlich das Problem gelöst. Wie sich herausstellte, hat Java-openAL Streaming-Unterstützung eingebaut, aber es war nicht in der Dokumentation von GitHub, also habe ich es zuerst nicht bemerkt. In der Source-Klasse gibt es eine createOutputStream-Methode, die den OutputStream zurückgibt. Sie können die Bytes direkt in den OutputStream schreiben.

Hier ist mein Code:

In diesem Snippet ich OpenAL initialisieren:

public void run() { 
    try { 
     this.socket = new Socket(this.IP, this.port); 
     this.openAL = new OpenAL(); 
    } catch (Exception ex) { 
     Log.severe(ex.toString()); 
    } 
    this.mainCycleMethod(); 
} 

Hier ist mein Spiel Methode, die aufgerufen wird, wenn die Input verfügbar:

private void play() { 
    try { 
     this.source = openAL.createSource(); 
     this.outputWriter = new OutputWriter(this.socket.getInputStream(), this.source, this.soundObject.getAudioFormat()); 
     this.source.setGain(1f); 
     this.outputWriter.start(); 
    } catch (Exception ex) { 
     Log.severe(ex.toString()); 
    } 
} 

Sie haben Um die createSource-Methode ohne Parameter zu verwenden, gibt sie eine neue Instanz von Source zurück. Rufen Sie die Wiedergabemethode für die Quelle nicht auf. Sie wird von der SourceOutputStream-Klasse behandelt, die von der createOutputStream-Methode zurückgegeben wird. Es ist nichts falsch daran, die Spielmethode manuell aufzurufen, aber ich hatte eine schlechte Erfahrung damit, wenn die Puffer leer sind. Im Grunde beginnt es später nicht zu spielen, wenn Sie mit dem Streamen der Daten zu OpenAL beginnen.

Hier ist mein OutputWriter Code, der die Bytes von Inputstream Output der Weitergabe kümmert:

package cz.speechtech.sound; 

import org.urish.openal.ALException; 
import org.urish.openal.Source; 

import javax.sound.sampled.AudioFormat; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 

/** 
* Created by honza on 16.12.15. 
*/ 
public class OutputWriter extends Thread { 
    private InputStream inputStream; 
    private OutputStream outputStream; 

    private int STREAMING_BUFFER_SIZE = 24000; 
    private int NUMBER_OF_BUFFERS = 4; 

    private boolean run = true; 

    public OutputWriter(InputStream inputStream, Source source, AudioFormat audioFormat) { 
     this.inputStream = inputStream; 
     try { 
      this.outputStream = source.createOutputStream(audioFormat, this.NUMBER_OF_BUFFERS, 1024); 
     } catch (ALException e) { 
      e.printStackTrace(); 
     } 
    } 

    public void run() { 
     byte[] buffer = new byte[this.STREAMING_BUFFER_SIZE]; 
     int i; 
     try { 
      Thread.sleep(1000); // Might cause problems 
      while (this.run) { 
       i = this.inputStream.read(buffer); 
       if (i == -1) break; 
       outputStream.write(buffer, 0, i); 
      } 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 

    public synchronized void stopRunning() { 
     this.run = false; 
     try { 
      this.outputStream.close(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

} 

einen schönen Tag.