2

サウンド データを処理するためのアプリケーション システムに取り組んでいます。最初のアプリケーションは、単にマイク ジャックからデータを読み取り、次のアプリケーションにデータを送信します。メイン ループは、次のコードを繰り返し実行します。

0 : Globals.mySleep(waitTime); // tells the thread to sleep for the proper amount of time for a given data format  
1 : inputLine.read(buffer, 0, bufferSize); // reads sound data from the microphone jack into buffer
2 : if(connections.get(REGISTER) != null) { // if the next application is connected
3 :     DataSlice slice = new DataSlice(buffer, serialIDCounter++, getDeviceName()); // create a slice of data to send, containing the sound data
4 :     try{
5 :         connections.get(REGISTER).sendDataSlice(slice); // send the data to the next application. supposed to block until next application receives the data
6 :         connections.get(REGISTER).flush(); // make sure data gets sent
7 :     } catch (IOException e) {
8 :         // Stream has been broken. Shut Down
9 :         close();
10:     }
11: }

システムを起動すると、常に数秒遅れます。システムを一時停止すると (GUI アプリケーションは入力アプリケーションに続くアプリケーションに入力アプリケーションからのデータの受信を停止するように指示するため、入力アプリケーションは一時停止時に 5 行目でブロックする必要があります)、待機してから再度再生すると、システムはさらに遅延します。一時停止したところです。たとえば、10 秒のラグで開始し、5 秒一時停止してから再度再生すると、15 秒遅れることになります。

これは、プログラムを実行可能な jar ファイルとして実行したときに発生します。Eclipseから実行すると発生しません。

Ubuntu Linux 10.04 LTS を実行している 2 台のコンピューターでこれをテストしました。一方では発生しますが、他方では発生しません。一方で、Eclipse から実行しようとすると、まったく別の問題が発生します。これをどうすればいいのかわからない。コンピューターのスペックをご希望でしたら、喜んで差し上げます。欲しいスペックと入手方法を教えてください。

遅延の原因を教えてください。ありがとう。

- 編集 -

