„Безкраен“ AudioInputStream от сокет

+3 гласа
107 прегледа
попитан 2016 юни 6 от Nikola.Nikolov. (3,100 точки)
Имам проблем с правенето на AudioInputStream от 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);

    }

.

.

.

.

}

Throw-ва ми UnsupportedAudioFileException на този ред:

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

Обаче като напиша това:

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

почва да звучи,но след като зареди тези 100000 sample frame-а в audioinputstream-а. След като ги изпълни,спира...?!

Предполагам,че може да се оправи ако pass-на AudioFormat-а директно като параметър през първата инициализация на AudioInputStream-а, но не мисля,че е възможно. Получавам спецификациите на аудио формата от сървър.

Мисля,че 1 възможно решение би било да направя dataline, който да pass-на като параметър към AudioInputStream конструктора... но не знам как да взема данните от сокета директно към data      line -а. Знам,че може да стане с безкраен цикъл (infite loop),който да може чете данните и да ги записва в dataline-a. Но ми се струва много сложно, има ли друг начин?

Надявам се, че може да стане с java-openAL library-то, понеже искам да променя скоростта и се надявам,че не трябва да го правя сам.

Благодаря предварително

1 отговор

+1 глас
отговорени 2016 юни 7 от G.Ivanoff. (920 точки)

Съществува createOutputStream метод в Source класа, който връща OutputStream-а. Можеш да зададеш байтовете директно от OutputStream-а:

Ето така си инициализираш OpenAL-a:

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();

}

Тука Play метода се call-ва от inputStream-а когато е available (на разположение):

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());

    }

}

Трябва да използваш createSource метода без параметри, понеже ти връща нова инстанция на Source. Не  call-вай play метода на source, той се handle-ва от SourceOutputStream класа,чиято инстанция е върната от създаването на createOutputStream метода. Няма нищо лошо в това да си call-ваш play метода ръчно, но имах проблеми с него, когато бъферите са празни. По принцип не почва да свири после, когато почваш да stream-ваш данните към OpenAL.

Ето ти OutputWriter кода, който се грижи за подаването на байтовете от InputStream-а към OutputStream-а:

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();

        }

    }

}

...