△Javaで波形データ作成再生サンプル:メモ
メモです。
Javaで波形データをbyte配列上に作成し、音として再生するサンプル
Javaで予め作り上げた波形データに基づくPCMクリップを再生するには、javax.sound.sampledパッケージの 次のクラスを使います。- AudioFormat
- DataLine.info
- AudioSystem
- Clip
実際に440Hzのサイン波(サンプリング周波数44100hz 8bit)を2秒間鳴らすサンプルを載せます。
// Test01.java import javax.sound.sampled.*; class Test01 { public static void main(String[] args_){ try{ byte[] wave_data= new byte[44100*2]; // @A-sta@ double l1 = 44100.0/440.0; // A 440hz for(int i=0;i<wave_data.length;i++){ wave_data[i]= (byte)(110*Math.sin((i/l1)*Math.PI*2)); } //@A-end@ AudioFormat frmt= new AudioFormat(44100,8,1,true,false);// @B-sta@ DataLine.Info info= new DataLine.Info(Clip.class,frmt); Clip clip= (Clip)AudioSystem.getLine(info); clip.open(frmt,wave_data,0,wave_data.length); // @B-end@ clip.start(); Thread.sleep(100);while(clip.isRunning()) {Thread.sleep(100);} } catch(Exception e){e.printStackTrace(System.err);} } }
@A-sta@行~@A-end@行でbyte配列上に単純なサイン波のデータを作成しています。
@B-sta@行~@B-end@行が音を出す基本のジュゲム記述です。
4つのクラスはいずれも2行目のimportで使用可能となっています。
Clip.start()は非同期関数です。音の終了を46行目で待っています。
注意が必要なのが、start()を呼んだ直後ではisRunning()がtrueとならない
場合があることです。ここではまず100ミリ秒sleepした上でisRunning()を
呼んでいます。
コンパイル/実行は単純にコマンドラインから行うことができますが、 次のようなバッチファイルで動作させることが出来ます(Windowsの場合)。
:: Test01.bat javac Test01.java java Test01 pause
参考までにAppletにしたものを置きます。
後半に記述してある「うなり」「ビブラート」「音階」もも含みます。
ちなみに、「うなり」で440Hz,vib:15Hzくらいにすると電話の呼び出し音のようになります。
「音階」にはビブラートが付いています。vib=0でビブラートは止まります。「音階つなぎ」では
音階と音階の間をグリッサンドのようにつなぎます。
「音階う」はサイン波の代わりに日本語の"う"の一波を繰り返しています。
他より音程を下げています。
倍音合成とfrmnt(フォルマント)合成では音程を1オクターブ下げています。
この仕組みを発展させたサンプル sample5.mp3 ( ▲「ん」「な行」「ま行」と「が」音声合成試験 )
うなり
周波数の異なる波を重ねればうなりが得られます
byte[] pcm_data= new byte[44100*3];// double l1 = 44100.0/440.0; double l2 = 44100.0/445.0; for(int i=0;i<pcm_data.length;i++){ pcm_data[i]= (byte)(55*Math.sin((i/l1)*Math.PI*2)); pcm_data[i]+= (byte)(55*Math.sin((i/l2)*Math.PI*2)); }
ビブラート
位相の進み方を揺らすとビブラート音が得られます。byte[] pcm_data= new byte[44100*4]; double p1 = (440.0*Math.PI*2)/44100; double pv = (5.0*Math.PI*2)/44100; double phase = 0; double v_phase = 0; for(int i=0;i<pcm_data.length;i++){ phase += p1; v_phase += pv; double p= phase+2.0*Math.sin(v_phase); pcm_data[i]= (byte)(110*Math.sin(p)); }
音階
基本の周波数に2のn/12乗をかけることにより半音ずつの音階を得ることができます。全音/半音の並びを設定すればドレミファ音階を得ることができます。
byte[] pcm_data= new byte[44100*4]; double onkai[]={ -9,-7,-5,-4,-2,0,2,3 };// C,D,E,F,G,A,H,C double px[] = new double[8]; for(int i=0;i<8;++i){ px[i]= (440*Math.pow(2.0,onkai[i]/12.0)*Math.PI*2)/44100; } double phase = 0; for(int i=0;i<pcm_data.length;i++){ phase += px[(i/10000)%8]; pcm_data[i]= (byte)(110*Math.sin(phase)); }
フォルマント合成
単純なサイン波を重ねる形のフォルマント合成を行ってみます。
2倍音、3倍音と第1フォルマント、第2フォルマントを重ね"あ"っぽい
音を得ます。このプログラムの数値は適当です。
byte[] pcm_data= new byte[44100*2]; double f01 = 44100.0/220; double f02 = 44100.0/(220*2); double f03 = 44100.0/(220*3); double f04 = 44100.0/(220*4); double f05 = 44100.0/(220*5); double f06 = 44100.0/(220*6); for(int i=0;i<pcm_data.length;i++){ double b = (byte)(50*Math.sin((i/f01)*Math.PI*2)); b += (byte)(20*Math.sin((i/f02)*Math.PI*2)); b += (byte)(10*Math.sin((i/f03)*Math.PI*2)); <-第1フォルマント b += (byte)(0 *Math.sin((i/f04)*Math.PI*2)); b += (byte)(20*Math.sin((i/f05)*Math.PI*2)); <-第2フォルマント b += (byte)(20*Math.sin((i/f05)*Math.PI*2)); pcm_data[i] = (byte)b; }
音量調節
javax.sound.sampled.FloatControlをClipから取得し、音量調節を行うことが できます。次のプログラムの断片はclipの生成時にFloatControlを取得し,UIのSlider値(0~100) が変化したときに音量をセットしています。setValueに与えるのはデシベル値です。
FloatControl control; // clip生成時 control = (FloatControl)clip.getControl(FloatControl.Type.MASTER_GAIN); // JSliderイベント発生時 control.setValue((float)(Math.log10(slVol.getValue()/100.0) * 20.0));
###
2011/7/12
サンプルアプレットを別HTMLから呼ぶ形だったものを、この記事で直接呼ぶ形に変更した。
2011/7/15
行番号付きソースコードがコピペ時にIE8最新版ではなぜか行番号も
ペーストされるようになってしまったので、行番号無しに変更。
昔は大丈夫だったし、今も
コピーの為のドラッグ中は行番号部は選択された形に見えない。
| 固定リンク
コメント