2009-06-25 07:16:22 -07:00
|
|
|
|
/*
|
|
|
|
|
* WavePlay.cs
|
|
|
|
|
* Copyright (c) 2009 kbinani
|
|
|
|
|
*
|
|
|
|
|
* This file is part of Boare.Lib.Media.
|
|
|
|
|
*
|
|
|
|
|
* Boare.Lib.Media is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the BSD License.
|
|
|
|
|
*
|
|
|
|
|
* Boare.Lib.Media is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
|
*/
|
|
|
|
|
using System;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
|
|
|
|
|
using bocoree;
|
|
|
|
|
|
|
|
|
|
namespace Boare.Lib.Media {
|
|
|
|
|
|
|
|
|
|
public delegate void FirstBufferWrittenCallback();
|
|
|
|
|
|
|
|
|
|
public unsafe class WavePlay {
|
|
|
|
|
const int _NUM_BUF = 3; // バッファの数
|
|
|
|
|
int s_block_size; // 1個のバッファのサイズ(サンプル)
|
|
|
|
|
int s_sample_rate; // サンプリングレート
|
|
|
|
|
WAVEFORMATEX s_wave_formatx; // WAVEファイルヘッダ
|
|
|
|
|
//IntPtr s_ptr_wave_formatx;
|
|
|
|
|
IntPtr s_hwave_out; // WAVE再生デバイス
|
|
|
|
|
|
|
|
|
|
WAVEHDR[] s_wave_header = new WAVEHDR[_NUM_BUF];// WAVEヘッダ
|
|
|
|
|
IntPtr[] s_ptr_wave_header = new IntPtr[_NUM_BUF];
|
|
|
|
|
|
|
|
|
|
uint*[] s_wave = new uint*[_NUM_BUF]; // バッファ
|
|
|
|
|
IntPtr[] s_ptr_wave = new IntPtr[_NUM_BUF];
|
|
|
|
|
|
|
|
|
|
bool[] s_done = new bool[_NUM_BUF];
|
|
|
|
|
int s_current_buffer; // 次回書き込むべきバッファのインデクス
|
|
|
|
|
uint s_processed_count; // 初回はバッファを_NUM_BUF個全部埋めなければいけないので、最初の _NUM_BUF + 1 回はカウントを行う。そのためのカウンタ
|
|
|
|
|
bool s_abort_required; // 再生の中断が要求された時立つフラグ
|
|
|
|
|
int s_buffer_loc; // 書き込み中のバッファ内の、現在位置
|
|
|
|
|
bool s_playing; // 再生中かどうかを表すフラグ
|
|
|
|
|
int s_error_samples; // appendされた波形データの内、先頭のs_error_samples分を省く。通常の使い方なら常に0だが、vocaloid2 vstiで使う場合、プリセンド分を除いてwaveOutWriteしなければいけないので非0になる。
|
|
|
|
|
int s_last_buffer; // 最後に再生されるバッファの番号。負値の場合、append_lastが未だ呼ばれていないことを意味する。
|
|
|
|
|
FirstBufferWrittenCallback s_first_buffer_written_callback; // 最初のバッファが書き込まれたとき呼び出されるコールバック関数
|
|
|
|
|
WaveReader[] s_wave_reader;
|
|
|
|
|
int s_num_wave_reader; // s_wave_readerの個数
|
|
|
|
|
float*[] s_another_wave_l;
|
|
|
|
|
IntPtr[] s_ptr_another_wave_l;
|
|
|
|
|
float*[] s_another_wave_r;
|
|
|
|
|
IntPtr[] s_ptr_another_wave_r;
|
|
|
|
|
long s_wave_read_offset_samples;
|
|
|
|
|
float* s_wave_buffer_l;
|
|
|
|
|
IntPtr s_ptr_wave_buffer_l;
|
|
|
|
|
float* s_wave_buffer_r;
|
|
|
|
|
IntPtr s_ptr_wave_buffer_r;
|
|
|
|
|
|
|
|
|
|
delegateWaveOutProc s_wave_callback;
|
|
|
|
|
|
|
|
|
|
/// コールバック関数
|
|
|
|
|
void wave_callback( IntPtr hwo, uint uMsg, uint dwInstance, uint dwParam1, uint dwParam2 ) {
|
|
|
|
|
#if DEBUG
|
|
|
|
|
Console.WriteLine( "WavePlay.wave_callback; uMsg=" + uMsg );
|
|
|
|
|
#endif
|
2010-03-16 20:14:08 -07:00
|
|
|
|
if ( uMsg == win32.MM_WOM_DONE ) {
|
2009-06-25 07:16:22 -07:00
|
|
|
|
int index_done = 0;
|
|
|
|
|
WAVEHDR whdr = (WAVEHDR)Marshal.PtrToStructure( new IntPtr( dwParam1 ), typeof( WAVEHDR ) );
|
|
|
|
|
int dwuser = whdr.dwUser.ToInt32();
|
|
|
|
|
if ( dwuser >= _NUM_BUF ) {
|
|
|
|
|
index_done = dwuser - _NUM_BUF;
|
|
|
|
|
} else {
|
|
|
|
|
index_done = dwuser;
|
|
|
|
|
}
|
2009-07-29 10:03:20 -07:00
|
|
|
|
if ( 0 <= index_done && index_done < _NUM_BUF ) {
|
|
|
|
|
s_done[index_done] = true;
|
|
|
|
|
if ( s_last_buffer == index_done ) {
|
|
|
|
|
s_playing = false;
|
|
|
|
|
}
|
|
|
|
|
if ( dwuser >= _NUM_BUF ) {
|
|
|
|
|
s_wave_header[index_done].dwUser = new IntPtr( index_done );
|
|
|
|
|
}
|
2009-06-25 07:16:22 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void append_cor( float** a_data, uint length, double amp_left, double amp_right, bool is_last_mode ) {
|
|
|
|
|
#if DEBUG
|
|
|
|
|
|
|
|
|
|
bocoree.debug.push_log( "append_cor *************************************************************" );
|
|
|
|
|
bocoree.debug.push_log( " length=" + length );
|
|
|
|
|
bocoree.debug.push_log( " s_hwave_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) );
|
|
|
|
|
#endif
|
|
|
|
|
s_playing = true;
|
|
|
|
|
int jmax = (int)length;
|
|
|
|
|
int remain = 0;
|
|
|
|
|
IntPtr ptr_data = IntPtr.Zero;
|
|
|
|
|
IntPtr ptr_data0 = IntPtr.Zero;
|
|
|
|
|
IntPtr ptr_data1 = IntPtr.Zero;
|
|
|
|
|
ptr_data = Marshal.AllocHGlobal( sizeof( float* ) * 2 );
|
|
|
|
|
float** data = (float**)ptr_data.ToPointer();// new float*[2];
|
|
|
|
|
bool cleaning_required = false;
|
|
|
|
|
if ( s_error_samples > 0 ) {
|
|
|
|
|
if ( s_error_samples >= length ) {
|
|
|
|
|
s_error_samples -= (int)length;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
cleaning_required = true;
|
|
|
|
|
int actual_length = (int)length - s_error_samples;
|
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( " actual_length=" + actual_length );
|
|
|
|
|
#endif
|
|
|
|
|
ptr_data0 = Marshal.AllocHGlobal( sizeof( float ) * actual_length );
|
|
|
|
|
ptr_data1 = Marshal.AllocHGlobal( sizeof( float ) * actual_length );
|
|
|
|
|
data[0] = (float*)ptr_data0.ToPointer();
|
|
|
|
|
data[1] = (float*)ptr_data1.ToPointer();
|
|
|
|
|
for ( int i = 0; i < actual_length; i++ ) {
|
|
|
|
|
data[0][i] = a_data[0][i + s_error_samples];
|
|
|
|
|
data[1][i] = a_data[1][i + s_error_samples];
|
|
|
|
|
}
|
|
|
|
|
s_error_samples = 0;
|
|
|
|
|
length = (uint)actual_length;
|
|
|
|
|
jmax = (int)length;
|
|
|
|
|
} else {
|
|
|
|
|
data = a_data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( length + s_buffer_loc >= s_block_size ) {
|
|
|
|
|
jmax = s_block_size - s_buffer_loc;
|
|
|
|
|
remain = (int)length - (int)jmax;
|
|
|
|
|
}
|
|
|
|
|
float aright = (float)amp_right;
|
|
|
|
|
float aleft = (float)amp_left;
|
|
|
|
|
|
|
|
|
|
for ( int j = 0; j < jmax; j++ ) {
|
|
|
|
|
s_wave_buffer_l[j + s_buffer_loc] = data[1][j];
|
|
|
|
|
s_wave_buffer_r[j + s_buffer_loc] = data[0][j];
|
|
|
|
|
}
|
|
|
|
|
s_buffer_loc += jmax;
|
|
|
|
|
|
|
|
|
|
if ( s_buffer_loc >= s_block_size ) {
|
|
|
|
|
// バッファー充填完了.バッファーを転送し、waveOutWriteが書き込めるタイミングまで待機
|
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "append_cor; waiting(1) " + s_current_buffer + "..." );
|
|
|
|
|
#endif
|
|
|
|
|
while ( true ) {
|
|
|
|
|
if ( s_abort_required ) {
|
|
|
|
|
s_abort_required = false;
|
|
|
|
|
goto clean_and_exit;
|
|
|
|
|
}
|
|
|
|
|
if ( s_done[s_current_buffer] ) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "append_cor; ...exit" );
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
s_processed_count++;
|
|
|
|
|
mix( (int)s_processed_count, aleft, aright );
|
|
|
|
|
|
|
|
|
|
if ( s_processed_count == _NUM_BUF ) {
|
|
|
|
|
s_done[0] = false;
|
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "calling waveOutWrite...; s_hawve_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) );
|
|
|
|
|
#endif
|
2010-03-16 20:14:08 -07:00
|
|
|
|
uint ret = win32.waveOutWrite( s_hwave_out, ref s_wave_header[0], (uint)sizeof( WAVEHDR ) );
|
2009-06-25 07:16:22 -07:00
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "...done; ret=" + ret );
|
|
|
|
|
#endif
|
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "(s_first_buffer_wirtten_callback==null)=" + (s_first_buffer_written_callback == null) );
|
|
|
|
|
#endif
|
|
|
|
|
if ( s_first_buffer_written_callback != null ) {
|
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "append_cor; calling s_first_buffer_written_callback" );
|
|
|
|
|
#endif
|
|
|
|
|
s_first_buffer_written_callback();
|
|
|
|
|
}
|
|
|
|
|
for ( int buffer_index = 1; buffer_index < _NUM_BUF; buffer_index++ ) {
|
|
|
|
|
s_done[buffer_index] = false;
|
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "calling waveOutWrite...; s_hawve_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) );
|
|
|
|
|
#endif
|
2010-03-16 20:14:08 -07:00
|
|
|
|
uint ret2 = win32.waveOutWrite( s_hwave_out, ref s_wave_header[buffer_index], (uint)sizeof( WAVEHDR ) );
|
2009-06-25 07:16:22 -07:00
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "...done; ret2=" + ret2 );
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
s_current_buffer = _NUM_BUF - 1;
|
|
|
|
|
} else if ( s_processed_count > _NUM_BUF ) {
|
|
|
|
|
s_done[s_current_buffer] = false;
|
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "calling waveOutWrite...; s_hawve_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) );
|
|
|
|
|
#endif
|
2010-03-16 20:14:08 -07:00
|
|
|
|
uint ret3 = win32.waveOutWrite( s_hwave_out, ref s_wave_header[s_current_buffer], (uint)sizeof( WAVEHDR ) );
|
2009-06-25 07:16:22 -07:00
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "...done; ret3=" + ret3 );
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
s_current_buffer++;
|
|
|
|
|
if ( s_current_buffer >= _NUM_BUF ) {
|
|
|
|
|
s_current_buffer = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s_buffer_loc = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( remain > 0 ) {
|
|
|
|
|
for ( int j = jmax; j < length; j++ ) {
|
|
|
|
|
s_wave_buffer_l[j - jmax] = data[1][j];
|
|
|
|
|
s_wave_buffer_r[j - jmax] = data[0][j];
|
|
|
|
|
}
|
|
|
|
|
if ( is_last_mode ) {
|
|
|
|
|
for ( int j = (int)length - jmax; j < s_block_size; j++ ) {
|
|
|
|
|
s_wave_buffer_l[j] = 0.0f;
|
|
|
|
|
s_wave_buffer_r[j] = 0.0f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
s_buffer_loc = remain;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( is_last_mode ) {
|
|
|
|
|
if ( s_processed_count < _NUM_BUF ) {
|
|
|
|
|
// _NUM_BUFブロック分のデータを未だ全て受信していない場合。バッファが未だひとつも書き込まれていないので
|
|
|
|
|
// 0番のブロックから順に書き込む
|
|
|
|
|
s_processed_count++;
|
|
|
|
|
mix( (int)s_processed_count, aleft, aright );
|
|
|
|
|
s_done[0] = false;
|
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "calling waveOutWrite...; s_hawve_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) );
|
|
|
|
|
#endif
|
2010-03-16 20:14:08 -07:00
|
|
|
|
uint ret35 = win32.waveOutWrite( s_hwave_out, ref s_wave_header[0], (uint)sizeof( WAVEHDR ) );
|
2009-06-25 07:16:22 -07:00
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "...done; ret35=" + ret35 );
|
|
|
|
|
bocoree.debug.push_log( "(s_first_buffer_written_callback==null)=" + (s_first_buffer_written_callback == null) );
|
|
|
|
|
#endif
|
|
|
|
|
if ( s_first_buffer_written_callback != null ) {
|
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "append_cor; calling s_first_buffer_written_callback" );
|
|
|
|
|
#endif
|
|
|
|
|
s_first_buffer_written_callback();
|
|
|
|
|
}
|
|
|
|
|
for ( int i = 1; i < _NUM_BUF - 1; i++ ) {
|
|
|
|
|
s_processed_count++;
|
|
|
|
|
mix( (int)s_processed_count, aleft, aright );
|
|
|
|
|
s_done[i] = false;
|
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "calling waveOutWrite...; s_hawve_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) );
|
|
|
|
|
#endif
|
2010-03-16 20:14:08 -07:00
|
|
|
|
uint ret36 = win32.waveOutWrite( s_hwave_out, ref s_wave_header[i], (uint)sizeof( WAVEHDR ) );
|
2009-06-25 07:16:22 -07:00
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "...done; ret36=" + ret36 );
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ulong zero = MAKELONG( 0, 0 );
|
|
|
|
|
for ( int j = s_buffer_loc; j < s_block_size; j++ ) {
|
|
|
|
|
s_wave_buffer_l[j] = 0.0f;
|
|
|
|
|
s_wave_buffer_r[j] = 0.0f;
|
|
|
|
|
}
|
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "append_cor; waiting(3) " + s_current_buffer + "..." );
|
|
|
|
|
#endif
|
|
|
|
|
while ( !s_done[s_current_buffer] ) {
|
|
|
|
|
if ( s_abort_required ) {
|
|
|
|
|
s_abort_required = false;
|
|
|
|
|
goto clean_and_exit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "append_cor; ...exit" );
|
|
|
|
|
#endif
|
|
|
|
|
s_processed_count++;
|
|
|
|
|
mix( (int)s_processed_count, aleft, aright );
|
|
|
|
|
s_done[s_current_buffer] = false;
|
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "calling waveOutWrite...; s_hawve_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) );
|
|
|
|
|
#endif
|
2010-03-16 20:14:08 -07:00
|
|
|
|
uint ret4 = win32.waveOutWrite( s_hwave_out, ref s_wave_header[s_current_buffer], (uint)sizeof( WAVEHDR ) );
|
2009-06-25 07:16:22 -07:00
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "...done; ret4=" + ret4 );
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
clean_and_exit:
|
|
|
|
|
if ( is_last_mode ) {
|
|
|
|
|
s_last_buffer = s_current_buffer;
|
|
|
|
|
}
|
|
|
|
|
if ( cleaning_required ) {
|
|
|
|
|
Marshal.FreeHGlobal( ptr_data0 ); //delete [] data[0];
|
|
|
|
|
Marshal.FreeHGlobal( ptr_data1 ); //delete [] data[1];
|
|
|
|
|
Marshal.FreeHGlobal( ptr_data ); //delete [] data;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void mix( int processed_count, float amp_left, float amp_right ) {
|
|
|
|
|
int current_buffer = (processed_count - 1) % _NUM_BUF;
|
|
|
|
|
for ( int k = 0; k < s_num_wave_reader; k++ ) {
|
2010-03-16 20:14:08 -07:00
|
|
|
|
s_wave_reader[k].read( s_block_size * (processed_count - 1) + (int)s_wave_read_offset_samples,
|
2009-06-25 07:16:22 -07:00
|
|
|
|
s_block_size,
|
2010-03-16 20:14:08 -07:00
|
|
|
|
ref s_ptr_another_wave_l[k],
|
|
|
|
|
ref s_ptr_another_wave_r[k] );
|
2009-06-25 07:16:22 -07:00
|
|
|
|
}
|
|
|
|
|
for ( int i = 0; i < s_block_size; i++ ) {
|
|
|
|
|
float l = s_wave_buffer_l[i] * amp_left;
|
|
|
|
|
float r = s_wave_buffer_r[i] * amp_right;
|
|
|
|
|
for ( int k = 0; k < s_num_wave_reader; k++ ) {
|
|
|
|
|
l += s_another_wave_l[k][i];
|
|
|
|
|
r += s_another_wave_r[k][i];
|
|
|
|
|
}
|
|
|
|
|
s_wave[current_buffer][i] = MAKELONG( (ushort)(r * 32768.0f), (ushort)(l * 32768.0f) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string util_get_errmsg( uint msg ) {
|
|
|
|
|
//IntPtr ptr_err = Marshal.AllocHGlobal( sizeof( byte ) * 260 );
|
|
|
|
|
//byte* err = (byte*)ptr_err.ToPointer();
|
|
|
|
|
//System.Text.StringBuilder sb = new System.Text.StringBuilder( 260 );
|
|
|
|
|
string ret = "";
|
2010-03-16 20:14:08 -07:00
|
|
|
|
win32.mciGetErrorStringA( msg, ret, 260 );
|
2009-06-25 07:16:22 -07:00
|
|
|
|
/*int len = 260;
|
|
|
|
|
for ( int i = 1; i < 260; i++ ) {
|
|
|
|
|
if ( err[i] == '\0' ) {
|
|
|
|
|
len = i - 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}*/
|
|
|
|
|
//string ret = new string( err );
|
|
|
|
|
//string ret = sb.ToString();
|
|
|
|
|
//Marshal.FreeHGlobal( ptr_err );
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private WavePlay() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 初期化関数
|
|
|
|
|
public WavePlay( int block_size, int sample_rate ) {
|
|
|
|
|
#if DEBUG
|
|
|
|
|
Console.WriteLine( "waveplay..ctor" );
|
|
|
|
|
#endif
|
|
|
|
|
s_block_size = block_size;
|
|
|
|
|
s_sample_rate = sample_rate;
|
|
|
|
|
|
|
|
|
|
//s_ptr_wave_formatx = Marshal.AllocHGlobal( sizeof( WAVEFORMATEX ) );
|
|
|
|
|
s_wave_formatx = new WAVEFORMATEX();
|
|
|
|
|
//Marshal.PtrToStructure( s_ptr_wave_formatx, s_wave_formatx );
|
|
|
|
|
s_wave_formatx.cbSize = (ushort)sizeof( WAVEFORMATEX );
|
|
|
|
|
#if DEBUG
|
|
|
|
|
Console.WriteLine( " s_wave_fomratx.cbSize=" + s_wave_formatx.cbSize );
|
|
|
|
|
Console.WriteLine( " sizeof( WAVEHDR )=" + sizeof( WAVEHDR ) );
|
|
|
|
|
#endif
|
2010-03-16 20:14:08 -07:00
|
|
|
|
s_wave_formatx.wFormatTag = win32.WAVE_FORMAT_PCM;
|
2009-06-25 07:16:22 -07:00
|
|
|
|
s_wave_formatx.nChannels = 2;
|
|
|
|
|
s_wave_formatx.wBitsPerSample = 16;
|
|
|
|
|
s_wave_formatx.nBlockAlign = (ushort)(s_wave_formatx.nChannels * s_wave_formatx.wBitsPerSample / 8);
|
|
|
|
|
s_wave_formatx.nSamplesPerSec = (uint)s_sample_rate;
|
|
|
|
|
s_wave_formatx.nAvgBytesPerSec = s_wave_formatx.nSamplesPerSec * s_wave_formatx.nBlockAlign;
|
|
|
|
|
|
|
|
|
|
s_wave_callback = new delegateWaveOutProc( wave_callback );
|
|
|
|
|
s_hwave_out = IntPtr.Zero;
|
|
|
|
|
Console.WriteLine( " calling waveOutOpen..." );
|
2010-03-16 20:14:08 -07:00
|
|
|
|
uint ret = win32.waveOutOpen( ref s_hwave_out,
|
|
|
|
|
win32.WAVE_MAPPER,
|
2009-06-25 07:16:22 -07:00
|
|
|
|
ref s_wave_formatx,
|
|
|
|
|
s_wave_callback,
|
2010-03-16 20:14:08 -07:00
|
|
|
|
IntPtr.Zero,
|
|
|
|
|
(uint)win32.CALLBACK_FUNCTION );
|
2009-06-25 07:16:22 -07:00
|
|
|
|
Console.WriteLine( " ...done; ret=" + ret );
|
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( " s_hwave_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) );
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
for ( int k = 0; k < _NUM_BUF; k++ ) {
|
|
|
|
|
s_ptr_wave[k] = Marshal.AllocHGlobal( sizeof( uint ) * s_block_size );
|
|
|
|
|
s_wave[k] = (uint*)s_ptr_wave[k];// = (ulong*)calloc( sizeof( ulong ), s_block_size );
|
|
|
|
|
s_ptr_wave_header[k] = Marshal.AllocHGlobal( sizeof( WAVEHDR ) );
|
|
|
|
|
s_wave_header[k] = (WAVEHDR)Marshal.PtrToStructure( s_ptr_wave_header[k], typeof( WAVEHDR ) );
|
|
|
|
|
s_wave_header[k].lpData = s_ptr_wave[k];
|
|
|
|
|
s_wave_header[k].dwBufferLength = (uint)(sizeof( uint ) * s_block_size);
|
2010-03-16 20:14:08 -07:00
|
|
|
|
s_wave_header[k].dwFlags = win32.WHDR_BEGINLOOP | win32.WHDR_ENDLOOP;
|
2009-06-25 07:16:22 -07:00
|
|
|
|
s_wave_header[k].dwLoops = 1;
|
|
|
|
|
|
|
|
|
|
#if DEBUG
|
|
|
|
|
Console.WriteLine( "calling waveOutPrepareHeader..." );
|
|
|
|
|
#endif
|
2010-03-16 20:14:08 -07:00
|
|
|
|
uint ret2 = win32.waveOutPrepareHeader( s_hwave_out, ref s_wave_header[k], (uint)sizeof( WAVEHDR ) );
|
2009-06-25 07:16:22 -07:00
|
|
|
|
#if DEBUG
|
|
|
|
|
Console.WriteLine( "...done; ret2=" + ret2 );
|
|
|
|
|
#endif
|
|
|
|
|
s_wave_header[k].dwUser = new IntPtr( k );
|
|
|
|
|
}
|
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( " exit waveplay..ctor; s_hwave_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) );
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 波形データをバッファに追加する。バッファが再生中などの理由で即座に書き込めない場合、バッファが書き込み可能となるまで待機させられる
|
|
|
|
|
public void append( float** data, uint length, double amp_left, double amp_right ) {
|
|
|
|
|
append_cor( data, length, amp_left, amp_right, false );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void flush_and_exit( double amp_left, double amp_right ) {
|
|
|
|
|
append_cor( (float**)0, 0, amp_left, amp_right, true );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 再生中断を要求する
|
|
|
|
|
public void abort() {
|
|
|
|
|
s_abort_required = true;
|
|
|
|
|
reset();
|
|
|
|
|
for ( int k = 0; k < _NUM_BUF; k++ ) {
|
|
|
|
|
if ( s_ptr_wave[k] != IntPtr.Zero ) {
|
|
|
|
|
for ( int i = 0; i < s_block_size; i++ ) {
|
|
|
|
|
s_wave[k][i] = 0;
|
|
|
|
|
}
|
|
|
|
|
//memset( s_wave[k], 0, s_block_size * sizeof( ulong ) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
s_buffer_loc = 0;
|
|
|
|
|
s_current_buffer = 0;
|
|
|
|
|
s_processed_count = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 現在の再生位置を取得する。再生中でない場合負の値となる。
|
|
|
|
|
public float get_play_time() {
|
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "WavePlay.get_play_time" );
|
|
|
|
|
#endif
|
|
|
|
|
if ( s_playing ) {
|
|
|
|
|
MMTIME mmt = new MMTIME();
|
|
|
|
|
mmt.cb = (uint)sizeof( MMTIME );
|
2010-03-16 20:14:08 -07:00
|
|
|
|
mmt.wType = win32.TIME_MS;
|
|
|
|
|
uint ret = win32.waveOutGetPosition( s_hwave_out, ref mmt, (uint)sizeof( MMTIME ) );
|
2009-06-25 07:16:22 -07:00
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( " ret=" + ret );
|
|
|
|
|
#endif
|
|
|
|
|
float ms = 0.0f;
|
|
|
|
|
switch ( mmt.wType ) {
|
2010-03-16 20:14:08 -07:00
|
|
|
|
case win32.TIME_MS:
|
2009-06-25 07:16:22 -07:00
|
|
|
|
return mmt.ms * 0.001f;
|
2010-03-16 20:14:08 -07:00
|
|
|
|
case win32.TIME_SAMPLES:
|
2009-06-25 07:16:22 -07:00
|
|
|
|
return (float)mmt.sample / (float)s_wave_formatx.nSamplesPerSec;
|
2010-03-16 20:14:08 -07:00
|
|
|
|
case win32.TIME_BYTES:
|
2009-06-25 07:16:22 -07:00
|
|
|
|
return (float)mmt.cb / (float)s_wave_formatx.nAvgBytesPerSec;
|
|
|
|
|
default:
|
|
|
|
|
return -1.0f;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return -1.0f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// リセットする。abort関数でも呼び出される。
|
|
|
|
|
public void reset() {
|
|
|
|
|
s_playing = false;
|
|
|
|
|
if ( s_hwave_out.ToInt32() != 0 ) {
|
|
|
|
|
for ( int k = 0; k < _NUM_BUF; k++ ) {
|
|
|
|
|
s_wave_header[k].dwUser = new IntPtr( _NUM_BUF + k );
|
|
|
|
|
}
|
2010-03-16 20:14:08 -07:00
|
|
|
|
win32.waveOutReset( s_hwave_out );
|
2009-06-25 07:16:22 -07:00
|
|
|
|
uint zero = MAKELONG( 0, 0 );
|
|
|
|
|
for ( int k = 0; k < _NUM_BUF; k++ ) {
|
|
|
|
|
for ( int i = 0; i < s_block_size; i++ ) {
|
|
|
|
|
s_wave[k][i] = zero;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for ( int i = 0; i < s_num_wave_reader; i++ ) {
|
2010-03-16 20:14:08 -07:00
|
|
|
|
s_wave_reader[i].close();
|
2009-06-25 07:16:22 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 再生のための準備を行う。この関数を呼び出した後は、バッファが再生開始されるまでget_play_timeの戻り値は0となる(負値にならない)。
|
|
|
|
|
/// 戻り値は、filesに指定されたファイルの内、最も再生時間の長いwaveファイルの、合計サンプル数
|
|
|
|
|
public int on_your_mark( string[] files, long wave_read_offset_samples ) {
|
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "on_your_mark; s_hwave_out=0x" + Convert.ToString( s_hwave_out.ToInt32(), 16 ) );
|
|
|
|
|
#endif
|
|
|
|
|
int num_files = files.Length;
|
|
|
|
|
reset();
|
|
|
|
|
s_wave_read_offset_samples = wave_read_offset_samples;
|
|
|
|
|
for ( int k = 0; k < _NUM_BUF; k++ ) {
|
|
|
|
|
s_wave_header[k].dwUser = new IntPtr( k );
|
|
|
|
|
s_done[k] = true;
|
|
|
|
|
}
|
|
|
|
|
s_abort_required = false;
|
|
|
|
|
s_buffer_loc = 0;
|
|
|
|
|
s_current_buffer = 0;
|
|
|
|
|
s_processed_count = 0;
|
|
|
|
|
s_playing = true;
|
|
|
|
|
s_last_buffer = -1;
|
|
|
|
|
|
|
|
|
|
if ( (int)s_ptr_wave_buffer_l.ToPointer() == 0 ) {
|
|
|
|
|
s_ptr_wave_buffer_l = Marshal.AllocHGlobal( sizeof( float ) * s_block_size );// s_wave_buffer_l = new float[s_block_size];
|
|
|
|
|
s_wave_buffer_l = (float*)s_ptr_wave_buffer_l.ToPointer();
|
|
|
|
|
}
|
|
|
|
|
if ( (int)s_ptr_wave_buffer_r.ToPointer() == 0 ) {
|
|
|
|
|
s_ptr_wave_buffer_r = Marshal.AllocHGlobal( sizeof( float ) * s_block_size ); //s_wave_buffer_r = new float[s_block_size];
|
|
|
|
|
s_wave_buffer_r = (float*)s_ptr_wave_buffer_r.ToPointer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( s_wave_reader != null ) {
|
|
|
|
|
for ( int i = 0; i < s_num_wave_reader; i++ ) {
|
2010-03-16 20:14:08 -07:00
|
|
|
|
s_wave_reader[i].close();
|
2009-06-25 07:16:22 -07:00
|
|
|
|
}
|
|
|
|
|
//delete [] s_wave_reader;
|
|
|
|
|
}
|
|
|
|
|
s_wave_reader = new WaveReader[num_files];
|
|
|
|
|
|
|
|
|
|
if ( s_another_wave_l != null ) {
|
|
|
|
|
for ( int i = 0; i < s_num_wave_reader; i++ ) {
|
|
|
|
|
Marshal.FreeHGlobal( s_ptr_another_wave_l[i] );// delete [] s_another_wave_l[i];
|
|
|
|
|
}
|
|
|
|
|
//delete [] s_another_wave_l;
|
|
|
|
|
}
|
|
|
|
|
if ( s_another_wave_r != null ) {
|
|
|
|
|
for ( int i = 0; i < s_num_wave_reader; i++ ) {
|
|
|
|
|
Marshal.FreeHGlobal( s_ptr_another_wave_r[i] ); //delete [] s_another_wave_r[i];
|
|
|
|
|
}
|
|
|
|
|
//delete [] s_another_wave_r;
|
|
|
|
|
}
|
|
|
|
|
s_another_wave_l = new float*[num_files];
|
|
|
|
|
s_another_wave_r = new float*[num_files];
|
|
|
|
|
int max_samples = 0;
|
|
|
|
|
for ( int i = 0; i < num_files; i++ ) {
|
|
|
|
|
// waveファイルヘッダを読込む
|
|
|
|
|
/*int len = files[i].Length;
|
|
|
|
|
wchar_t *name = new wchar_t[len + 1];
|
|
|
|
|
array<wchar_t> ^buf = files[i]->ToCharArray();
|
|
|
|
|
for( int k = 0; k < len; k++ ){
|
|
|
|
|
name[k] = buf[k];
|
|
|
|
|
}
|
|
|
|
|
name[len] = '\0';*/
|
2010-03-16 20:14:08 -07:00
|
|
|
|
s_wave_reader[i].open( files[i] );
|
|
|
|
|
int samples = s_wave_reader[i].getTotalSamples();
|
2009-06-25 07:16:22 -07:00
|
|
|
|
if ( samples > max_samples ) {
|
|
|
|
|
max_samples = samples;
|
|
|
|
|
}
|
|
|
|
|
//delete [] name;
|
|
|
|
|
|
|
|
|
|
// バッファを用意
|
|
|
|
|
s_ptr_another_wave_l[i] = Marshal.AllocHGlobal( sizeof( float ) * s_block_size );
|
|
|
|
|
s_another_wave_l[i] = (float*)s_ptr_another_wave_l[i].ToPointer();
|
|
|
|
|
s_ptr_another_wave_r[i] = Marshal.AllocHGlobal( sizeof( float ) * s_block_size );
|
|
|
|
|
s_another_wave_r[i] = (float*)s_ptr_another_wave_r[i].ToPointer();
|
|
|
|
|
//s_another_wave_l[i] = new float[s_block_size];
|
|
|
|
|
//s_another_wave_r[i] = new float[s_block_size];
|
|
|
|
|
}
|
|
|
|
|
s_num_wave_reader = num_files;
|
|
|
|
|
return max_samples;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void set_error_samples( int error_samples ) {
|
|
|
|
|
s_error_samples = error_samples;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// コールバック関数を設定する
|
|
|
|
|
public void set_first_buffer_written_callback( FirstBufferWrittenCallback proc ) {
|
|
|
|
|
s_first_buffer_written_callback = proc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void terminate() {
|
|
|
|
|
if ( s_hwave_out.ToInt32() != 0 ) {
|
2010-03-16 20:14:08 -07:00
|
|
|
|
win32.waveOutReset( s_hwave_out );
|
2009-06-25 07:16:22 -07:00
|
|
|
|
#if DEBUG
|
|
|
|
|
bocoree.debug.push_log( "waveplay::terminate; waveOutReset" );
|
|
|
|
|
#endif
|
|
|
|
|
for ( int k = 0; k < _NUM_BUF; k++ ) {
|
2010-03-16 20:14:08 -07:00
|
|
|
|
win32.waveOutUnprepareHeader( s_hwave_out, ref s_wave_header[k], (uint)sizeof( WAVEHDR ) );
|
2009-06-25 07:16:22 -07:00
|
|
|
|
}
|
2010-03-16 20:14:08 -07:00
|
|
|
|
win32.waveOutClose( s_hwave_out );
|
2009-06-25 07:16:22 -07:00
|
|
|
|
}
|
|
|
|
|
for ( int i = 0; i < _NUM_BUF; i++ ) {
|
|
|
|
|
if ( s_ptr_wave[i].ToInt32() != 0 ) {
|
|
|
|
|
Marshal.FreeHGlobal( s_ptr_wave[i] ); //delete [] s_wave[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 現在再生中かどうかを取得する
|
|
|
|
|
public bool is_alive() {
|
|
|
|
|
return s_playing;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ブロックサイズを変更します
|
|
|
|
|
public bool change_block_size( int block_size ) {
|
|
|
|
|
if ( s_playing ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if ( block_size <= 0 ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for ( int k = 0; k < _NUM_BUF; k++ ) {
|
|
|
|
|
if ( s_ptr_wave[k].ToInt32() != 0 ) {
|
|
|
|
|
Marshal.FreeHGlobal( s_ptr_wave[k] );// delete [] s_wave[k];
|
|
|
|
|
}
|
|
|
|
|
s_ptr_wave[k] = Marshal.AllocHGlobal( sizeof( uint ) * block_size );
|
|
|
|
|
s_wave[k] = (uint*)s_ptr_wave[k].ToPointer();// calloc( sizeof( ulong ), block_size );
|
|
|
|
|
s_wave_header[k].lpData = s_ptr_wave[k];
|
|
|
|
|
s_wave_header[k].dwBufferLength = (uint)(sizeof( uint ) * block_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// s_wave_buffer_l, s_wave_buffer_rは、NULLならばon_your_markで初期化されるので、開放だけやっておけばOK
|
|
|
|
|
if ( s_ptr_wave_buffer_l.ToInt32() != 0 ) {
|
|
|
|
|
Marshal.FreeHGlobal( s_ptr_wave_buffer_l ); //delete [] s_wave_buffer_l;
|
|
|
|
|
}
|
|
|
|
|
if ( s_ptr_wave_buffer_r.ToInt32() != 0 ) {
|
|
|
|
|
Marshal.FreeHGlobal( s_ptr_wave_buffer_r ); //delete[] s_wave_buffer_r;
|
|
|
|
|
}
|
|
|
|
|
// s_another_wave_l, s_another_wave_rは、on_your_markで全自動で初期化されるので特に操作の必要なし
|
|
|
|
|
s_block_size = block_size;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint MAKELONG( ushort a, ushort b ) {
|
|
|
|
|
return (uint)(a & 0xffff) | (uint)((b & 0xffff) << 16);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|