public int startRendering( long total_samples, boolean mode_infinite, int sample_rate, IWaveIncoming runner, WorkerState state )
{
#if DEBUG
sout.println( "VocaloidDriver#startRendering; entry; total_samples=" + total_samples + "; sample_rate=" + sample_rate );
#endif
lock ( locker ) {
rendering = true;
//g_cancelRequired = false;
g_progress = 0.0;
sampleRate = sample_rate;
Vector<MidiEvent> lpEvents = merge_events( s_track_events.get( 0 ), s_track_events.get( 1 ) );
int current_count = -1;
MidiEvent current = new MidiEvent();// = lpEvents;
MemoryManager mman = null;
float* left_ch;
float* right_ch;
float** out_buffer;
try {
mman = new MemoryManager();
left_ch = (float*)mman.malloc( sizeof( float ) * sampleRate ).ToPointer();
right_ch = (float*)mman.malloc( sizeof( float ) * sampleRate ).ToPointer();
out_buffer = (float**)mman.malloc( sizeof( float* ) * 2 ).ToPointer();
out_buffer[0] = left_ch;
out_buffer[1] = right_ch;
double[] buffer_l = new double[sampleRate];
double[] buffer_r = new double[sampleRate];
#if TEST
org.kbinani.debug.push_log( " calling initial dispatch..." );
#endif
#if DEBUG
sout.println( "VocaloidDriver#startRendering; sampleRate=" + sampleRate );
#endif
aEffect.Dispatch( AEffectOpcodes.effSetSampleRate, 0, 0, IntPtr.Zero, (float)sampleRate );
aEffect.Dispatch( AEffectOpcodes.effMainsChanged, 0, 1, IntPtr.Zero, 0 );
// ここではブロックサイズ=サンプリングレートということにする
aEffect.Dispatch( AEffectOpcodes.effSetBlockSize, 0, sampleRate, IntPtr.Zero, 0 );
// レンダリングの途中で停止した場合,ここでProcessする部分が無音でない場合がある
for ( int i = 0; i < 3; i++ ) {
aEffect.ProcessReplacing( IntPtr.Zero, new IntPtr( out_buffer ), sampleRate );
}
#if TEST
org.kbinani.debug.push_log( " ...done" );
#endif
int delay = 0;
int duration = 0;
int dwNow = 0;
int dwPrev = 0;
int dwDelta;
int dwDelay = 0;
int dwDeltaDelay = 0;
int addr_msb = 0, addr_lsb = 0;
int data_msb = 0, data_lsb = 0;
int total_processed = 0;
int total_processed2 = 0;
#if TEST
org.kbinani.debug.push_log( " getting dwDelay..." );
#endif
dwDelay = 0;
Vector<MidiEvent> list = s_track_events.get( 1 );
int list_size = list.size();
for ( int i = 0; i < list_size; i++ ) {
MidiEvent work = list.get( i );
if ( (work.firstByte & 0xf0) == 0xb0 ) {
switch ( work.data[0] ) {
case 0x63:
addr_msb = work.data[1];
addr_lsb = 0;
break;
case 0x62:
addr_lsb = work.data[1];
break;
case 0x06:
data_msb = work.data[1];
break;
case 0x26:
data_lsb = work.data[1];
if ( addr_msb == 0x50 && addr_lsb == 0x01 ) {
dwDelay = (data_msb & 0xff) << 7 | (data_lsb & 0x7f);
}
break;
}
}
if ( dwDelay > 0 ) {
break;
}
}
#if TEST
org.kbinani.debug.push_log( " ...done; dwDelay=" + dwDelay );
#endif
while ( !state.isCancelRequested() ) {
int process_event_count = current_count;
int nEvents = 0;
#if TEST
org.kbinani.debug.push_log( "lpEvents.Count=" + lpEvents.size() );
#endif
if ( current_count < 0 ) {
current_count = 0;
current = lpEvents.get( current_count );
process_event_count = current_count;
}
while ( current.clock == dwNow ) {
// durationを取得
if ( (current.firstByte & 0xf0) == 0xb0 ) {
switch ( current.data[0] ) {
case 0x63:
addr_msb = current.data[1];
addr_lsb = 0;
break;
case 0x62:
addr_lsb = current.data[1];
break;
case 0x06:
data_msb = current.data[1];
break;
case 0x26:
data_lsb = current.data[1];
// Note Duration in millisec
if ( addr_msb == 0x50 && addr_lsb == 0x4 ) {
duration = data_msb << 7 | data_lsb;
}
break;
}
}
nEvents++;
if ( current_count + 1 < lpEvents.size() ) {
current_count++;
current = lpEvents.get( current_count );
} else {
break;
}
}
if ( current_count + 1 >= lpEvents.size() ) {
break;
}
double msNow = msec_from_clock( dwNow );
dwDelta = (int)(msNow / 1000.0 * sampleRate) - total_processed;
#if TEST
org.kbinani.debug.push_log( "dwNow=" + dwNow );
org.kbinani.debug.push_log( "dwPrev=" + dwPrev );
org.kbinani.debug.push_log( "dwDelta=" + dwDelta );
#endif
VstEvents* pVSTEvents = (VstEvents*)mman.malloc( sizeof( VstEvent ) + nEvents * sizeof( VstEvent* ) ).ToPointer();
pVSTEvents->numEvents = 0;
pVSTEvents->reserved = (VstIntPtr)0;
for ( int i = 0; i < nEvents; i++ ) {
MidiEvent pProcessEvent = lpEvents.get( process_event_count );
int event_code = pProcessEvent.firstByte;
VstEvent* pVSTEvent = (VstEvent*)0;
VstMidiEvent* pMidiEvent;
switch ( event_code ) {
case 0xf0:
case 0xf7:
case 0xff:
break;
default:
pMidiEvent = (VstMidiEvent*)mman.malloc( (int)(sizeof( VstMidiEvent ) + (pProcessEvent.data.Length + 1) * sizeof( byte )) ).ToPointer();
pMidiEvent->byteSize = sizeof( VstMidiEvent );
pMidiEvent->deltaFrames = dwDelta;
pMidiEvent->detune = 0;
pMidiEvent->flags = 1;
pMidiEvent->noteLength = 0;
pMidiEvent->noteOffset = 0;
pMidiEvent->noteOffVelocity = 0;
pMidiEvent->reserved1 = 0;
pMidiEvent->reserved2 = 0;
pMidiEvent->type = VstEventTypes.kVstMidiType;
pMidiEvent->midiData[0] = (byte)(0xff & pProcessEvent.firstByte);
for ( int j = 0; j < pProcessEvent.data.Length; j++ ) {
pMidiEvent->midiData[j + 1] = (byte)(0xff & pProcessEvent.data[j]);
}
pVSTEvents->events[pVSTEvents->numEvents++] = (int)(VstEvent*)pMidiEvent;
break;
}
process_event_count++;
//pProcessEvent = lpEvents[process_event_count];
}
#if TEST
org.kbinani.debug.push_log( "calling Dispatch with effProcessEvents..." );
#endif
aEffect.Dispatch( AEffectXOpcodes.effProcessEvents, 0, 0, new IntPtr( pVSTEvents ), 0 );
#if TEST
org.kbinani.debug.push_log( "...done" );
#endif
while ( dwDelta > 0 && !state.isCancelRequested() ) {
int dwFrames = dwDelta > sampleRate ? sampleRate : dwDelta;
#if TEST
org.kbinani.debug.push_log( "calling ProcessReplacing..." );
#endif
aEffect.ProcessReplacing( IntPtr.Zero, new IntPtr( out_buffer ), dwFrames );
#if TEST
org.kbinani.debug.push_log( "...done" );
#endif
int iOffset = dwDelay - dwDeltaDelay;
if ( iOffset > (int)dwFrames ) {
iOffset = (int)dwFrames;
}
if ( iOffset == 0 ) {
for ( int i = 0; i < (int)dwFrames; i++ ) {
buffer_l[i] = out_buffer[0][i];
buffer_r[i] = out_buffer[1][i];
}
total_processed2 += dwFrames;
runner.waveIncomingImpl( buffer_l, buffer_r, dwFrames, state );
} else {
dwDeltaDelay += iOffset;
}
dwDelta -= dwFrames;
total_processed += dwFrames;
}
dwPrev = dwNow;
dwNow = (int)current.clock;
g_progress = total_processed / (double)total_samples * 100.0;
}
double msLast = msec_from_clock( dwNow );
dwDelta = (int)(sampleRate * ((double)duration + (double)delay) / 1000.0 + dwDeltaDelay);
if ( total_samples - total_processed2 > dwDelta ) {
dwDelta = (int)total_samples - total_processed2;
}
while ( dwDelta > 0 && !state.isCancelRequested() ) {
int dwFrames = dwDelta > sampleRate ? sampleRate : dwDelta;
#if TEST
org.kbinani.debug.push_log( "calling ProcessReplacing..." );
#endif
aEffect.ProcessReplacing( IntPtr.Zero, new IntPtr( out_buffer ), dwFrames );
#if TEST
org.kbinani.debug.push_log( "...done" );
#endif
for ( int i = 0; i < (int)dwFrames; i++ ) {
buffer_l[i] = out_buffer[0][i];
buffer_r[i] = out_buffer[1][i];
}
total_processed2 += dwFrames;
runner.waveIncomingImpl( buffer_l, buffer_r, dwFrames, state );
dwDelta -= dwFrames;
total_processed += dwFrames;
}
#if TEST
sout.println( "vstidrv::StartRendering; total_processed=" + total_processed );
#endif
if ( mode_infinite ) {
for ( int i = 0; i < sampleRate; i++ ) {
buffer_l[i] = 0.0;
buffer_r[i] = 0.0;
}
while ( !state.isCancelRequested() ) {
total_processed2 += sampleRate;
runner.waveIncomingImpl( buffer_l, buffer_r, sampleRate, state );
}
}
aEffect.Dispatch( AEffectOpcodes.effMainsChanged, 0, 0, IntPtr.Zero, 0 );
lpEvents.clear();
#if DEBUG
sout.println( "VocaloidDriver#startRendering; done; total_processed=" + total_processed + "; total_processed2=" + total_processed2 );
#endif
} catch ( Exception ex ) {
serr.println( "VocaloidDriver#startRendering; ex=" + ex );
} finally {
if ( mman != null ) {
try {
mman.dispose();
} catch ( Exception ex2 ) {
serr.println( "VocaloidDriver#startRendering; ex2=" + ex2 );
}
}
}
rendering = false;
g_saProcessed = 0;
for ( int i = 0; i < s_track_events.size(); i++ ) {
s_track_events.get( i ).clear();
}
g_tempoList.clear();
//g_cancelRequired = false;
}
return 1;
}