Coverage report

| %line | %branch
--- | --- | ---
tsukuba_bunko.peko.canvas.stage.audio.SampledAudioClip |
0% |
0% |

1 |  |
     /*
2 |  |
      * All Rights Reserved.
3 |  |
      * Copyright (C) 1999-2005 Tsukuba Bunko.
4 |  |
      *
5 |  |
      * Licensed under the BSD License ("the License"); you may not use
6 |  |
      * this file except in compliance with the License.
7 |  |
      * You may obtain a copy of the License at
8 |  |
      *
9 |  |
      *       http://www.tsukuba-bunko.org/licenses/LICENSE.txt
10 |  |
      *
11 |  |
      * Unless required by applicable law or agreed to in writing, software
12 |  |
      * distributed under the License is distributed on an "AS IS" BASIS,
13 |  |
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 |  |
      * See the License for the specific language governing permissions and
15 |  |
      * limitations under the License.
16 |  |
      *
17 |  |
      * $Id: SampledAudioClip.java,v 1.3 2005/08/19 03:18:11 ppoi Exp $
18 |  |
      */
19 |  |
     package tsukuba_bunko.peko.canvas.stage.audio;
20 |  |
21 |  |
     import java.io.IOException;
22 |  |
23 |  |
     import java.net.URL;
24 |  |
25 |  |
     import javax.sound.sampled.AudioFormat;
26 |  |
     import javax.sound.sampled.AudioInputStream;
27 |  |
     import javax.sound.sampled.AudioSystem;
28 |  |
     import javax.sound.sampled.DataLine;
29 |  |
     import javax.sound.sampled.FloatControl;
30 |  |
     import javax.sound.sampled.LineUnavailableException;
31 |  |
     import javax.sound.sampled.SourceDataLine;
32 |  |
     import javax.sound.sampled.UnsupportedAudioFileException;
33 |  |
34 |  |
     import tsukuba_bunko.peko.Logger;
35 |  |
     import tsukuba_bunko.peko.canvas.stage.AudioClip;
36 |  |
     import tsukuba_bunko.peko.canvas.stage.AudioPlayer;
37 |  |
38 |  |
39 |  |
     /**
40 |  |
      * サウンドファイルを再生する機能を提供します。
41 |  |
      * @author	$Author: ppoi $
42 |  |
      * @version	$Revision: 1.3 $ $Date: 2005/08/19 03:18:11 $
43 |  |
      */
