2

すべてのオーディオ データが書き込まれる (再生される) まで、SourceDataLine.write(...) の呼び出しをループするスレッドがあります。途中で (ループが通常 EOF タイプの条件で終了する前に) 再生を停止できるようにしたいのですが、割り込みを使用して (試みています)。PlaybackThread.interrupt() を呼び出すと、スレッドの割り込みと終了が散発的に発生します。コードのマイナーな変更、または連続した呼び出しにより、異なる (一見ランダムな) 結果が得られます。Thread.interrupted() の代わりに Thread.currentThread.isInterrupted() をデバッグして使用してみました。SourceDataLine.write(...) 呼び出しを別のもの (例: 長いカウント ループ) に置き換えると、スレッドは予測どおりに一貫して終了することに注意してください。

SourceDataLine.write(...) は割り込みを「消費」しますが、InterruptedException はスローしませんか? つまり、割り込みフラグを (おそらく Thread.interrupted() で) クリアし、無視してそのまま続行しますか? これにより、割り込みが断続的に機能する理由が説明されます。SDL.write(...) メソッドで割り込みが発生した場合、割り込みは破棄されます。それ以外の場合は、 Thread.interrupted() 呼び出しでテストするまで「そのまま」のままです。これはひどいことですが、一日中費やした後に私が思いつくことができる唯一の説明です. SourceDataLine.write(...) はインターフェイスであり、実装はマシンに依存しているため、ソースが見つかりません。

私は try/catch を使用しておらず、ループで InterruptedExeption をキャッチすることを期待していますが、各パスのループの最後で Thread.interrupted() (または Thread.currentThread.isInterrupted()) を呼び出しているため、簡単に作成できます。私自身のinterruptFlagとそれをテストします。これはまさに、スケジュールを満たすために急いで数か月前に TargetDataLine.read(...) メソッドを中断したときに別の場所で行ったことです。私は週末のほとんどを、この長年の謎の根底にあるものを徹底的に調査することに費やしましたが、成功していません.

実際のJava割り込みメカニズムを使用する方が一貫性がありエレガントに見えますが、機能しない場合はそうではありません。この説明はもっともらしいですか?

再生スレッドのコード:

public void run() {
    int offset = 0;
    int remaining = cRawAudioBuffer.length;
    int length = WRITE_LENGTH;
    int bytesWritten = 0;
    cOutputLine.start();
    while (remaining>0) {
        length = Math.min(length,remaining);
        bytesWritten = cOutputLine.write(cRawAudioBuffer,offset,length);
        offset += bytesWritten;
        remaining -= bytesWritten;
        if (Thread.currentThread().isInterrupted()) {
            System.out.println("I Was interrupted");
            break;
        }
    }
    cOutputLine.close();
}

アップデート:

ここで何が起こっているのかをより完全に理解するために、まだ調査中です。書き込みループ自体から Thread.interrupt() を呼び出して、playbackThread の中断を強制し、Thread.currentThread().isInterrupted() への呼び出しを挿入したため、テスト自体で割り込みフラグをクリアしませんでした。 write() 呼び出しの後。私が見つけたのは、最初の数回のループでは、write() メソッドから戻った後も割り込みが保持されていることです。最初の 3 つのループの後、write() から戻った後に割り込みがクリアされます。それ以降はほぼ必ずですが、そうではありません常に、write() から戻った後にクリアされます。私の推測では、バッファがクリアされるのを待って write() がブロックされている場合、割り込みフラグを検出、無視、およびクリアします。まだ推測にすぎませんが、私は確信したいと思います(私が間違っていることを広めて後で噛まれるのを避けるため)。ソースにアクセスせずにこの仮説を検証する方法についてのアイデアはありますか?

public void run() {
    ....
    while (remaining>0) {
        length = Math.min(length,remaining);
        Thread.currentThread().interrupt();
        System.out.println("Player interrupt flag is " + (Thread.currentThread().isInterrupted()?"":"not ") + "set before write()");
        bytesWritten = cOutputLine.write(cRawAudioBuffer,offset,length);
        System.out.println("Player interrupt flag is " + (Thread.currentThread().isInterrupted()?"":"not ") + "set after write()");
        offset += bytesWritten;
        remaining -= bytesWritten;
        if (Thread.interrupted()) {
            System.out.println("I Was interrupted");
            //break;
        }
    }
    cOutputLine.close();
}
4

4 に答える 4

5

結局のところ、SourceDataLine.write() は書き込み中にブロックされ、その書き込み中に割り込みが発生した場合、割り込みは無視され、割り込みフラグがリセットされます。割り込みを直接検出する方法はありません。これは、Java Audio を使用している人々にとっては常識であると思います。これを理解しないと、確実に中断可能なオーディオ再生を作成する方法はありません。(悲しいことに) 割り込みを検出するために、各 write() の後に読み取られる同期フラグに頼る必要があります。

于 2012-11-16T20:36:49.237 に答える
1

あなたが抱えている問題は、ほとんどの時間がcOutputLine.write割り込みのために止まらないことに費やされているということです。これは、ループが割り込みをチェックする前に、いくつかのデータを書き込む必要があることを意味します。これが(バッファがいっぱいであるために)ブロックされた場合、割り込みをチェックすることはありません。

これを回避する1つの方法は、書き込みでIOExceptionをトリガーするストリームを閉じることです。または、スレッドが中断されたときに停止するブロッキングNIO(非ブロッキングである必要はありません)を使用できる場合があります。

于 2012-04-23T08:10:09.157 に答える