private void doSynthesis(long to_sample, double[] left, double[] right, WorkerState state) { int buffer_length = left.Length; long remain = to_sample - position_; while (0 < remain && !state.isCancelRequested()) { int process = buffer_length < remain ? buffer_length : (int)remain; Array.Clear(left, 0, process); Array.Clear(right, 0, process); driver_.process(left, right, process); if (receiver_ != null) { receiver_.push(left, right, process); } remain -= process; position_ += process; state.reportProgress(position_); } }
public void testMethod(WorkerState receiver, Object argument) { int wait_ms = 10; int amount = ((int)argument) / wait_ms; int proc = 0; for (int i = 0; i < amount; i++) { // 中止処理後にreportCompleteを送ってはいけない if (receiver.isCancelRequested()) { sout.println("TestFormWorker#testMethod; cancel requested"); return; } receiver.reportProgress(proc); Thread.Sleep(wait_ms); proc += wait_ms; } receiver.reportComplete(); sout.println("TestFormWorker#testMethod; job done"); }
public void begin(long total_samples, WorkerState state) { var mDriver = getDriver(); #if DEBUG sout.println("AquesToneRenderingRunner#begin; (mDriver==null)=" + (mDriver == null)); String file = System.IO.Path.Combine(System.Windows.Forms.Application.StartupPath, "AquesToneWaveGenerator.txt"); log = new System.IO.StreamWriter(file); log.AutoFlush = true; #endif if (mDriver == null) { #if DEBUG log.WriteLine("mDriver==null"); log.Close(); #endif exitBegin(); state.reportComplete(); return; } #if DEBUG sout.println("AquesToneRenderingRunner#begin; mDriver.loaded=" + mDriver.loaded); #endif if (!mDriver.loaded) { #if DEBUG log.WriteLine("mDriver.loaded=" + mDriver.loaded); log.Close(); #endif exitBegin(); state.reportComplete(); return; } mRunning = true; //mAbortRequired = false; mTotalSamples = total_samples; #if DEBUG sout.println("AquesToneWaveGenerator#begin; mTotalSamples=" + mTotalSamples); log.WriteLine("mTotalSamples=" + mTotalSamples); log.WriteLine("mTrimRemain=" + mTrimRemain); #endif VsqTrack track = mVsq.Track.get(mTrack); int BUFLEN = mSampleRate / 10; double[] left = new double[BUFLEN]; double[] right = new double[BUFLEN]; long saProcessed = 0; int saRemain = 0; int lastClock = 0; // 最後に処理されたゲートタイム // 最初にダミーの音を鳴らす // (最初に入るノイズを回避するためと、前回途中で再生停止した場合に無音から始まるようにするため) mDriver.resetAllParameters(); mDriver.process(left, right, BUFLEN); MidiEvent f_noteon = new MidiEvent(); f_noteon.firstByte = 0x90; f_noteon.data = new int[] { 0x40, 0x40 }; f_noteon.clock = 0; mDriver.send(new MidiEvent[] { f_noteon }); mDriver.process(left, right, BUFLEN); MidiEvent f_noteoff = new MidiEvent(); f_noteoff.firstByte = 0x80; f_noteoff.data = new int[] { 0x40, 0x7F }; mDriver.send(new MidiEvent[] { f_noteoff }); for (int i = 0; i < 3; i++) { mDriver.process(left, right, BUFLEN); } #if DEBUG log.WriteLine("pre-process done"); log.WriteLine("-----------------------------------------------------"); VsqTrack vsq_track = mVsq.Track.get(mTrack); for (Iterator <VsqEvent> itr = vsq_track.getNoteEventIterator(); itr.hasNext();) { VsqEvent item = itr.next(); log.WriteLine("c" + item.Clock + "; " + item.ID.LyricHandle.L0.Phrase); } #endif // レンダリング開始位置での、パラメータの値をセットしておく for (Iterator <VsqEvent> itr = track.getNoteEventIterator(); itr.hasNext();) { VsqEvent item = itr.next(); #if DEBUG sout.println("AquesToneWaveGenerator#begin; item.Clock=" + item.Clock); log.WriteLine("*********************************************************"); log.WriteLine("item.Clock=" + item.Clock); #endif long saNoteStart = (long)(mVsq.getSecFromClock(item.Clock) * mSampleRate); long saNoteEnd = (long)(mVsq.getSecFromClock(item.Clock + item.ID.getLength()) * mSampleRate); #if DEBUG log.WriteLine("saNoteStart=" + saNoteStart + "; saNoteEnd=" + saNoteEnd); #endif EventQueueSequence list = generateMidiEvent(mVsq, mTrack, lastClock, item.Clock + item.ID.getLength()); lastClock = item.Clock + item.ID.Length + 1; for (Iterator <Integer> itr2 = list.keyIterator(); itr2.hasNext();) { // まず直前までの分を合成 Integer clock = itr2.next(); #if DEBUG log.WriteLine("-------------------------------------------------------"); sout.println("AquesToneWaveGenerator#begin; clock=" + clock); #endif long saStart = (long)(mVsq.getSecFromClock(clock) * mSampleRate); saRemain = (int)(saStart - saProcessed); #if DEBUG log.WriteLine("saStart=" + saStart); log.WriteLine("saRemain=" + saRemain); #endif while (saRemain > 0) { if (state.isCancelRequested()) { goto heaven; } int len = saRemain > BUFLEN ? BUFLEN : saRemain; mDriver.process(left, right, len); waveIncoming(left, right, len); saRemain -= len; saProcessed += len; state.reportProgress(saProcessed); //mTotalAppend += len; <- waveIncomingで計算されるので } // MIDiイベントを送信 MidiEventQueue queue = list.get(clock); // まずnoteoff boolean noteoff_send = false; if (queue.noteoff.size() > 0) { #if DEBUG for (int i = 0; i < queue.noteoff.size(); i++) { String str = ""; MidiEvent itemi = queue.noteoff.get(i); str += "0x" + PortUtil.toHexString(itemi.firstByte, 2) + " "; for (int j = 0; j < itemi.data.Length; j++) { str += "0x" + PortUtil.toHexString(itemi.data[j], 2) + " "; } sout.println(typeof(AquesToneWaveGenerator) + "#begin; noteoff; " + str); } #endif mDriver.send(queue.noteoff.toArray(new MidiEvent[] { })); noteoff_send = true; } // parameterの変更 if (queue.param.size() > 0) { for (Iterator <ParameterEvent> itr3 = queue.param.iterator(); itr3.hasNext();) { ParameterEvent pe = itr3.next(); #if DEBUG sout.println(typeof(AquesToneWaveGenerator) + "#begin; param; index=" + pe.index + "; value=" + pe.value); #endif mDriver.setParameter(pe.index, pe.value); } } // ついでnoteon if (queue.noteon.size() > 0) { // 同ゲートタイムにピッチベンドも指定されている場合、同時に送信しないと反映されないようだ! if (queue.pit.size() > 0) { queue.noteon.addAll(queue.pit); queue.pit.clear(); } #if DEBUG for (int i = 0; i < queue.noteon.size(); i++) { String str = ""; MidiEvent itemi = queue.noteon.get(i); str += "0x" + PortUtil.toHexString(itemi.firstByte, 2) + " "; for (int j = 0; j < itemi.data.Length; j++) { str += "0x" + PortUtil.toHexString(itemi.data[j], 2) + " "; } sout.println(typeof(AquesToneWaveGenerator) + "#begin; noteon; " + str); } #endif mDriver.send(queue.noteon.toArray(new MidiEvent[] { })); } // PIT if (queue.pit.size() > 0 && !noteoff_send) { #if DEBUG for (int i = 0; i < queue.pit.size(); i++) { String str = ""; MidiEvent itemi = queue.pit.get(i); str += "0x" + PortUtil.toHexString(itemi.firstByte, 2) + " "; for (int j = 0; j < itemi.data.Length; j++) { str += "0x" + PortUtil.toHexString(itemi.data[j], 2) + " "; } sout.println(typeof(AquesToneWaveGenerator) + "#begin; pit; " + str); } #endif mDriver.send(queue.pit.toArray(new MidiEvent[] { })); } if (mDriver.getUi(mMainWindow) != null) { mDriver.getUi(mMainWindow).invalidateUi(); } } } // totalSamplesに足りなかったら、追加してレンダリング saRemain = (int)(mTotalSamples - mTotalAppend); #if DEBUG sout.println("AquesToneRenderingRunner#run; totalSamples=" + mTotalSamples + "; mTotalAppend=" + mTotalAppend + "; saRemain=" + saRemain); #endif while (saRemain > 0) { if (state.isCancelRequested()) { goto heaven; } int len = saRemain > BUFLEN ? BUFLEN : saRemain; mDriver.process(left, right, len); waveIncoming(left, right, len); saRemain -= len; saProcessed += len; state.reportProgress(saProcessed); //mTotalAppend += len; } heaven: #if DEBUG log.Close(); #endif exitBegin(); state.reportComplete(); }
public void begin( long total_samples, WorkerState state ) { var mDriver = getDriver(); #if DEBUG sout.println( "AquesToneRenderingRunner#begin; (mDriver==null)=" + (mDriver == null) ); String file = System.IO.Path.Combine( System.Windows.Forms.Application.StartupPath, "AquesToneWaveGenerator.txt" ); log = new System.IO.StreamWriter( file ); log.AutoFlush = true; #endif if ( mDriver == null ) { #if DEBUG log.WriteLine( "mDriver==null" ); log.Close(); #endif exitBegin(); state.reportComplete(); return; } #if DEBUG sout.println( "AquesToneRenderingRunner#begin; mDriver.loaded=" + mDriver.loaded ); #endif if ( !mDriver.loaded ) { #if DEBUG log.WriteLine( "mDriver.loaded=" + mDriver.loaded ); log.Close(); #endif exitBegin(); state.reportComplete(); return; } mRunning = true; //mAbortRequired = false; mTotalSamples = total_samples; #if DEBUG sout.println( "AquesToneWaveGenerator#begin; mTotalSamples=" + mTotalSamples ); log.WriteLine( "mTotalSamples=" + mTotalSamples ); log.WriteLine( "mTrimRemain=" + mTrimRemain ); #endif VsqTrack track = mVsq.Track.get( mTrack ); int BUFLEN = mSampleRate / 10; double[] left = new double[BUFLEN]; double[] right = new double[BUFLEN]; long saProcessed = 0; int saRemain = 0; int lastClock = 0; // 最後に処理されたゲートタイム // 最初にダミーの音を鳴らす // (最初に入るノイズを回避するためと、前回途中で再生停止した場合に無音から始まるようにするため) mDriver.resetAllParameters(); mDriver.process( left, right, BUFLEN ); MidiEvent f_noteon = new MidiEvent(); f_noteon.firstByte = 0x90; f_noteon.data = new int[] { 0x40, 0x40 }; f_noteon.clock = 0; mDriver.send( new MidiEvent[] { f_noteon } ); mDriver.process( left, right, BUFLEN ); MidiEvent f_noteoff = new MidiEvent(); f_noteoff.firstByte = 0x80; f_noteoff.data = new int[] { 0x40, 0x7F }; mDriver.send( new MidiEvent[] { f_noteoff } ); for ( int i = 0; i < 3; i++ ) { mDriver.process( left, right, BUFLEN ); } #if DEBUG log.WriteLine( "pre-process done" ); log.WriteLine( "-----------------------------------------------------" ); VsqTrack vsq_track = mVsq.Track.get( mTrack ); for ( Iterator<VsqEvent> itr = vsq_track.getNoteEventIterator(); itr.hasNext(); ) { VsqEvent item = itr.next(); log.WriteLine( "c" + item.Clock + "; " + item.ID.LyricHandle.L0.Phrase ); } #endif // レンダリング開始位置での、パラメータの値をセットしておく for ( Iterator<VsqEvent> itr = track.getNoteEventIterator(); itr.hasNext(); ) { VsqEvent item = itr.next(); #if DEBUG sout.println( "AquesToneWaveGenerator#begin; item.Clock=" + item.Clock ); log.WriteLine( "*********************************************************" ); log.WriteLine( "item.Clock=" + item.Clock ); #endif long saNoteStart = (long)(mVsq.getSecFromClock( item.Clock ) * mSampleRate); long saNoteEnd = (long)(mVsq.getSecFromClock( item.Clock + item.ID.getLength() ) * mSampleRate); #if DEBUG log.WriteLine( "saNoteStart=" + saNoteStart + "; saNoteEnd=" + saNoteEnd ); #endif EventQueueSequence list = generateMidiEvent( mVsq, mTrack, lastClock, item.Clock + item.ID.getLength() ); lastClock = item.Clock + item.ID.Length + 1; for ( Iterator<Integer> itr2 = list.keyIterator(); itr2.hasNext(); ) { // まず直前までの分を合成 Integer clock = itr2.next(); #if DEBUG log.WriteLine( "-------------------------------------------------------" ); sout.println( "AquesToneWaveGenerator#begin; clock=" + clock ); #endif long saStart = (long)(mVsq.getSecFromClock( clock ) * mSampleRate); saRemain = (int)(saStart - saProcessed); #if DEBUG log.WriteLine( "saStart=" + saStart ); log.WriteLine( "saRemain=" + saRemain ); #endif while ( saRemain > 0 ) { if ( state.isCancelRequested() ) { goto heaven; } int len = saRemain > BUFLEN ? BUFLEN : saRemain; mDriver.process( left, right, len ); waveIncoming( left, right, len ); saRemain -= len; saProcessed += len; state.reportProgress( saProcessed ); //mTotalAppend += len; <- waveIncomingで計算されるので } // MIDiイベントを送信 MidiEventQueue queue = list.get( clock ); // まずnoteoff boolean noteoff_send = false; if ( queue.noteoff.size() > 0 ) { #if DEBUG for ( int i = 0; i < queue.noteoff.size(); i++ ) { String str = ""; MidiEvent itemi = queue.noteoff.get( i ); str += "0x" + PortUtil.toHexString( itemi.firstByte, 2 ) + " "; for ( int j = 0; j < itemi.data.Length; j++ ) { str += "0x" + PortUtil.toHexString( itemi.data[j], 2 ) + " "; } sout.println( typeof( AquesToneWaveGenerator ) + "#begin; noteoff; " + str ); } #endif mDriver.send( queue.noteoff.toArray( new MidiEvent[] { } ) ); noteoff_send = true; } // parameterの変更 if ( queue.param.size() > 0 ) { for ( Iterator<ParameterEvent> itr3 = queue.param.iterator(); itr3.hasNext(); ) { ParameterEvent pe = itr3.next(); #if DEBUG sout.println( typeof( AquesToneWaveGenerator ) + "#begin; param; index=" + pe.index + "; value=" + pe.value ); #endif mDriver.setParameter( pe.index, pe.value ); } } // ついでnoteon if ( queue.noteon.size() > 0 ) { // 同ゲートタイムにピッチベンドも指定されている場合、同時に送信しないと反映されないようだ! if ( queue.pit.size() > 0 ) { queue.noteon.addAll( queue.pit ); queue.pit.clear(); } #if DEBUG for ( int i = 0; i < queue.noteon.size(); i++ ) { String str = ""; MidiEvent itemi = queue.noteon.get( i ); str += "0x" + PortUtil.toHexString( itemi.firstByte, 2 ) + " "; for ( int j = 0; j < itemi.data.Length; j++ ) { str += "0x" + PortUtil.toHexString( itemi.data[j], 2 ) + " "; } sout.println( typeof( AquesToneWaveGenerator ) + "#begin; noteon; " + str ); } #endif mDriver.send( queue.noteon.toArray( new MidiEvent[] { } ) ); } // PIT if ( queue.pit.size() > 0 && !noteoff_send ) { #if DEBUG for ( int i = 0; i < queue.pit.size(); i++ ) { String str = ""; MidiEvent itemi = queue.pit.get( i ); str += "0x" + PortUtil.toHexString( itemi.firstByte, 2 ) + " "; for ( int j = 0; j < itemi.data.Length; j++ ) { str += "0x" + PortUtil.toHexString( itemi.data[j], 2 ) + " "; } sout.println( typeof( AquesToneWaveGenerator ) + "#begin; pit; " + str ); } #endif mDriver.send( queue.pit.toArray( new MidiEvent[] { } ) ); } if ( mDriver.getUi( mMainWindow ) != null ) { mDriver.getUi( mMainWindow ).invalidateUi(); } } } // totalSamplesに足りなかったら、追加してレンダリング saRemain = (int)(mTotalSamples - mTotalAppend); #if DEBUG sout.println( "AquesToneRenderingRunner#run; totalSamples=" + mTotalSamples + "; mTotalAppend=" + mTotalAppend + "; saRemain=" + saRemain ); #endif while ( saRemain > 0 ) { if ( state.isCancelRequested() ) { goto heaven; } int len = saRemain > BUFLEN ? BUFLEN : saRemain; mDriver.process( left, right, len ); waveIncoming( left, right, len ); saRemain -= len; saProcessed += len; state.reportProgress( saProcessed ); //mTotalAppend += len; } heaven: #if DEBUG log.Close(); #endif exitBegin(); state.reportComplete(); }
private void doSynthesis( long to_sample, double[] left, double[] right, WorkerState state ) { int buffer_length = left.Length; long remain = to_sample - position_; while ( 0 < remain && !state.isCancelRequested() ) { int process = buffer_length < remain ? buffer_length : (int)remain; Array.Clear( left, 0, process ); Array.Clear( right, 0, process ); driver_.process( left, right, process ); if ( receiver_ != null ) { receiver_.push( left, right, process ); } remain -= process; position_ += process; state.reportProgress( position_ ); } }
public void patchWork(WorkerState state, Object arg) { #if DEBUG sout.println("SynthesizeWorker#patchWork"); #endif VsqFileEx vsq = AppManager.getVsqFile(); Object[] args = (Object[])arg; List <PatchWorkQueue> queue = (List <PatchWorkQueue>)args[0]; List <int> tracks = (List <int>)args[1]; int finished = queue.Count; string temppath = AppManager.getTempWaveDir(); for (int k = 0; k < tracks.Count; k++) { int track = tracks[k]; string wavePath = Path.Combine(temppath, track + ".wav"); List <int> queueIndex = new List <int>(); for (int i = 0; i < queue.Count; i++) { if (queue[i].track == track) { queueIndex.Add(i); } } if (queueIndex.Count <= 0) { // 第trackトラックに対してパッチワークを行う必要無し continue; } #if DEBUG sout.println("AppManager#pathWorkToFreeze; wavePath=" + wavePath + "; queue.get( queueIndex.get( 0 ) ).file=" + queue[queueIndex[0]].file); sout.println("AppManager#pathWorkToFreeze; queueIndex.size()=" + queueIndex.Count); #endif if (queueIndex.Count == 1 && wavePath.Equals(queue[queueIndex[0]].file)) { // 第trackトラック全体の合成を指示するキューだった場合. // このとき,パッチワークを行う必要なし. AppManager.mLastRenderedStatus[track - 1] = new RenderedStatus((VsqTrack)vsq.Track[track].clone(), vsq.TempoTable, (SequenceConfig)vsq.config.clone()); AppManager.serializeRenderingStatus(temppath, track); AppManager.invokeWaveViewReloadRequiredEvent(track, wavePath, 1, -1); continue; } WaveWriter writer = null; try { int sampleRate = vsq.config.SamplingRate; long totalLength = (long)((vsq.getSecFromClock(vsq.TotalClocks) + 1.0) * sampleRate); writer = new WaveWriter(wavePath, vsq.config.WaveFileOutputChannel, 16, sampleRate); int BUFLEN = 1024; double[] bufl = new double[BUFLEN]; double[] bufr = new double[BUFLEN]; double total = 0.0; for (int m = 0; m < queueIndex.Count; m++) { int i = queueIndex[m]; if (finished <= i) { break; } // パッチワークの開始秒時 double secStart = vsq.getSecFromClock(queue[i].clockStart); long sampleStart = (long)(secStart * sampleRate); // パッチワークの終了秒時 int clockEnd = queue[i].clockEnd; if (clockEnd == int.MaxValue) { clockEnd = vsq.TotalClocks + 240; } double secEnd = vsq.getSecFromClock(clockEnd); long sampleEnd = (long)(secEnd * sampleRate); WaveReader wr = null; try { wr = new WaveReader(queue[i].file); long remain2 = sampleEnd - sampleStart; long proc = 0; while (remain2 > 0) { int delta = remain2 > BUFLEN ? BUFLEN : (int)remain2; wr.read(proc, delta, bufl, bufr); writer.replace(sampleStart + proc, delta, bufl, bufr); proc += delta; remain2 -= delta; total += delta; state.reportProgress(total); } } catch (Exception ex) { Logger.write(typeof(AppManager) + ".patchWorkToFreeze; ex=" + ex + "\n"); serr.println("AppManager#patchWorkToFreeze; ex=" + ex); } finally { if (wr != null) { try { wr.close(); } catch (Exception ex2) { Logger.write(typeof(AppManager) + ".patchWorkToFreeze; ex=" + ex2 + "\n"); serr.println("AppManager#patchWorkToFreeze; ex2=" + ex2); } } } try { PortUtil.deleteFile(queue[i].file); } catch (Exception ex) { Logger.write(typeof(AppManager) + ".patchWorkToFreeze; ex=" + ex + "\n"); serr.println("AppManager#patchWorkToFreeze; ex=" + ex); } } VsqTrack vsq_track = vsq.Track[track]; if (queueIndex[queueIndex.Count - 1] <= finished) { // 途中で終了せず,このトラックの全てのパッチワークが完了した. AppManager.mLastRenderedStatus[track - 1] = new RenderedStatus((VsqTrack)vsq_track.clone(), vsq.TempoTable, (SequenceConfig)vsq.config.clone()); AppManager.serializeRenderingStatus(temppath, track); AppManager.setRenderRequired(track, false); } else { // パッチワークの作成途中で,キャンセルされた // キャンセルされたやつ以降の範囲に、プログラムチェンジ17の歌手変更イベントを挿入する。→AppManager#detectTrackDifferenceに必ず検出してもらえる。 VsqTrack copied = (VsqTrack)vsq_track.clone(); VsqEvent dumy = new VsqEvent(); dumy.ID.type = VsqIDType.Singer; dumy.ID.IconHandle = new IconHandle(); dumy.ID.IconHandle.Program = 17; for (int m = 0; m < queueIndex.Count; m++) { int i = queueIndex[m]; if (i < finished) { continue; } int start = queue[i].clockStart; int end = queue[i].clockEnd; VsqEvent singerAtEnd = vsq_track.getSingerEventAt(end); // startの位置に歌手変更が既に指定されていないかどうかを検査 int foundStart = -1; int foundEnd = -1; for (Iterator <int> itr = copied.indexIterator(IndexIteratorKind.SINGER); itr.hasNext();) { int j = itr.next(); VsqEvent ve = copied.getEvent(j); if (ve.Clock == start) { foundStart = j; } if (ve.Clock == end) { foundEnd = j; } if (end < ve.Clock) { break; } } VsqEvent dumyStart = (VsqEvent)dumy.clone(); dumyStart.Clock = start; if (foundStart >= 0) { copied.setEvent(foundStart, dumyStart); } else { copied.addEvent(dumyStart); } if (end != int.MaxValue) { VsqEvent dumyEnd = (VsqEvent)singerAtEnd.clone(); dumyEnd.Clock = end; if (foundEnd >= 0) { copied.setEvent(foundEnd, dumyEnd); } else { copied.addEvent(dumyEnd); } } copied.sortEvent(); } AppManager.mLastRenderedStatus[track - 1] = new RenderedStatus(copied, vsq.TempoTable, (SequenceConfig)vsq.config.clone()); AppManager.serializeRenderingStatus(temppath, track); } state.reportComplete(); } catch (Exception ex) { Logger.write(typeof(AppManager) + ".patchWorkToFreeze; ex=" + ex + "\n"); serr.println("AppManager#patchWorkToFreeze; ex=" + ex); } finally { if (writer != null) { try { writer.close(); } catch (Exception ex2) { Logger.write(typeof(AppManager) + ".patchWorkToFreeze; ex=" + ex2 + "\n"); serr.println("AppManager#patchWorkToFreeze; ex2=" + ex2); } } } // 波形表示用のWaveDrawContextの内容を更新する。 /*for ( int j = 0; j < queueIndex.size(); j++ ) { * int i = queueIndex.get( j ); * if ( i >= finished ) { * continue; * } * double secStart = mVsq.getSecFromClock( queue.get( i ).clockStart ); * int clockEnd = queue.get( i ).clockEnd; * if ( clockEnd == int.MaxValue ) { * clockEnd = mVsq.TotalClocks + 240; * } * double secEnd = mVsq.getSecFromClock( clockEnd ); * * invokeWaveViewReloadRequiredEvent( tracks.get( k ), wavePath, secStart, secEnd ); * }*/ AppManager.invokeWaveViewReloadRequiredEvent(track, wavePath, 1, -1); } #if DEBUG sout.println("SynthesizeWorker#patchWork; done"); #endif state.reportComplete(); }