44 | 0 |
     public class SampledAudioClip	extends AudioClip	{
45 |  |
46 |  |
     	/**
47 |  |
     	 * 出力フォーマット
48 |  |
     	 */
49 | 0 |
     	private AudioFormat	_outputFormat = null;
50 |  |
51 |  |
     	/**
52 |  |
     	 * デコード済み再生データ取得用オーディオ入力ストリーム
53 |  |
     	 */
54 | 0 |
     	private AudioInputStream	_inputStream = null;
55 |  |
56 |  |
     	/**
57 |  |
     	 * デコード前再生データ取得用オーディオ入力ストリーム
58 |  |
     	 */
59 | 0 |
     	private AudioInputStream	_baseInputStream = null;
60 |  |
61 |  |
     	/**
62 |  |
     	 * 再生出力ライン
63 |  |
     	 */
64 | 0 |
     	private SourceDataLine	_line = null;
65 |  |
66 |  |
     	/**
67 |  |
     	 * マスターゲインコントロール
68 |  |
     	 */
69 | 0 |
     	private FloatControl	_masterGainControl = null;
70 |  |
71 |  |
     	/**
72 |  |
     	 * 再生用ワークスレッド
73 |  |
     	 */
74 | 0 |
     	private Thread	_workThread = null;
75 |  |
76 |  |
     	/**
77 |  |
     	 * 再生フラグ
78 |  |
     	 */
79 | 0 |
     	private boolean	_running = false;
80 |  |
81 |  |
82 |  |
     	/**
83 |  |
     	 * <code>Player</code> のインスタンスを生成します。
84 |  |
     	 * このコンストラクタで作成された Player インスタンスはループ再生しません。
85 |  |
     	 * @param	id	クリップ ID
86 |  |
     	 * @param	clipURL	サウンドファイルの URL
87 |  |
     	 */
88 |  |
     	public SampledAudioClip( String id, URL clipURL )
89 |  |
     	{
90 | 0 |
     		super( id, clipURL );
91 | 0 |
     	}
92 |  |
93 |  |
94 |  |
     	/**
95 |  |
     	 * サウンドファイルを再生します。
96 |  |
     	 * <p>サウンドファイルの再生は非同期に行われ，本メソッドの呼び出しはすぐに返ります。</p>
97 |  |
     	 * @throws	UnsupportedAudioFileException	サウンドファイルが未サポートのオーディオ形式の場合
98 |  |
     	 * @throws	IOException	再生中に I/O エラーが発生した場合
99 |  |
     	 */
100 |  |
     	public void play()
101 |  |
     	{
102 | 0 |
     		if( _inputStream != null )	{
103 | 0 |
     			return;
104 |  |
     		}
105 |  |
106 |  |
     		try {
107 | 0 |
     			setSoundFile( getClipURL() );
108 |  |
     		}
109 | 0 |
     		catch( UnsupportedAudioFileException uafe ) {
110 |  |
     			//TODO: ログメッセージ
111 | 0 |
     			Logger.warn( "unsupported sound format.", uafe );
112 |  |
     		}
113 | 0 |
     		catch( LineUnavailableException lue )	{
114 |  |
     			//TODO: ログメッセージ
115 | 0 |
     			Logger.warn( "source dataLine is not available.", lue );
116 |  |
     		}
117 | 0 |
     		catch( IOException ioe ) {
118 |  |
     			//TODO: ログメッセージ
119 | 0 |
     			Logger.warn( "fail to load sound file.", ioe );
120 | 0 |
     		}
121 |  |
122 | 0 |
     		_workThread = new Thread()	{
123 |  |
     			public void run()
124 |  |
     			{
125 |  |
     				try	{
126 |  |
     					AudioInputStream	is = _inputStream;
127 |  |
     					SourceDataLine	line = _line;
128 |  |
     					byte[]	buffer = new byte[ 8192 ];
129 |  |
     					line.start();
130 |  |
     					int	readSize = 0;
131 |  |
     					while( _running )	{
132 |  |
     						readSize = is.read( buffer, 0, buffer.length );
133 |  |
     						if( _running )	{
134 |  |
     							if( readSize != -1 )	{
135 |  |
     								line.write( buffer, 0, readSize );
136 |  |
     							}
137 |  |
     							else if( isLoop() )	{
138 |  |
     								closeInput();
139 |  |
     								prepareInput();
140 |  |
     								is = _inputStream;
141 |  |
     							}
142 |  |
     							else	{
143 |  |
     								line.drain();
144 |  |
     								if( _running )	{
145 |  |
     									synchronized( SampledAudioClip.this )	{
146 |  |
     										if( _running )	{
147 |  |
     											playingFinished();
148 |  |
     										}
149 |  |
     									}
150 |  |
     								}
151 |  |
     								break;
152 |  |
     							}
153 |  |
     						}
154 |  |
     					}
155 |  |
     				}
156 |  |
     				catch( UnsupportedAudioFileException uafe ) {
157 |  |
     					//TODO: ログメッセージ
158 |  |
     					Logger.warn( "unsupported sound format.", uafe );
159 |  |
     				}
160 |  |
     				catch( IOException ioe ) {
161 |  |
     					//TODO: ログメッセージ
162 |  |
     					Logger.warn( "fail to load sound file.", ioe );
163 |  |
     				}
164 |  |
     				finally	{
165 |  |
     					closeInput();
166 |  |
     				}
167 |  |
168 |  |
     				synchronized( SampledAudioClip.this )	{
169 |  |
     					_workThread = null;
170 |  |
     					SampledAudioClip.this.notify();
171 |  |
     				}
172 |  |
     			}
173 |  |
     		};
174 | 0 |
     		_running = true;
175 | 0 |
     		_workThread.start();
176 | 0 |
     	}
177 |  |
178 |  |
     	/**
179 |  |
     	 * サウンドファイルの再生を停止します。
180 |  |
     	 * <p>サウンドファイルが再生されていない場合，何も実行されません。</p>
181 |  |
     	 * @param	mode	停止モード
182 |  |
     	 */
183 |  |
     	public void stop( int mode )
184 |  |
     	{
185 | 0 |
     		if( _workThread != null )	{
186 | 0 |
     			synchronized( this )	{
187 | 0 |
     				if( _workThread != null )	{
188 | 0 |
     					playingFinished();
189 | 0 |
     					if( mode == AudioPlayer.STOP_WITH_ASYNC_FADEOUT )	{
190 | 0 |
     						new Thread()	{
191 |  |
     							public void run() {
192 |  |
     								fadeout();
193 |  |
     								_running = false;
194 |  |
     							}
195 |  |
     						}.start();
196 | 0 |
     					}
197 |  |
     					else	{
198 | 0 |
     						if( mode == AudioPlayer.STOP_WITH_SYNC_FADEOUT )	{
199 | 0 |
     							fadeout();
200 |  |
     						}
201 |  |
202 |  |
     						try {
203 | 0 |
     							_running = false;
204 | 0 |
     							SampledAudioClip.this.wait();
205 |  |
     						}
206 | 0 |
     						catch( InterruptedException e ) {
207 |  |
     							//ignore
208 | 0 |
     						}
209 |  |
     					}
210 |  |
     				}
211 | 0 |
     				_workThread = null;
212 | 0 |
     			}
213 |  |
     		}
214 | 0 |
     	}
215 |  |
216 |  |
     	/**
217 |  |
     	 * このプレイヤーを破棄します。
218 |  |
     	 */
219 |  |
     	public void dispose()
220 |  |
     	{
221 | 0 |
     		stop( AudioPlayer.STOP_IMMEDIATELY );
222 | 0 |
     		if( _line != null )	{
223 | 0 |
     			_line.stop();
224 | 0 |
     			_line.close();
225 |  |
     		}
226 | 0 |
     		_line = null;
227 | 0 |
     		_masterGainControl = null;
228 | 0 |
     	}
229 |  |
230 |  |
     	/**
231 |  |
     	 * 再生音量を変更します。
232 |  |
     	 * <p>マスターゲインコントロールが有効でない場合，何も行われません。</p>
233 |  |
     	 */
234 |  |
     	public void setVolume( float level )
235 |  |
     	{
236 | 0 |
     		if( _masterGainControl != null )	{
237 |  |
     			float	decibel;
238 | 0 |
     			if( level < 0.02f )	{
239 | 0 |
     				decibel = _masterGainControl.getMinimum();
240 | 0 |
     			}
241 |  |
     			else	{
242 | 0 |
     				decibel = (float)Math.log( (double)level ) * 20.0f;
243 |  |
     			}
244 | 0 |
     			_masterGainControl.setValue( decibel );
245 |  |
     		}
246 | 0 |
     	}
247 |  |
248 |  |
     	/**
249 |  |
     	 * 再生音量を取得します。
250 |  |
     	 * <p>マスターゲインコントロールが有効でない場合，-1f を返します。
251 |  |
     	 */
252 |  |
     	public float getVolume()
253 |  |
     	{
254 | 0 |
     		if( _masterGainControl == null )	{
255 | 0 |
     			return -1f;
256 |  |
     		}
257 |  |
     		else	{
258 | 0 |
     			float	decibel = _masterGainControl.getValue();
259 | 0 |
     			return (float)Math.pow( 10, (decibel / 20.0f) );
260 |  |
     		}
261 |  |
     	}
262 |  |
263 |  |
     	/**
264 |  |
     	 * 再生するサウンドファイルを設定し，オーディオシステムの初期化を行います。
265 |  |
     	 * @param	clipURL	サウンドファイルの URL
266 |  |
     	 * @throws	UnsupportedAudioFileException	サウンドファイルが未サポートのオーディオ形式の場合
267 |  |
     	 * @throws	LineUnavailableException	再生出力ラインが使用不能の場合
268 |  |
     	 * @throws	IOException	ファイルの解析中に I/O エラーが発生した場合
269 |  |
     	 */
270 |  |
     	private void setSoundFile( URL clipURL )
271 |  |
     		throws UnsupportedAudioFileException, LineUnavailableException, IOException
272 |  |
     	{
273 | 0 |
     		prepareInput();
274 |  |
275 | 0 |
     		DataLine.Info	info = new DataLine.Info( SourceDataLine.class, _outputFormat );
276 | 0 |
     		SourceDataLine	line = (SourceDataLine)AudioSystem.getLine( info );
277 | 0 |
     		line.open( _outputFormat );
278 | 0 |
     		_line = line;
279 |  |
280 |  |
     		try	{
281 | 0 |
     			_masterGainControl = (FloatControl)line.getControl( FloatControl.Type.MASTER_GAIN );
282 |  |
     		}
283 | 0 |
     		catch( IllegalArgumentException iae )	{
284 |  |
     			//TODO: ログメッセージ
285 | 0 |
     			Logger.warn( "Master Gain Control is not supported.", iae );
286 | 0 |
     		}
287 | 0 |
     	}
288 |  |
289 |  |
     	/**
290 |  |
     	 * 再生するサウンドファイルを初期化します。
291 |  |
     	 * @throws	UnsupportedAudioFileException	サウンドファイルが未サポートのオーディオ形式の場合
292 |  |
     	 * @throws	IOException	解析中に I/O エラーが発生した場合
293 |  |
     	 */
294 |  |
     	private void prepareInput()
295 |  |
     		throws UnsupportedAudioFileException, IOException
296 |  |
     	{
297 | 0 |
     		AudioInputStream	baseInputStream = AudioSystem.getAudioInputStream( getClipURL() );
298 | 0 |
     		AudioFormat	baseFormat = baseInputStream.getFormat();
299 | 0 |
     		AudioFormat.Encoding	baseEncoding = baseFormat.getEncoding();
300 | 0 |
     		Logger.debug( "file=" + getClipURL() + " fomart=" + baseFormat + " encoding=" + baseEncoding + "[" + baseEncoding.getClass() + "]" );
301 |  |
302 | 0 |
     		if( AudioFormat.Encoding.class.equals(baseEncoding.getClass()) )	{
303 | 0 |
     			_outputFormat = baseFormat;
304 | 0 |
     			_inputStream = baseInputStream;
305 | 0 |
     			_baseInputStream = null;
306 | 0 |
     		}
307 |  |
     		else	{
308 | 0 |
     			_outputFormat = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, baseFormat.getSampleRate(), 16, baseFormat.getChannels(),
309 |  |
     					baseFormat.getChannels() * 2, baseFormat.getSampleRate(), false );
310 | 0 |
     			Logger.debug( "decoded: " + _outputFormat );
311 | 0 |
     			_inputStream = AudioSystem.getAudioInputStream( _outputFormat, baseInputStream );
312 | 0 |
     			_baseInputStream = baseInputStream;
313 |  |
     		}
314 | 0 |
     	}
