MYAO's WEBSITE

2017-01-09

Javaで遊んでたらisRunningについていろいろあった話

皆さん、こんにちは。祝日ですね。気づいたら新成人として祝ってもらった成人の日も、ウン年前かぁという年月が過ぎていました。なお、前置きは長い。

さて、Twitterでも書きましたが、JavaのClipインターフェースのisRunningメソッドについてです。今日祝日じゃないですか。前回の日記で来週から頑張ると書きましたけど、まあ祝日は休みたいじゃないですか。そういう感じで、ダラダラとりあえず午前中はニコニコで動画漁るかぁと、昨日プレミアムにしちゃったしなぁとか思いながら、結月ゆかり実況動画を見てたわけです。そうすると、やっぱり(?)VOICEROIDの中身ってどうなってるんだろうなぁとか気になるわけですね。で、去年はCommonLispでWAVファイルを書いてみて遊んだりしてたわけですが、とりあえずファイルに出力するんじゃなくて音を出すものをなんとしようと思ったわけです。で、まあいろいろ検索してたんですけど、Javaの勉強兼ねて何か書いてみようと、その方向で調べることにしましたら、 Javaで音声出力する | JProgramer というサイトを見つけましたので、参考にさせていただいて、とりあえずコピペして動かしてみようと思いました。

が、GUIはまあええかなぁと思ったので、適当に関係ありそうな部分だけ取り出して、mainメソッドの中に貼っつけました。少し変わってますが、だいたい下の通り。

import javax.sound.sampled.*;  
public class SoundTest {  
    public static void main (String[] args) {  
        try{  
            double a = 440 * 2.0 * Math.PI / (44100.0);  
            byte[] data = new byte[44100];  
            for(int i = 0; i < data.length; i++){  
                data[i] = (byte)(125 * Math.sin(i * a));  
            }  
            AudioFormat frmt = new AudioFormat(44100, 8, 1,  
                                               true, false);  
            DataLine.Info info = new DataLine.Info(Clip.class, frmt);  
            Clip clip = (Clip)AudioSystem.getLine(info);  
            clip.open(frmt, data, 0, data.length);  
            clip.start();  
            // boolean b = clip.isRunning();  
            // System.out.println(b);  
            // System.out.println(clip.isRunning());  
            // System.out.println("HOGE");  
            // Thread.sleep(0);  
            if(clip.isRunning()){  
                Thread.sleep(1000);  
                System.out.println("hoge");  
            } else {  
                System.out.println("piyo");  
            }  
        }catch(Exception er){  
            System.out.println("error:"+er);  
        }  
    }  
} 

コメントアウトしてる箇所が本題の付近なのですが、元々のソースでは、while文になっていましたが、clip.startで出力を開始して、動いてたらプログラムが終わらんように1秒待てということになってるようです。ついでに上から見ておくと、サンプリングレート44100Hzで周波数440Hzのサイン波を振幅最大値が125となるようにして、その情報が入った長さ44100のbyte配列を作って、フォーマットを指定して、出力するための準備して、startということのようです。

で、本題。上に書いたソースをコンパイルして動かすと、piyoが印字されます。まあ、最初はwhile文で書いてたので、ただすぐ止まるだけ。なんとなく、サウンド周りを叩いているようなプツという音はしますが止まる。まあ、半端にコピペしたのが悪かったのかと思い、とりあえず上記のサイトのソースをまるごとコピペしてコンパイルして実行すると動く。そこでソースを読んで見るもよくわからん。なんでやと思って、isRunningが何してるかを見にAPIリファレンスをさらっと見に行きましたが、私のへっぽこ検索力では有力情報を見つけること能わず。そこで、とりあえずisRunningを呼ばずに、1秒待たせると予想通りの音がなる。とりあえず、isRunningがfalseを返してることを確認しようと上のif-elseの部分をSystem.out.println(clip.isRunning());にしてみました。結果はtrueが印字される。ここで、かなり悩みました。

が、printlnを呼ぶのに時間がかかるのでは? と思い至り、そこで、なんとなくタイムラグか何かがあるんだろうなぁと思った私は、まず、1ミリ秒またせました。コメントアウトのThread.sleep(0)のところをThread.sleep(1)にしたわけですね。結果は、うまいこと動く。

何か処理噛ませたら、時間がかかるだろうということで、HOGEを印字させてみると、予想通り動く。

Thread.sleep(0)だと待たないけどオーバーヘッドがあるだろうなという予想で、こっちで試してみるも動く。

上の形にすると、piyoが印字される。なお、変数bにclip.isRunning()の返り値に代入して、その値を印字してみるとfalseになります。

結局、いまのところ直後にisRunningを呼ぶとfalseとなるようで、startしてから、ラインが実行中になるまでに時間がかかるようです。なんか、メソッド呼び出しは結構時間がかかり、評価させるだけとか、代入はそんなに時間がかからないということも分かりましたね。まあ、こんなところで。なんか間違ってるとか勘違いしてるって場合は適当に連絡ください。