アンドリューの提案に従って、私はSSCCEであると信じているものを作成しました:

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.sound.sampled.*;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Main implements MouseListener{
    // Class that reads a signal from Line-in source and sends that signal
    // to either a recorder module or the signal-viewing pipeline
    public class PlayThread extends Thread {

        byte[] buffer = new byte[bufferSize];
        boolean playing = false;
        boolean connected = false;

        PlayThread() {}

        public void run() {
            while(true) {
                try {
                    sleep(waitTime);
                    inputLine.read(buffer, 0, bufferSize);
                    if(connected) {
                        while(!playing)
                            sleep(100);
                        int max = 0;
                        for(int i = 0; i < buffer.length; i++) {
                            if(Math.abs(buffer[i]) > max)
                                max = Math.abs(buffer[i]);
                        }
                        System.out.println("Max: " + max);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        public void setPlaying(boolean playing) {
            this.playing = playing;
        }

        public void setConnected(boolean connected) {
            this.connected = connected;
        }
    }

    TargetDataLine inputLine;
    AudioFormat format;
    float sampleRate;
    int sampleSizeBits;
    int channels;
    int waitTime;
    int bufferSize;
    int slicesPerSecond;
    int windowSize = 512;
    PlayThread pThread;

    JFrame gui = new JFrame("Sound Lag");
    JPanel panel = new JPanel();
    JButton play = new JButton("Play"), pause = new JButton("Pause"),
            connect = new JButton("Connect"), disconnect = new JButton("Disconnect");

    Main() {
        sampleRate = 44100;
        sampleSizeBits = 16;
        channels = 2;
        bufferSize = (sampleSizeBits/8)*channels*windowSize;
        slicesPerSecond = (int) ((sampleRate/(float)channels)/(float)windowSize);
        waitTime = (int)((((1000f/sampleRate)/(float)sampleSizeBits)/2f)*8f*(float)bufferSize);

        play.addMouseListener(this);
        pause.addMouseListener(this);
        connect.addMouseListener(this);
        disconnect.addMouseListener(this);

        panel.add(play);
        panel.add(pause);
        panel.add(connect);
        panel.add(disconnect);
        gui.add(panel);
        gui.setVisible(true);
        gui.pack();
        gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void read() {
        // Open line from line-in
        format = new AudioFormat(sampleRate, sampleSizeBits, channels, true, true);

        // Obtain and open the lines.
        inputLine = getTargetDataLine();

        pThread = new PlayThread();
        pThread.start();
    }

    private TargetDataLine getTargetDataLine() {
        try {
            DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
            for (Mixer.Info mi : AudioSystem.getMixerInfo()) {
                TargetDataLine dataline = null;
                try {
                    Mixer mixer = AudioSystem.getMixer(mi);
                    dataline = (TargetDataLine)mixer.getLine(info);
                    dataline.open(format);
                    dataline.start();
                    return dataline;
                }
                catch (Exception e) {}
                if (dataline != null)
                    try {
                        dataline.close();
                    }
                    catch (Exception e) {}
            }
        }
        catch (Exception e) {}
        return null;
    }

    public static void main(String[] args) {
        Main main = new Main();
        main.read();
    }

    @Override
    public void mouseClicked(MouseEvent arg0) {
        if(arg0.getSource() == play) {
            System.out.println("Playing");
            pThread.setPlaying(true);
        }
        else if(arg0.getSource() == pause) {
            System.out.println("Paused");
            pThread.setPlaying(false);
        }
        else if(arg0.getSource() == connect) {
            System.out.println("Connected");
            pThread.setConnected(true);
        }
        else if(arg0.getSource() == disconnect) {
            System.out.println("Disconnected");
            pThread.setConnected(false);
        }
    }

    @Override public void mouseEntered(MouseEvent arg0) {}
    @Override public void mouseExited(MouseEvent arg0) {}
    @Override public void mousePressed(MouseEvent arg0) {}
    @Override public void mouseReleased(MouseEvent arg0) {}
}

このコードは、再生、一時停止、接続、切断の 4 つのボタンを含むウィンドウを生成します。再生を押すと、プログラムが「再生」モードにあるかのようになります。接続をクリックすると、あたかもサウンド入力アプリケーションが次のモジュールに接続されたかのようになります。
テストするには、次の手順を実行します。
サウンド デバイスをマイク ジャックに接続します (ただし、何も再生しないでください)。
このコードから実行可能な jar ファイルを作成します。
ターミナルからファイルを実行します。
「再生」をクリックします。
「接続」をクリックします。

この時点で、端末を下って行く小さな数字の束が表示されるはずです。

サウンド デバイスで、サウンドの再生を開始します。

ターミナルですぐに大きな数字が表示されるはずです。

サウンド デバイスでのサウンドの再生を停止します (ターミナルで小さい数値に戻る必要があります)。
「一時停止」をクリックします。
5 秒待ちます。
「再生」をクリックします。

オーディオ デバイスでサウンドの再生を開始します。

ここでバグが発生します。このコードを Eclipse で実行すると、すぐに再び大きな数値が得られます。jar ファイルを実行しているだけの場合、5 秒の遅延があり、その後、より大きな数値が得られます。

何か新しい考えはありますか?

4

3 に答える 3

2

修正されました。サウンド ストリームを再生したいときはいつでも (再生ボタンを押すたびに)、現在のストリームを閉じて、新しいストリームを開きます。

私は、TargetDataLine が、read メソッドが呼び出されるたびに選択されるサウンド データのバッファーを実際に保持していることに気づきませんでした。

Eclipse からアプリケーションを実行したとき、実行可能な jar ファイルとして実行したときとは異なるタイプの TargetDataLine を使用していたようです。これは、バッファ間のサイズの違いによって証明されました。サイズの違いは約 2 倍に過ぎませんでしたが、問題はバッファーのサイズではなく、フェッチされた TargetDataLines に関係していると思います。

奇妙なことに、Globals.mySleep(waitTime) を削除すると SSCCE は修正されましたが、それが表すはずの実際のプログラムは修正されませんでした。

ラインを交換する代わりに、ラインの排水とフラッシュの両方を試みましたが、間違った使い方をしていた可能性がありますが、どちらも機能していないようです。

問題は、DataLine のバッファがいっぱいになり、プログラムが再生されていない間、バッファが空にならなかったため、再生を開始したときに、通常の再生速度でバッファからデータをフェッチし続けたため、後れを取ります。

解決策は次のとおりです。プログラムの再生が開始されたら、DataLine を置き換えます。

- 編集 -

さらに観察すると、Eclipse から実行したときは、jar ファイルとして実行したときとは異なる JRE を使用しているように見えました。デフォルトの Java プログラムを java-6-openjdk ではなく java-6-sun に設定すると、jar ファイルから正常に動作します。

また、別のコンピューターで DataLine を置き換える方法を実行してみました。このコンピューターでは、信号が途切れてしまいました。新しい DataLine をプルするのに時間がかかるようだったので、それはうまくいかないと判断しました。今は、常に DataLine から読み取るだけです。システムが一時停止している場合、どこにも信号を送信しません。

于 2011-04-01T16:51:48.493 に答える
1

コードが遅いが、プロファイラーを使用する理由がわからない、このような状況で行うのが最善の方法を見つけました。http: //www.quest.com/jprobe/software_download.aspx無料の証跡を取得できます。このJavaプロファイラーを使用すると、費やされた時間と実行回数が1行ずつ表示されます。これにより、コードの速度が低下している原因を正確に特定できるはずです。

これがお役に立てば幸いです、Eamonn

于 2011-03-31T14:40:49.040 に答える
1

Globals.mySleep(waitTime); // 指定されたデータ形式に適した時間だけスリープするようにスレッドに指示します

ここでの「適切な」waitTimeは「0」だと思います。

疑い以上の何かが必要な場合は、SSCCE (行番号なし) を投稿することをお勧めします。

于 2011-03-31T15:05:42.020 に答える