315 |  |
316 |  |
     	/**
317 |  |
     	 * サウンドファイルをクローズします。
318 |  |
     	 */
319 |  |
     	private void closeInput()
320 |  |
     	{
321 | 0 |
     		if( _inputStream != null )	{
322 |  |
     			try	{
323 | 0 |
     				_inputStream.close();
324 |  |
     			}
325 | 0 |
     			catch( IOException ioe )	{
326 |  |
     				//ignore
327 | 0 |
     			}
328 |  |
     		}
329 | 0 |
     		if( _baseInputStream != null )	{
330 |  |
     			try	{
331 | 0 |
     				_baseInputStream.close();
332 |  |
     			}
333 | 0 |
     			catch( IOException ioe )	{
334 |  |
     				//ignore
335 | 0 |
     			}
336 |  |
     		}
337 | 0 |
     		_inputStream = null;
338 | 0 |
     		_baseInputStream = null;
339 | 0 |
     		_outputFormat = null;
340 | 0 |
     	}
341 |  |
342 |  |
     	/**
343 |  |
     	 * フェードアウトします。
344 |  |
     	 */
345 |  |
     	private void fadeout()
346 |  |
     	{
347 | 0 |
     		synchronized( SampledAudioClip.this )	{
348 | 0 |
     			if( _masterGainControl != null )	{
349 | 0 |
     				float	level = getVolume();
350 | 0 |
     				for( ;  level > 0.03f; level = level * 0.97f )	{//桁をそろえるにはどうすればよい？
351 |  |
     					try	{
352 | 0 |
     						SampledAudioClip.this.wait( 2L );
353 |  |
     					}
354 | 0 |
     					catch( InterruptedException ie 	)	{
355 | 0 |
     					}
356 | 0 |
     					setVolume( level );
357 |  |
     				}
358 | 0 |
     				_masterGainControl.setValue( _masterGainControl.getMinimum() );
359 |  |
     			}
360 | 0 |
     		}
361 | 0 |
     	}
362 |  |
     }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.