1 |
/* |
2 |
* WavePlay.cs |
3 |
* Copyright (c) 2009 kbinani |
4 |
* |
5 |
* This file is part of Boare.Lib.Media. |
6 |
* |
7 |
* Boare.Lib.Media is free software; you can redistribute it and/or |
8 |
* modify it under the terms of the BSD License. |
9 |
* |
10 |
* Boare.Lib.Media is distributed in the hope that it will be useful, |
11 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
13 |
*/ |
14 |
using System; |
15 |
using System.Runtime.InteropServices; |
16 |
|
17 |
using bocoree; |
18 |
|
19 |
namespace Boare.Lib.Media { |
20 |
|
21 |
public delegate void FirstBufferWrittenCallback(); |
22 |
|
23 |
public unsafe class WavePlay { |
24 |
const int _NUM_BUF = 3; // バッファの数 |
25 |
int s_block_size; // 1個のバッファのサイズ(サンプル) |
26 |
int s_sample_rate; // サンプリングレート |
27 |
WAVEFORMATEX s_wave_formatx; // WAVEファイルヘッダ |
28 |
//IntPtr s_ptr_wave_formatx; |
29 |
IntPtr s_hwave_out; // WAVE再生デバイス |
30 |
|
31 |
WAVEHDR[] s_wave_header = new WAVEHDR[_NUM_BUF];// WAVEヘッダ |
32 |
IntPtr[] s_ptr_wave_header = new IntPtr[_NUM_BUF]; |
33 |
|
34 |
uint*[] s_wave = new uint*[_NUM_BUF]; // バッファ |
35 |
IntPtr[] s_ptr_wave = new IntPtr[_NUM_BUF]; |
36 |
|
37 |
bool[] s_done = new bool[_NUM_BUF]; |
38 |
int s_current_buffer; // 次回書き込むべきバッファのインデクス |
39 |
uint s_processed_count; // 初回はバッファを_NUM_BUF個全部埋めなければいけないので、最初の _NUM_BUF + 1 回はカウントを行う。そのためのカウンタ |
40 |
bool s_abort_required; // 再生の中断が要求された時立つフラグ |
41 |
int s_buffer_loc; // 書き込み中のバッファ内の、現在位置 |
42 |
bool s_playing; // 再生中かどうかを表すフラグ |
43 |
int s_error_samples; // appendされた波形データの内、先頭のs_error_samples分を省く。通常の使い方なら常に0だが、vocaloid2 vstiで使う場合、プリセンド分を除いてwaveOutWriteしなければいけないので非0になる。 |
44 |
int s_last_buffer; // 最後に再生されるバッファの番号。負値の場合、append_lastが未だ呼ばれていないことを意味する。 |
45 |
FirstBufferWrittenCallback s_first_buffer_written_callback; // 最初のバッファが書き込まれたとき呼び出されるコールバック関数 |
46 |
WaveReader[] s_wave_reader; |
47 |
int s_num_wave_reader; // s_wave_readerの個数 |
48 |
float*[] s_another_wave_l; |
49 |
IntPtr[] s_ptr_another_wave_l; |
50 |
float*[] s_another_wave_r; |
51 |
IntPtr[] s_ptr_another_wave_r; |
52 |
long s_wave_read_offset_samples; |
53 |
float* s_wave_buffer_l; |
54 |
IntPtr s_ptr_wave_buffer_l; |
55 |
float* s_wave_buffer_r; |
56 |
IntPtr s_ptr_wave_buffer_r; |
57 |
|
58 |
delegateWaveOutProc s_wave_callback; |
59 |
|
60 |
/// コールバック関数 |
61 |
void wave_callback( IntPtr hwo, uint uMsg, uint dwInstance, uint dwParam1, uint dwParam2 ) { |
62 |
#if DEBUG |
63 |
Console.WriteLine( "WavePlay.wave_callback; uMsg=" + uMsg ); |
64 |
#endif |
65 |
if ( uMsg == windows.MM_WOM_DONE ) { |
66 |
int index_done = 0; |
67 |
WAVEHDR whdr = (WAVEHDR)Marshal.PtrToStructure( new IntPtr( dwParam1 ), typeof( WAVEHDR ) ); |
68 |
int dwuser = whdr.dwUser.ToInt32(); |
69 |
if ( dwuser >= _NUM_BUF ) { |
70 |
index_done = dwuser - _NUM_BUF; |
71 |
} else { |
72 |
index_done = dwuser; |
73 |
} |
74 |
#if DEBUG |
75 |
bocoree.debug.push_log( "dwuser=" + dwuser ); |
76 |
bocoree.debug.push_log( "index_done=" + index_done ); |
77 |
#endif |
78 |
s_done[index_done] = true; |
79 |
if ( s_last_buffer == index_done ) { |
80 |
s_playing = false; |
81 |
} |
82 |
if ( dwuser >= _NUM_BUF ) { |
83 |
s_wave_header[index_done].dwUser = new IntPtr( index_done ); |
84 |
} |
85 |
#if DEBUG |
86 |
bocoree.debug.push_log( "whdr.dwUser=" + whdr.dwUser.ToInt32() ); |
87 |
bocoree.debug.push_log( "dwParam1=0x" + Convert.ToString( dwParam1, 16 ) + "; dwParam2=0x" + Convert.ToString( dwParam2, 16 ) ); |
88 |
#endif |
89 |
} |
90 |
} |
91 |
|
92 |
void append_cor( float** a_data, uint length, double amp_left, double amp_right, bool is_last_mode ) { |
93 |
#if DEBUG |
94 |
|
95 |
bocoree.debug.push_log( "append_cor *************************************************************" ); |
96 |
bocoree.debug.push_log( " length=" + length ); |
97 |
bocoree.debug.push_log( " s_hwave_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) ); |
98 |
#endif |
99 |
s_playing = true; |
100 |
int jmax = (int)length; |
101 |
int remain = 0; |
102 |
IntPtr ptr_data = IntPtr.Zero; |
103 |
IntPtr ptr_data0 = IntPtr.Zero; |
104 |
IntPtr ptr_data1 = IntPtr.Zero; |
105 |
ptr_data = Marshal.AllocHGlobal( sizeof( float* ) * 2 ); |
106 |
float** data = (float**)ptr_data.ToPointer();// new float*[2]; |
107 |
bool cleaning_required = false; |
108 |
if ( s_error_samples > 0 ) { |
109 |
if ( s_error_samples >= length ) { |
110 |
s_error_samples -= (int)length; |
111 |
return; |
112 |
} |
113 |
cleaning_required = true; |
114 |
int actual_length = (int)length - s_error_samples; |
115 |
#if DEBUG |
116 |
bocoree.debug.push_log( " actual_length=" + actual_length ); |
117 |
#endif |
118 |
ptr_data0 = Marshal.AllocHGlobal( sizeof( float ) * actual_length ); |
119 |
ptr_data1 = Marshal.AllocHGlobal( sizeof( float ) * actual_length ); |
120 |
data[0] = (float*)ptr_data0.ToPointer(); |
121 |
data[1] = (float*)ptr_data1.ToPointer(); |
122 |
for ( int i = 0; i < actual_length; i++ ) { |
123 |
data[0][i] = a_data[0][i + s_error_samples]; |
124 |
data[1][i] = a_data[1][i + s_error_samples]; |
125 |
} |
126 |
s_error_samples = 0; |
127 |
length = (uint)actual_length; |
128 |
jmax = (int)length; |
129 |
} else { |
130 |
data = a_data; |
131 |
} |
132 |
|
133 |
if ( length + s_buffer_loc >= s_block_size ) { |
134 |
jmax = s_block_size - s_buffer_loc; |
135 |
remain = (int)length - (int)jmax; |
136 |
} |
137 |
float aright = (float)amp_right; |
138 |
float aleft = (float)amp_left; |
139 |
|
140 |
for ( int j = 0; j < jmax; j++ ) { |
141 |
s_wave_buffer_l[j + s_buffer_loc] = data[1][j]; |
142 |
s_wave_buffer_r[j + s_buffer_loc] = data[0][j]; |
143 |
} |
144 |
s_buffer_loc += jmax; |
145 |
|
146 |
if ( s_buffer_loc >= s_block_size ) { |
147 |
// バッファー充填完了.バッファーを転送し、waveOutWriteが書き込めるタイミングまで待機 |
148 |
#if DEBUG |
149 |
bocoree.debug.push_log( "append_cor; waiting(1) " + s_current_buffer + "..." ); |
150 |
#endif |
151 |
while ( true ) { |
152 |
if ( s_abort_required ) { |
153 |
s_abort_required = false; |
154 |
goto clean_and_exit; |
155 |
} |
156 |
if ( s_done[s_current_buffer] ) { |
157 |
break; |
158 |
} |
159 |
} |
160 |
#if DEBUG |
161 |
bocoree.debug.push_log( "append_cor; ...exit" ); |
162 |
#endif |
163 |
|
164 |
s_processed_count++; |
165 |
mix( (int)s_processed_count, aleft, aright ); |
166 |
|
167 |
if ( s_processed_count == _NUM_BUF ) { |
168 |
s_done[0] = false; |
169 |
#if DEBUG |
170 |
bocoree.debug.push_log( "calling waveOutWrite...; s_hawve_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) ); |
171 |
#endif |
172 |
uint ret = windows.waveOutWrite( s_hwave_out, ref s_wave_header[0], (uint)sizeof( WAVEHDR ) ); |
173 |
#if DEBUG |
174 |
bocoree.debug.push_log( "...done; ret=" + ret ); |
175 |
#endif |
176 |
#if DEBUG |
177 |
bocoree.debug.push_log( "(s_first_buffer_wirtten_callback==null)=" + (s_first_buffer_written_callback == null) ); |
178 |
#endif |
179 |
if ( s_first_buffer_written_callback != null ) { |
180 |
#if DEBUG |
181 |
bocoree.debug.push_log( "append_cor; calling s_first_buffer_written_callback" ); |
182 |
#endif |
183 |
s_first_buffer_written_callback(); |
184 |
} |
185 |
for ( int buffer_index = 1; buffer_index < _NUM_BUF; buffer_index++ ) { |
186 |
s_done[buffer_index] = false; |
187 |
#if DEBUG |
188 |
bocoree.debug.push_log( "calling waveOutWrite...; s_hawve_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) ); |
189 |
#endif |
190 |
uint ret2 = windows.waveOutWrite( s_hwave_out, ref s_wave_header[buffer_index], (uint)sizeof( WAVEHDR ) ); |
191 |
#if DEBUG |
192 |
bocoree.debug.push_log( "...done; ret2=" + ret2 ); |
193 |
#endif |
194 |
} |
195 |
s_current_buffer = _NUM_BUF - 1; |
196 |
} else if ( s_processed_count > _NUM_BUF ) { |
197 |
s_done[s_current_buffer] = false; |
198 |
#if DEBUG |
199 |
bocoree.debug.push_log( "calling waveOutWrite...; s_hawve_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) ); |
200 |
#endif |
201 |
uint ret3 = windows.waveOutWrite( s_hwave_out, ref s_wave_header[s_current_buffer], (uint)sizeof( WAVEHDR ) ); |
202 |
#if DEBUG |
203 |
bocoree.debug.push_log( "...done; ret3=" + ret3 ); |
204 |
#endif |
205 |
} |
206 |
s_current_buffer++; |
207 |
if ( s_current_buffer >= _NUM_BUF ) { |
208 |
s_current_buffer = 0; |
209 |
} |
210 |
|
211 |
s_buffer_loc = 0; |
212 |
} |
213 |
|
214 |
if ( remain > 0 ) { |
215 |
for ( int j = jmax; j < length; j++ ) { |
216 |
s_wave_buffer_l[j - jmax] = data[1][j]; |
217 |
s_wave_buffer_r[j - jmax] = data[0][j]; |
218 |
} |
219 |
if ( is_last_mode ) { |
220 |
for ( int j = (int)length - jmax; j < s_block_size; j++ ) { |
221 |
s_wave_buffer_l[j] = 0.0f; |
222 |
s_wave_buffer_r[j] = 0.0f; |
223 |
} |
224 |
} |
225 |
s_buffer_loc = remain; |
226 |
} |
227 |
|
228 |
if ( is_last_mode ) { |
229 |
if ( s_processed_count < _NUM_BUF ) { |
230 |
// _NUM_BUFブロック分のデータを未だ全て受信していない場合。バッファが未だひとつも書き込まれていないので |
231 |
// 0番のブロックから順に書き込む |
232 |
s_processed_count++; |
233 |
mix( (int)s_processed_count, aleft, aright ); |
234 |
s_done[0] = false; |
235 |
#if DEBUG |
236 |
bocoree.debug.push_log( "calling waveOutWrite...; s_hawve_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) ); |
237 |
#endif |
238 |
uint ret35 = windows.waveOutWrite( s_hwave_out, ref s_wave_header[0], (uint)sizeof( WAVEHDR ) ); |
239 |
#if DEBUG |
240 |
bocoree.debug.push_log( "...done; ret35=" + ret35 ); |
241 |
bocoree.debug.push_log( "(s_first_buffer_written_callback==null)=" + (s_first_buffer_written_callback == null) ); |
242 |
#endif |
243 |
if ( s_first_buffer_written_callback != null ) { |
244 |
#if DEBUG |
245 |
bocoree.debug.push_log( "append_cor; calling s_first_buffer_written_callback" ); |
246 |
#endif |
247 |
s_first_buffer_written_callback(); |
248 |
} |
249 |
for ( int i = 1; i < _NUM_BUF - 1; i++ ) { |
250 |
s_processed_count++; |
251 |
mix( (int)s_processed_count, aleft, aright ); |
252 |
s_done[i] = false; |
253 |
#if DEBUG |
254 |
bocoree.debug.push_log( "calling waveOutWrite...; s_hawve_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) ); |
255 |
#endif |
256 |
uint ret36 = windows.waveOutWrite( s_hwave_out, ref s_wave_header[i], (uint)sizeof( WAVEHDR ) ); |
257 |
#if DEBUG |
258 |
bocoree.debug.push_log( "...done; ret36=" + ret36 ); |
259 |
#endif |
260 |
} |
261 |
} |
262 |
ulong zero = MAKELONG( 0, 0 ); |
263 |
for ( int j = s_buffer_loc; j < s_block_size; j++ ) { |
264 |
s_wave_buffer_l[j] = 0.0f; |
265 |
s_wave_buffer_r[j] = 0.0f; |
266 |
} |
267 |
#if DEBUG |
268 |
bocoree.debug.push_log( "append_cor; waiting(3) " + s_current_buffer + "..." ); |
269 |
#endif |
270 |
while ( !s_done[s_current_buffer] ) { |
271 |
if ( s_abort_required ) { |
272 |
s_abort_required = false; |
273 |
goto clean_and_exit; |
274 |
} |
275 |
} |
276 |
#if DEBUG |
277 |
bocoree.debug.push_log( "append_cor; ...exit" ); |
278 |
#endif |
279 |
s_processed_count++; |
280 |
mix( (int)s_processed_count, aleft, aright ); |
281 |
s_done[s_current_buffer] = false; |
282 |
#if DEBUG |
283 |
bocoree.debug.push_log( "calling waveOutWrite...; s_hawve_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) ); |
284 |
#endif |
285 |
uint ret4 = windows.waveOutWrite( s_hwave_out, ref s_wave_header[s_current_buffer], (uint)sizeof( WAVEHDR ) ); |
286 |
#if DEBUG |
287 |
bocoree.debug.push_log( "...done; ret4=" + ret4 ); |
288 |
#endif |
289 |
} |
290 |
clean_and_exit: |
291 |
if ( is_last_mode ) { |
292 |
s_last_buffer = s_current_buffer; |
293 |
} |
294 |
if ( cleaning_required ) { |
295 |
Marshal.FreeHGlobal( ptr_data0 ); //delete [] data[0]; |
296 |
Marshal.FreeHGlobal( ptr_data1 ); //delete [] data[1]; |
297 |
Marshal.FreeHGlobal( ptr_data ); //delete [] data; |
298 |
} |
299 |
} |
300 |
|
301 |
void mix( int processed_count, float amp_left, float amp_right ) { |
302 |
int current_buffer = (processed_count - 1) % _NUM_BUF; |
303 |
for ( int k = 0; k < s_num_wave_reader; k++ ) { |
304 |
s_wave_reader[k].Read( s_block_size * (processed_count - 1) + (int)s_wave_read_offset_samples, |
305 |
s_block_size, |
306 |
out s_ptr_another_wave_l[k], |
307 |
out s_ptr_another_wave_r[k] ); |
308 |
} |
309 |
for ( int i = 0; i < s_block_size; i++ ) { |
310 |
float l = s_wave_buffer_l[i] * amp_left; |
311 |
float r = s_wave_buffer_r[i] * amp_right; |
312 |
for ( int k = 0; k < s_num_wave_reader; k++ ) { |
313 |
l += s_another_wave_l[k][i]; |
314 |
r += s_another_wave_r[k][i]; |
315 |
} |
316 |
s_wave[current_buffer][i] = MAKELONG( (ushort)(r * 32768.0f), (ushort)(l * 32768.0f) ); |
317 |
} |
318 |
} |
319 |
|
320 |
string util_get_errmsg( uint msg ) { |
321 |
//IntPtr ptr_err = Marshal.AllocHGlobal( sizeof( byte ) * 260 ); |
322 |
//byte* err = (byte*)ptr_err.ToPointer(); |
323 |
//System.Text.StringBuilder sb = new System.Text.StringBuilder( 260 ); |
324 |
string ret = ""; |
325 |
windows.mciGetErrorStringA( msg, ret, 260 ); |
326 |
/*int len = 260; |
327 |
for ( int i = 1; i < 260; i++ ) { |
328 |
if ( err[i] == '\0' ) { |
329 |
len = i - 1; |
330 |
break; |
331 |
} |
332 |
}*/ |
333 |
//string ret = new string( err ); |
334 |
//string ret = sb.ToString(); |
335 |
//Marshal.FreeHGlobal( ptr_err ); |
336 |
return ret; |
337 |
} |
338 |
|
339 |
private WavePlay() { |
340 |
} |
341 |
|
342 |
/// 初期化関数 |
343 |
public WavePlay( int block_size, int sample_rate ) { |
344 |
#if DEBUG |
345 |
Console.WriteLine( "waveplay..ctor" ); |
346 |
#endif |
347 |
s_block_size = block_size; |
348 |
s_sample_rate = sample_rate; |
349 |
|
350 |
//s_ptr_wave_formatx = Marshal.AllocHGlobal( sizeof( WAVEFORMATEX ) ); |
351 |
s_wave_formatx = new WAVEFORMATEX(); |
352 |
//Marshal.PtrToStructure( s_ptr_wave_formatx, s_wave_formatx ); |
353 |
s_wave_formatx.cbSize = (ushort)sizeof( WAVEFORMATEX ); |
354 |
#if DEBUG |
355 |
Console.WriteLine( " s_wave_fomratx.cbSize=" + s_wave_formatx.cbSize ); |
356 |
Console.WriteLine( " sizeof( WAVEHDR )=" + sizeof( WAVEHDR ) ); |
357 |
#endif |
358 |
s_wave_formatx.wFormatTag = windows.WAVE_FORMAT_PCM; |
359 |
s_wave_formatx.nChannels = 2; |
360 |
s_wave_formatx.wBitsPerSample = 16; |
361 |
s_wave_formatx.nBlockAlign = (ushort)(s_wave_formatx.nChannels * s_wave_formatx.wBitsPerSample / 8); |
362 |
s_wave_formatx.nSamplesPerSec = (uint)s_sample_rate; |
363 |
s_wave_formatx.nAvgBytesPerSec = s_wave_formatx.nSamplesPerSec * s_wave_formatx.nBlockAlign; |
364 |
|
365 |
s_wave_callback = new delegateWaveOutProc( wave_callback ); |
366 |
s_hwave_out = IntPtr.Zero; |
367 |
Console.WriteLine( " calling waveOutOpen..." ); |
368 |
uint ret = windows.waveOutOpen( ref s_hwave_out, |
369 |
windows.WAVE_MAPPER, |
370 |
ref s_wave_formatx, |
371 |
s_wave_callback, |
372 |
IntPtr.Zero, |
373 |
(uint)windows.CALLBACK_FUNCTION ); |
374 |
Console.WriteLine( " ...done; ret=" + ret ); |
375 |
#if DEBUG |
376 |
bocoree.debug.push_log( " s_hwave_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) ); |
377 |
#endif |
378 |
|
379 |
for ( int k = 0; k < _NUM_BUF; k++ ) { |
380 |
s_ptr_wave[k] = Marshal.AllocHGlobal( sizeof( uint ) * s_block_size ); |
381 |
s_wave[k] = (uint*)s_ptr_wave[k];// = (ulong*)calloc( sizeof( ulong ), s_block_size ); |
382 |
s_ptr_wave_header[k] = Marshal.AllocHGlobal( sizeof( WAVEHDR ) ); |
383 |
s_wave_header[k] = (WAVEHDR)Marshal.PtrToStructure( s_ptr_wave_header[k], typeof( WAVEHDR ) ); |
384 |
s_wave_header[k].lpData = s_ptr_wave[k]; |
385 |
s_wave_header[k].dwBufferLength = (uint)(sizeof( uint ) * s_block_size); |
386 |
s_wave_header[k].dwFlags = windows.WHDR_BEGINLOOP | windows.WHDR_ENDLOOP; |
387 |
s_wave_header[k].dwLoops = 1; |
388 |
|
389 |
#if DEBUG |
390 |
Console.WriteLine( "calling waveOutPrepareHeader..." ); |
391 |
#endif |
392 |
uint ret2 = windows.waveOutPrepareHeader( s_hwave_out, ref s_wave_header[k], (uint)sizeof( WAVEHDR ) ); |
393 |
#if DEBUG |
394 |
Console.WriteLine( "...done; ret2=" + ret2 ); |
395 |
#endif |
396 |
s_wave_header[k].dwUser = new IntPtr( k ); |
397 |
} |
398 |
#if DEBUG |
399 |
bocoree.debug.push_log( " exit waveplay..ctor; s_hwave_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) ); |
400 |
#endif |
401 |
} |
402 |
|
403 |
/// 波形データをバッファに追加する。バッファが再生中などの理由で即座に書き込めない場合、バッファが書き込み可能となるまで待機させられる |
404 |
public void append( float** data, uint length, double amp_left, double amp_right ) { |
405 |
append_cor( data, length, amp_left, amp_right, false ); |
406 |
} |
407 |
|
408 |
public void flush_and_exit( double amp_left, double amp_right ) { |
409 |
append_cor( (float**)0, 0, amp_left, amp_right, true ); |
410 |
} |
411 |
|
412 |
/// 再生中断を要求する |
413 |
public void abort() { |
414 |
s_abort_required = true; |
415 |
reset(); |
416 |
for ( int k = 0; k < _NUM_BUF; k++ ) { |
417 |
if ( s_ptr_wave[k] != IntPtr.Zero ) { |
418 |
for ( int i = 0; i < s_block_size; i++ ) { |
419 |
s_wave[k][i] = 0; |
420 |
} |
421 |
//memset( s_wave[k], 0, s_block_size * sizeof( ulong ) ); |
422 |
} |
423 |
} |
424 |
s_buffer_loc = 0; |
425 |
s_current_buffer = 0; |
426 |
s_processed_count = 0; |
427 |
} |
428 |
|
429 |
/// 現在の再生位置を取得する。再生中でない場合負の値となる。 |
430 |
public float get_play_time() { |
431 |
#if DEBUG |
432 |
bocoree.debug.push_log( "WavePlay.get_play_time" ); |
433 |
#endif |
434 |
if ( s_playing ) { |
435 |
MMTIME mmt = new MMTIME(); |
436 |
mmt.cb = (uint)sizeof( MMTIME ); |
437 |
mmt.wType = windows.TIME_MS; |
438 |
uint ret = windows.waveOutGetPosition( s_hwave_out, ref mmt, (uint)sizeof( MMTIME ) ); |
439 |
#if DEBUG |
440 |
bocoree.debug.push_log( " ret=" + ret ); |
441 |
#endif |
442 |
float ms = 0.0f; |
443 |
switch ( mmt.wType ) { |
444 |
case windows.TIME_MS: |
445 |
return mmt.ms * 0.001f; |
446 |
case windows.TIME_SAMPLES: |
447 |
return (float)mmt.sample / (float)s_wave_formatx.nSamplesPerSec; |
448 |
case windows.TIME_BYTES: |
449 |
return (float)mmt.cb / (float)s_wave_formatx.nAvgBytesPerSec; |
450 |
default: |
451 |
return -1.0f; |
452 |
} |
453 |
} else { |
454 |
return -1.0f; |
455 |
} |
456 |
} |
457 |
|
458 |
/// リセットする。abort関数でも呼び出される。 |
459 |
public void reset() { |
460 |
s_playing = false; |
461 |
if ( s_hwave_out.ToInt32() != 0 ) { |
462 |
for ( int k = 0; k < _NUM_BUF; k++ ) { |
463 |
s_wave_header[k].dwUser = new IntPtr( _NUM_BUF + k ); |
464 |
} |
465 |
windows.waveOutReset( s_hwave_out ); |
466 |
uint zero = MAKELONG( 0, 0 ); |
467 |
for ( int k = 0; k < _NUM_BUF; k++ ) { |
468 |
for ( int i = 0; i < s_block_size; i++ ) { |
469 |
s_wave[k][i] = zero; |
470 |
} |
471 |
} |
472 |
} |
473 |
for ( int i = 0; i < s_num_wave_reader; i++ ) { |
474 |
s_wave_reader[i].Close(); |
475 |
} |
476 |
} |
477 |
|
478 |
/// 再生のための準備を行う。この関数を呼び出した後は、バッファが再生開始されるまでget_play_timeの戻り値は0となる(負値にならない)。 |
479 |
/// 戻り値は、filesに指定されたファイルの内、最も再生時間の長いwaveファイルの、合計サンプル数 |
480 |
public int on_your_mark( string[] files, long wave_read_offset_samples ) { |
481 |
#if DEBUG |
482 |
bocoree.debug.push_log( "on_your_mark; s_hwave_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) ); |
483 |
#endif |
484 |
int num_files = files.Length; |
485 |
reset(); |
486 |
s_wave_read_offset_samples = wave_read_offset_samples; |
487 |
for ( int k = 0; k < _NUM_BUF; k++ ) { |
488 |
s_wave_header[k].dwUser = new IntPtr( k ); |
489 |
s_done[k] = true; |
490 |
} |
491 |
s_abort_required = false; |
492 |
s_buffer_loc = 0; |
493 |
s_current_buffer = 0; |
494 |
s_processed_count = 0; |
495 |
s_playing = true; |
496 |
s_last_buffer = -1; |
497 |
|
498 |
if ( (int)s_ptr_wave_buffer_l.ToPointer() == 0 ) { |
499 |
s_ptr_wave_buffer_l = Marshal.AllocHGlobal( sizeof( float ) * s_block_size );// s_wave_buffer_l = new float[s_block_size]; |
500 |
s_wave_buffer_l = (float*)s_ptr_wave_buffer_l.ToPointer(); |
501 |
} |
502 |
if ( (int)s_ptr_wave_buffer_r.ToPointer() == 0 ) { |
503 |
s_ptr_wave_buffer_r = Marshal.AllocHGlobal( sizeof( float ) * s_block_size ); //s_wave_buffer_r = new float[s_block_size]; |
504 |
s_wave_buffer_r = (float*)s_ptr_wave_buffer_r.ToPointer(); |
505 |
} |
506 |
|
507 |
if ( s_wave_reader != null ) { |
508 |
for ( int i = 0; i < s_num_wave_reader; i++ ) { |
509 |
s_wave_reader[i].Close(); |
510 |
} |
511 |
//delete [] s_wave_reader; |
512 |
} |
513 |
s_wave_reader = new WaveReader[num_files]; |
514 |
|
515 |
if ( s_another_wave_l != null ) { |
516 |
for ( int i = 0; i < s_num_wave_reader; i++ ) { |
517 |
Marshal.FreeHGlobal( s_ptr_another_wave_l[i] );// delete [] s_another_wave_l[i]; |
518 |
} |
519 |
//delete [] s_another_wave_l; |
520 |
} |
521 |
if ( s_another_wave_r != null ) { |
522 |
for ( int i = 0; i < s_num_wave_reader; i++ ) { |
523 |
Marshal.FreeHGlobal( s_ptr_another_wave_r[i] ); //delete [] s_another_wave_r[i]; |
524 |
} |
525 |
//delete [] s_another_wave_r; |
526 |
} |
527 |
s_another_wave_l = new float*[num_files]; |
528 |
s_another_wave_r = new float*[num_files]; |
529 |
int max_samples = 0; |
530 |
for ( int i = 0; i < num_files; i++ ) { |
531 |
// waveファイルヘッダを読込む |
532 |
/*int len = files[i].Length; |
533 |
wchar_t *name = new wchar_t[len + 1]; |
534 |
array<wchar_t> ^buf = files[i]->ToCharArray(); |
535 |
for( int k = 0; k < len; k++ ){ |
536 |
name[k] = buf[k]; |
537 |
} |
538 |
name[len] = '\0';*/ |
539 |
s_wave_reader[i].Open( files[i] ); |
540 |
int samples = s_wave_reader[i].TotalSamples; |
541 |
if ( samples > max_samples ) { |
542 |
max_samples = samples; |
543 |
} |
544 |
//delete [] name; |
545 |
|
546 |
// バッファを用意 |
547 |
s_ptr_another_wave_l[i] = Marshal.AllocHGlobal( sizeof( float ) * s_block_size ); |
548 |
s_another_wave_l[i] = (float*)s_ptr_another_wave_l[i].ToPointer(); |
549 |
s_ptr_another_wave_r[i] = Marshal.AllocHGlobal( sizeof( float ) * s_block_size ); |
550 |
s_another_wave_r[i] = (float*)s_ptr_another_wave_r[i].ToPointer(); |
551 |
//s_another_wave_l[i] = new float[s_block_size]; |
552 |
//s_another_wave_r[i] = new float[s_block_size]; |
553 |
} |
554 |
s_num_wave_reader = num_files; |
555 |
return max_samples; |
556 |
} |
557 |
|
558 |
public void set_error_samples( int error_samples ) { |
559 |
s_error_samples = error_samples; |
560 |
} |
561 |
|
562 |
/// コールバック関数を設定する |
563 |
public void set_first_buffer_written_callback( FirstBufferWrittenCallback proc ) { |
564 |
s_first_buffer_written_callback = proc; |
565 |
} |
566 |
|
567 |
public void terminate() { |
568 |
if ( s_hwave_out.ToInt32() != 0 ) { |
569 |
windows.waveOutReset( s_hwave_out ); |
570 |
#if DEBUG |
571 |
bocoree.debug.push_log( "waveplay::terminate; waveOutReset" ); |
572 |
#endif |
573 |
for ( int k = 0; k < _NUM_BUF; k++ ) { |
574 |
windows.waveOutUnprepareHeader( s_hwave_out, ref s_wave_header[k], (uint)sizeof( WAVEHDR ) ); |
575 |
} |
576 |
windows.waveOutClose( s_hwave_out ); |
577 |
} |
578 |
for ( int i = 0; i < _NUM_BUF; i++ ) { |
579 |
if ( s_ptr_wave[i].ToInt32() != 0 ) { |
580 |
Marshal.FreeHGlobal( s_ptr_wave[i] ); //delete [] s_wave[i]; |
581 |
} |
582 |
} |
583 |
} |
584 |
|
585 |
/// 現在再生中かどうかを取得する |
586 |
public bool is_alive() { |
587 |
return s_playing; |
588 |
} |
589 |
|
590 |
/// ブロックサイズを変更します |
591 |
public bool change_block_size( int block_size ) { |
592 |
if ( s_playing ) { |
593 |
return false; |
594 |
} |
595 |
if ( block_size <= 0 ) { |
596 |
return false; |
597 |
} |
598 |
|
599 |
for ( int k = 0; k < _NUM_BUF; k++ ) { |
600 |
if ( s_ptr_wave[k].ToInt32() != 0 ) { |
601 |
Marshal.FreeHGlobal( s_ptr_wave[k] );// delete [] s_wave[k]; |
602 |
} |
603 |
s_ptr_wave[k] = Marshal.AllocHGlobal( sizeof( uint ) * block_size ); |
604 |
s_wave[k] = (uint*)s_ptr_wave[k].ToPointer();// calloc( sizeof( ulong ), block_size ); |
605 |
s_wave_header[k].lpData = s_ptr_wave[k]; |
606 |
s_wave_header[k].dwBufferLength = (uint)(sizeof( uint ) * block_size); |
607 |
} |
608 |
|
609 |
// s_wave_buffer_l, s_wave_buffer_rは、NULLならばon_your_markで初期化されるので、開放だけやっておけばOK |
610 |
if ( s_ptr_wave_buffer_l.ToInt32() != 0 ) { |
611 |
Marshal.FreeHGlobal( s_ptr_wave_buffer_l ); //delete [] s_wave_buffer_l; |
612 |
} |
613 |
if ( s_ptr_wave_buffer_r.ToInt32() != 0 ) { |
614 |
Marshal.FreeHGlobal( s_ptr_wave_buffer_r ); //delete[] s_wave_buffer_r; |
615 |
} |
616 |
// s_another_wave_l, s_another_wave_rは、on_your_markで全自動で初期化されるので特に操作の必要なし |
617 |
s_block_size = block_size; |
618 |
return true; |
619 |
} |
620 |
|
621 |
uint MAKELONG( ushort a, ushort b ) { |
622 |
return (uint)(a & 0xffff) | (uint)((b & 0xffff) << 16); |
623 |
} |
624 |
} |
625 |
|
626 |
} |