(メッセージはありません)
@@ -0,0 +1,1182 @@ | ||
1 | +/* | |
2 | + * VSTiProxy.cs | |
3 | + * Copyright (c) 2008-2009 kbinani | |
4 | + * | |
5 | + * This file is part of Boare.Cadencii. | |
6 | + * | |
7 | + * Boare.Cadencii is free software; you can redistribute it and/or | |
8 | + * modify it under the terms of the BSD License. | |
9 | + * | |
10 | + * Boare.Cadencii 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 | +#define TEST_NEW_VSTIPROXY | |
15 | +#define TEST | |
16 | +using System; | |
17 | +using System.IO; | |
18 | +using System.Runtime.InteropServices; | |
19 | +using System.Collections.Generic; | |
20 | +using System.Threading; | |
21 | +using System.ComponentModel; | |
22 | +using System.Security; | |
23 | +using System.Windows.Forms; | |
24 | + | |
25 | +using Boare.Lib.Vsq; | |
26 | +using Boare.Lib.Media; | |
27 | + | |
28 | +namespace Boare.Cadencii { | |
29 | + | |
30 | + public enum VSTiProxyTrackType { | |
31 | + TempoTrack, | |
32 | + MainTrack, | |
33 | + } | |
34 | + | |
35 | + [StructLayout( LayoutKind.Sequential, Pack = 1 )] | |
36 | + struct VstEvent : IComparable<VstEvent> { | |
37 | + public Int32 type; | |
38 | + public Int32 byteSize; | |
39 | + public Int32 deltaFrames; | |
40 | + public Int32 flags; | |
41 | + byte data0; | |
42 | + byte data1; | |
43 | + byte data2; | |
44 | + byte data3; | |
45 | + byte data4; | |
46 | + byte data5; | |
47 | + byte data6; | |
48 | + byte data7; | |
49 | + byte data8; | |
50 | + byte data9; | |
51 | + byte data10; | |
52 | + byte data11; | |
53 | + byte data12; | |
54 | + byte data13; | |
55 | + byte data14; | |
56 | + byte data15; | |
57 | + | |
58 | + public int CompareTo( VstEvent item ) { | |
59 | + return deltaFrames - item.deltaFrames; | |
60 | + } | |
61 | + | |
62 | + public byte this[int index] { | |
63 | + get { | |
64 | + switch ( index ) { | |
65 | + case 0: | |
66 | + return data0; | |
67 | + case 1: | |
68 | + return data1; | |
69 | + case 2: | |
70 | + return data2; | |
71 | + case 3: | |
72 | + return data3; | |
73 | + case 4: | |
74 | + return data4; | |
75 | + case 5: | |
76 | + return data5; | |
77 | + case 6: | |
78 | + return data6; | |
79 | + case 7: | |
80 | + return data7; | |
81 | + case 8: | |
82 | + return data8; | |
83 | + case 9: | |
84 | + return data9; | |
85 | + case 10: | |
86 | + return data10; | |
87 | + case 11: | |
88 | + return data11; | |
89 | + case 12: | |
90 | + return data12; | |
91 | + case 13: | |
92 | + return data13; | |
93 | + case 14: | |
94 | + return data14; | |
95 | + case 15: | |
96 | + return data15; | |
97 | + default: | |
98 | + throw new ArgumentOutOfRangeException(); | |
99 | + } | |
100 | + } | |
101 | + set { | |
102 | + switch ( index ) { | |
103 | + case 0: | |
104 | + data0 = value; | |
105 | + break; | |
106 | + case 1: | |
107 | + data1 = value; | |
108 | + break; | |
109 | + case 2: | |
110 | + data2 = value; | |
111 | + break; | |
112 | + case 3: | |
113 | + data3 = value; | |
114 | + break; | |
115 | + case 4: | |
116 | + data4 = value; | |
117 | + break; | |
118 | + case 5: | |
119 | + data5 = value; | |
120 | + break; | |
121 | + case 6: | |
122 | + data6 = value; | |
123 | + break; | |
124 | + case 7: | |
125 | + data7 = value; | |
126 | + break; | |
127 | + case 8: | |
128 | + data8 = value; | |
129 | + break; | |
130 | + case 9: | |
131 | + data9 = value; | |
132 | + break; | |
133 | + case 10: | |
134 | + data10 = value; | |
135 | + break; | |
136 | + case 11: | |
137 | + data11 = value; | |
138 | + break; | |
139 | + case 12: | |
140 | + data12 = value; | |
141 | + break; | |
142 | + case 13: | |
143 | + data13 = value; | |
144 | + break; | |
145 | + case 14: | |
146 | + data14 = value; | |
147 | + break; | |
148 | + case 15: | |
149 | + data15 = value; | |
150 | + break; | |
151 | + default: | |
152 | + throw new ArgumentOutOfRangeException(); | |
153 | + } | |
154 | + } | |
155 | + } | |
156 | + } | |
157 | + | |
158 | + unsafe struct MIDI_EVENT { | |
159 | + public uint clock; | |
160 | + public MIDI_EVENT* pNext; | |
161 | + public uint dwDataSize; | |
162 | + public byte dwOffset; | |
163 | + public byte* pMidiEvent; | |
164 | + } | |
165 | + | |
166 | + public static class VSTiProxy { | |
167 | + private const int _SAMPLE_RATE = 44100; | |
168 | + private const int _BLOCK_SIZE = 44100; | |
169 | + | |
170 | + private static string s_dll_path = ""; | |
171 | + public static string CurrentUser = ""; | |
172 | + private static bool s_loaded = false; | |
173 | + private static Wave s_wave; | |
174 | + //private static WaveWriter s_wave; | |
175 | + private static bool s_rendering = false; | |
176 | + private static int s_trim_remain = 0; | |
177 | + private static object s_locker; | |
178 | + private static double s_amplify_left = 1.0; | |
179 | + private static double s_amplify_right = 1.0; | |
180 | + | |
181 | + static VSTiProxy() { | |
182 | + try { | |
183 | + s_locker = new object(); | |
184 | + s_dll_path = VsqUtil.VstiDllPath; | |
185 | +#if DEBUG | |
186 | + Common.DebugWriteLine( "VSTiProxy+Init()" ); | |
187 | + Common.DebugWriteLine( " s_dll_path=" + s_dll_path ); | |
188 | +#endif | |
189 | + char[] str = s_dll_path.ToCharArray(); | |
190 | + if ( s_dll_path != "" ) { | |
191 | + s_loaded = vstidrv.Init( str, _BLOCK_SIZE, _SAMPLE_RATE ); | |
192 | + } else { | |
193 | + s_loaded = false; | |
194 | + } | |
195 | +#if TEST | |
196 | + bocoree.debug.PushLog( "VSTiProxy..cctor()" ); | |
197 | + bocoree.debug.PushLog( " s_dll_path=" + s_dll_path ); | |
198 | + bocoree.debug.PushLog( " s_loaded=" + s_loaded ); | |
199 | +#endif | |
200 | + } catch ( Exception ex ) { | |
201 | +#if TEST | |
202 | + bocoree.debug.PushLog( " ex=" + ex ); | |
203 | +#endif | |
204 | + } | |
205 | + } | |
206 | + | |
207 | + public static unsafe void RenderToWave( NrpnData[] nrpn, TempoTableEntry[] tempo, int clEos, string file, double amplify_left, double amplify_right ) { | |
208 | +#if TEST | |
209 | + Console.WriteLine( "VSTiProxy.RenderToWave( NRPN[], TempoTableEntry[], int, string )" ); | |
210 | + bocoree.debug.PushLog( "VSTiPRoxy+RenderToWave(NrpnData[], TempoTableEntry[], int, string)" ); | |
211 | +#endif | |
212 | + if ( !s_loaded ) { | |
213 | + return; | |
214 | + } | |
215 | + int tempo_count = tempo.Length; | |
216 | + float first_tempo = 125.0f; | |
217 | + if ( tempo.Length > 0 ) { | |
218 | + first_tempo = (float)(60e6 / (double)tempo[0].Tempo); | |
219 | + } | |
220 | + byte[] masterEventsSrc = new byte[tempo_count * 3]; | |
221 | + int[] masterClocksSrc = new int[tempo_count]; | |
222 | + int count = -3; | |
223 | + for ( int i = 0; i < tempo.Length; i++ ) { | |
224 | + count += 3; | |
225 | + masterClocksSrc[i] = tempo[i].Clock; | |
226 | + byte b0 = (byte)(tempo[i].Tempo >> 16); | |
227 | + uint u0 = (uint)(tempo[i].Tempo - (b0 << 16)); | |
228 | + byte b1 = (byte)(u0 >> 8); | |
229 | + byte b2 = (byte)(u0 - (u0 << 8)); | |
230 | +#if DEBUG | |
231 | + Console.WriteLine( " b0-b2=0x" + Convert.ToString( b0, 16 ) + ", 0x" + Convert.ToString( b1, 16 ) + ", 0x" + Convert.ToString( b2, 16 ) ); | |
232 | +#endif | |
233 | + masterEventsSrc[count] = b0; | |
234 | + masterEventsSrc[count + 1] = b1; | |
235 | + masterEventsSrc[count + 2] = b2; | |
236 | + } | |
237 | + fixed ( byte* masterEvents = &masterEventsSrc[0] ) | |
238 | + fixed ( int* masterClocks = &masterClocksSrc[0] ) { | |
239 | + vstidrv.SendEvent( masterEvents, masterClocks, tempo_count, (int)VSTiProxyTrackType.TempoTrack ); | |
240 | + } | |
241 | + | |
242 | + int numEvents = nrpn.Length + 1; | |
243 | + byte[] bodyEventsSrc = new byte[numEvents * 3]; | |
244 | + int[] bodyClocksSrc = new int[numEvents]; | |
245 | + count = -3; | |
246 | + int last_clock = 0; | |
247 | +#if DEBUG | |
248 | + string test = Path.Combine( Path.GetDirectoryName( file ), Path.GetFileNameWithoutExtension( file ) + ".txt" ); | |
249 | + StreamWriter sw = new StreamWriter( test, false ); | |
250 | +#endif | |
251 | + for ( int i = 0; i < numEvents - 1; i++ ) { | |
252 | + count += 3; | |
253 | + bodyEventsSrc[count] = 0xb0; | |
254 | + bodyEventsSrc[count + 1] = nrpn[i].Parameter; | |
255 | + bodyEventsSrc[count + 2] = nrpn[i].Value; | |
256 | + bodyClocksSrc[i] = nrpn[i].Clock; | |
257 | + last_clock = nrpn[i].Clock; | |
258 | +#if DEBUG | |
259 | + sw.WriteLine( nrpn[i].Clock + "\t0x" + Convert.ToString( nrpn[i].Parameter, 16 ) + "\t0x" + Convert.ToString( nrpn[i].Value, 16 ) ); | |
260 | +#endif | |
261 | + } | |
262 | +#if DEBUG | |
263 | + sw.Close(); | |
264 | +#endif | |
265 | + count += 3; | |
266 | + bodyEventsSrc[count] = 0xff; | |
267 | + bodyEventsSrc[count + 1] = 0x2f; | |
268 | + bodyEventsSrc[count + 2] = 0x00; | |
269 | + bodyClocksSrc[numEvents - 1] = clEos; | |
270 | + int index = tempo_count - 1; | |
271 | + for ( int i = tempo_count - 1; i >= 0; i-- ) { | |
272 | + if ( tempo[i].Clock < last_clock ) { | |
273 | + index = i; | |
274 | + break; | |
275 | + } | |
276 | + } | |
277 | + int last_tempo = tempo[index].Tempo; | |
278 | + | |
279 | + fixed ( byte* bodyEvents = &bodyEventsSrc[0] ) | |
280 | + fixed ( int* bodyClocks = &bodyClocksSrc[0] ) { | |
281 | + vstidrv.SendEvent( bodyEvents, bodyClocks, numEvents, (int)VSTiProxyTrackType.MainTrack ); | |
282 | + } | |
283 | + | |
284 | + s_rendering = true; | |
285 | + s_wave = new Wave( WaveChannel.Stereo, 16, _SAMPLE_RATE, 0 ); | |
286 | + s_trim_remain = GetErrorSamples( first_tempo ); | |
287 | +#if DEBUG | |
288 | + bocoree.debug.PushLog( " first_tempo=" + first_tempo ); | |
289 | + bocoree.debug.PushLog( " s_trim_remain=" + s_trim_remain ); | |
290 | +#endif | |
291 | +#if TEST_NEW_VSTIPROXY | |
292 | + if ( s_trim_remain < 0 ) { | |
293 | + double[] d = new double[-s_trim_remain]; | |
294 | + for ( int i = 0; i < -s_trim_remain; i++ ) { | |
295 | + d[i] = 0.0; | |
296 | + } | |
297 | + s_wave.Append( d, d ); | |
298 | + s_trim_remain = 0; | |
299 | + } | |
300 | +#endif | |
301 | + s_amplify_left = amplify_left; | |
302 | + s_amplify_right = amplify_right; | |
303 | + vstidrv.WaveIncoming += VSTi3_WaveIncoming; | |
304 | + vstidrv.RenderingFinished += VSTi3_RenderingFinished; | |
305 | + vstidrv.StartRendering( clEos ); | |
306 | + while ( s_rendering ) { | |
307 | + Application.DoEvents(); | |
308 | + } | |
309 | + s_rendering = false; | |
310 | + vstidrv.WaveIncoming -= VSTi3_WaveIncoming; | |
311 | + vstidrv.RenderingFinished -= VSTi3_RenderingFinished; | |
312 | + s_wave.Write( file ); | |
313 | + s_wave.Dispose(); | |
314 | + s_wave = null; | |
315 | + } | |
316 | + | |
317 | + static void VSTi3_RenderingFinished( object sender, EventArgs e ) { | |
318 | + s_rendering = false; | |
319 | + } | |
320 | + | |
321 | + static void VSTi3_WaveIncoming( double[] L, double[] R ) { | |
322 | +#if TEST_NEW_VSTIPROXY | |
323 | + lock ( s_locker ) { | |
324 | + if ( s_trim_remain > 0 ) { | |
325 | + if ( s_trim_remain >= L.Length ) { | |
326 | + s_trim_remain -= L.Length; | |
327 | + return; | |
328 | + } | |
329 | + int actual_append = L.Length - s_trim_remain; | |
330 | + double[] dL = new double[actual_append]; | |
331 | + double[] dR = new double[actual_append]; | |
332 | + Array.Copy( L, L.Length - actual_append, dL, 0, actual_append ); | |
333 | + Array.Copy( R, L.Length - actual_append, dR, 0, actual_append ); | |
334 | + if ( s_amplify_left != 0.0 ) { | |
335 | + for ( int i = 0; i < dL.Length; i++ ) { | |
336 | + dL[i] = dL[i] * s_amplify_left; | |
337 | + } | |
338 | + } | |
339 | + if ( s_amplify_right != 0.0 ) { | |
340 | + for ( int i = 0; i < dR.Length; i++ ) { | |
341 | + dR[i] = dR[i] * s_amplify_right; | |
342 | + } | |
343 | + } | |
344 | + s_wave.Append( dL, dR ); | |
345 | + dL = null; | |
346 | + dR = null; | |
347 | + s_trim_remain = 0; | |
348 | + } else { | |
349 | +#endif | |
350 | + if ( s_amplify_left != 0.0 ) { | |
351 | + for ( int i = 0; i < L.Length; i++ ) { | |
352 | + L[i] = L[i] * s_amplify_left; | |
353 | + } | |
354 | + } | |
355 | + if ( s_amplify_right != 0.0 ) { | |
356 | + for ( int i = 0; i < R.Length; i++ ) { | |
357 | + R[i] = R[i] * s_amplify_right; | |
358 | + } | |
359 | + } | |
360 | + s_wave.Append( L, R ); | |
361 | +#if TEST_NEW_VSTIPROXY | |
362 | + } | |
363 | + } | |
364 | +#endif | |
365 | + } | |
366 | + | |
367 | + public static double GetProgress() { | |
368 | + if ( s_loaded ) { | |
369 | + return vstidrv.GetProgress(); | |
370 | + } else { | |
371 | + return 0.0; | |
372 | + } | |
373 | + } | |
374 | + | |
375 | + public static void AbortRendering() { | |
376 | + if ( s_rendering ) { | |
377 | + s_rendering = false; | |
378 | + vstidrv.AbortRendering(); | |
379 | + } | |
380 | + } | |
381 | + | |
382 | + /*public static string CurrentUser { | |
383 | + get { | |
384 | +#if DEBUG | |
385 | + Console.WriteLine( "VSTiProxy+get_CurrentUser" ); | |
386 | +#endif | |
387 | + return s_current_user; | |
388 | + } | |
389 | + set { | |
390 | +#if DEBUG | |
391 | + Console.WriteLine( "VSTiPRoxy+set_CurrentUser" ); | |
392 | +#endif | |
393 | + s_current_user = value; | |
394 | + } | |
395 | + }*/ | |
396 | + | |
397 | + private static int GetErrorSamples( float tempo ) { | |
398 | + const float a0 = -17317.563f; | |
399 | + const float a1 = 86.7312112f; | |
400 | + const float a2 = -0.237323499f; | |
401 | + if ( tempo <= 240 ) { | |
402 | + return 4666; | |
403 | + } else { | |
404 | + float x = tempo - 240; | |
405 | + return (int)((a2 * x + a1) * x + a0); | |
406 | + } | |
407 | + } | |
408 | + } | |
409 | + | |
410 | + /// <summary> | |
411 | + /// C++製dll,VSTi2.dllを使った,VSTiホストへのプラクシー | |
412 | + /// </summary> | |
413 | + static class VSTiProxy_ { | |
414 | + private static bool s_loaded = false; | |
415 | + private static string s_dll_path = "C:\\Program Files\\Steinberg\\VSTplugins\\VOCALOID2\\VOCALOID2.dll"; | |
416 | + private static int s_error_counter = 0; | |
417 | + private static int s_sample_rate = 44100; | |
418 | + private static string s_current_user = ""; | |
419 | + | |
420 | + const int NUM_ERR_THRESHOLD = 10; | |
421 | + const int _BLOCK_SIZE = 44100; | |
422 | + const int _SAMPLE_RATE = 44100; | |
423 | + /* /// ファイル名を指定してプラグインを読み込む | |
424 | + int __declspec(dllexport) LoadPluginA( char *name, int blockSize, int sampleRate ); | |
425 | + /// ファイル名を指定してプラグインを読み込む(ユニコード版) | |
426 | + int __declspec(dllexport) LoadPluginW( WCHAR *name, int blockSize, int sampleRate ); | |
427 | + /// プラグインを開放する | |
428 | + int __declspec(dllexport) FreePlugin(); | |
429 | + /// プラグインに送るmidiデータを指定する。 | |
430 | + int __declspec(dllexport) SendEvent( unsigned char *src, int *deltaFrames, int numEventsm, int targetTrack ); | |
431 | + /// 音符1個分のレンダリングを進める。続きのレンダリングはRenderContinueで行う。戻り値は、残りのフレーム数 | |
432 | + int __declspec(dllexport) RenderFirst( int *outL, int *outR, int sampleFrames ); | |
433 | + /// RenderFirstでレンダリング出来なかった残りの部分のレンダリングを行う。戻り値は、残りのフレーム数 | |
434 | + int __declspec(dllexport) RenderContinue( int *outL, int *outR, int sampleFrames ); | |
435 | + int __declspec(dllexport) RenderToWave( TCHAR *path );*/ | |
436 | + | |
437 | + [DllImport( "VSTi2.dll", CharSet = CharSet.Ansi, EntryPoint = "LoadPluginA" )] | |
438 | + private static extern int LoadPluginA_( string name, int blockSize, int sampleRate ); | |
439 | + | |
440 | + [DllImport( "VSTi2.dll", CharSet = CharSet.Unicode, EntryPoint = "LoadPluginW" )] | |
441 | + private static extern int LoadPluginW_( string name, int blockSize, int sampleRate ); | |
442 | + | |
443 | + /*[System.Security.SuppressUnmanagedCodeSecurity] | |
444 | + [DllImport( "VSTi2.dll", CharSet = CharSet.Unicode, EntryPoint = "LoadPluginW", CallingConvention = CallingConvention.StdCall )] | |
445 | + private static extern int UnsafeLoadPluginWStdCall( string name, int blockSize, int sampleRate );*/ | |
446 | + | |
447 | + [SuppressUnmanagedCodeSecurity] | |
448 | + [DllImport( "VSTi2.dll", CharSet = CharSet.Unicode, EntryPoint = "LoadPluginW", CallingConvention = CallingConvention.Cdecl )] | |
449 | + private static extern int UnsafeLoadPluginWCdecl( string name, int blockSize, int sampleRate ); | |
450 | + | |
451 | + [DllImport( "VSTi2.dll", EntryPoint = "SendEvent" )] | |
452 | + private static unsafe extern int SendEvent_( byte* src, int* deltaFrames, int numEvents, int targetTrack ); | |
453 | + | |
454 | + [DllImport( "VSTi2.dll", CharSet = CharSet.Unicode, EntryPoint = "RenderToWaveW" )] | |
455 | + private static extern int RenderToWaveW_( string name, uint eof_clock ); | |
456 | + | |
457 | + [DllImport( "VSTi2.dll", EntryPoint = "FreePlugin" )] | |
458 | + private static extern int FreePlugin_(); | |
459 | + | |
460 | + [DllImport( "VSTi2.dll", EntryPoint = "SetSampleRate" )] | |
461 | + private static extern int SetSampleRate_( float sample_rate ); | |
462 | + | |
463 | + [DllImport( "VSTi2.dll", EntryPoint = "MainsChanged" )] | |
464 | + private static extern int MainsChanged_( int status ); | |
465 | + | |
466 | + [DllImport( "VSTi2.dll", EntryPoint = "SetBlockSize" )] | |
467 | + private static extern int SetBlockSize_( int block_size ); | |
468 | + | |
469 | + [DllImport( "VSTi2.dll", EntryPoint = "ProcessEvents" )] | |
470 | + private static unsafe extern int ProcessEvents_( int numEvents, VstEvent* events ); | |
471 | + | |
472 | + [DllImport( "VSTi2.dll", EntryPoint = "ProcessReplacing" )] | |
473 | + private static unsafe extern void ProcessReplacing_( float** out_buffer, int frames ); | |
474 | + | |
475 | + [DllImport( "VSTi2.dll", EntryPoint = "AbortRendering" )] | |
476 | + private static extern void AbortRendering_(); | |
477 | + | |
478 | + [DllImport( "VSTi2.dll", EntryPoint = "GetProgress" )] | |
479 | + private static extern double GetProgress_(); | |
480 | + | |
481 | + /// <summary> | |
482 | + /// このクラスを使っているMainFormを特定するIDを取得、または設定します。""なら、どのFormMainのインスタンスもコイツを使ってない | |
483 | + /// </summary> | |
484 | + public static string CurrentUser { | |
485 | + get { | |
486 | + return s_current_user; | |
487 | + } | |
488 | + set { | |
489 | + s_current_user = value; | |
490 | + } | |
491 | + } | |
492 | + | |
493 | + public static bool ReloadPlugin() { | |
494 | + if ( s_loaded ) { | |
495 | + if ( FreePlugin() ) { | |
496 | + s_loaded = false; | |
497 | + } else { | |
498 | + return false; | |
499 | + } | |
500 | + } | |
501 | + return LoadPlugin( _BLOCK_SIZE, _SAMPLE_RATE ); | |
502 | + } | |
503 | + | |
504 | + public static double GetProgress() { | |
505 | + return GetProgress_(); | |
506 | + } | |
507 | + | |
508 | + public static int SampleRate { | |
509 | + get { | |
510 | + return s_sample_rate; | |
511 | + } | |
512 | + set { | |
513 | + s_sample_rate = value; | |
514 | + } | |
515 | + } | |
516 | + | |
517 | + private static double totalMilliSec_from_timeCode( uint timeCode, TempoTableEntry[] tempo ) { | |
518 | + const int _TIME_FORMAT = 480; | |
519 | + const int DEF_TEMPO = 500000; // デフォルトのテンポ. | |
520 | + double ret = 0.0; | |
521 | + int index = -1; | |
522 | + for ( int i = 0; i < tempo.Length; i++ ) { | |
523 | + if ( timeCode <= tempo[i].Clock ) { | |
524 | + break; | |
525 | + } | |
526 | + index = i; | |
527 | + } | |
528 | + if ( index >= 0 ) { | |
529 | + ret = tempo[index].Time + (timeCode - tempo[index].Clock) * (double)tempo[index].Tempo / (1000.0 * _TIME_FORMAT); | |
530 | + } else { | |
531 | + ret = timeCode * (double)DEF_TEMPO / (1000.0 * _TIME_FORMAT); | |
532 | + } | |
533 | + return ret; | |
534 | + } | |
535 | + | |
536 | + public static unsafe int NEW_RenderToWave( NrpnData[] nrpn, TempoTableEntry[] tempo, int clEos, string file ) { | |
537 | + if ( !s_loaded ) { | |
538 | + s_loaded = LoadPlugin( 44100, 44100 ); | |
539 | + } | |
540 | + /*WAVEFORMATEX wf; | |
541 | + wf.nChannels = 2; | |
542 | + wf.wFormatTag = WAVE_FORMAT_PCM; | |
543 | + wf.wBitsPerSample = 16; | |
544 | + wf.nBlockAlign = wf.nChannels*wf.wBitsPerSample/8; | |
545 | + wf.nSamplesPerSec = g_sampleRate;// sampleFrames; | |
546 | + wf.nAvgBytesPerSec = wf.nSamplesPerSec*wf.nBlockAlign; | |
547 | + | |
548 | + // Create WAV | |
549 | + HMMIO hmmio = mmioOpen( path, NULL, MMIO_CREATE | MMIO_WRITE | MMIO_EXCLUSIVE ); | |
550 | + MMCKINFO mmckRiff, mmckData, mmckFmt; | |
551 | + mmckRiff.fccType = mmioStringToFOURCC( TEXT("WAVE"), 0 ); | |
552 | + mmioCreateChunk( hmmio, &mmckRiff, MMIO_CREATERIFF ); | |
553 | + mmckFmt.ckid = mmioStringToFOURCC( TEXT("fmt "), 0 ); | |
554 | + mmioCreateChunk( hmmio, &mmckFmt, 0 ); | |
555 | + mmioWrite( hmmio, (char*)&wf, sizeof(WAVEFORMATEX) ); | |
556 | + mmioAscend( hmmio, &mmckFmt, 0 ); | |
557 | + | |
558 | + mmckData.ckid = mmioStringToFOURCC( TEXT("data"), 0 ); | |
559 | + mmioCreateChunk( hmmio, &mmckData, 0 );*/ | |
560 | + | |
561 | + //MIDI_EVENT* lpEvents = merge_events( g_ppTrackEvents[0], g_ppTrackEvents[1] ); | |
562 | + //MIDI_EVENT* current = lpEvents; | |
563 | + List<VstEvent> proc = new List<VstEvent>(); | |
564 | + for ( int i = 0; i < nrpn.Length; i++ ) { | |
565 | + VstEvent ve = new VstEvent(); | |
566 | + ve.deltaFrames = nrpn[i].Clock; | |
567 | + ve[0] = 0xb0; | |
568 | + ve[1] = nrpn[i].Parameter; | |
569 | + ve[2] = nrpn[i].Value; | |
570 | + ve.byteSize = 3; | |
571 | + proc.Add( ve ); | |
572 | + } | |
573 | + for ( int i = 0; i < tempo.Length; i++ ) { | |
574 | + VstEvent ve = new VstEvent(); | |
575 | + ve.deltaFrames = tempo[i].Clock; | |
576 | + ve[0] = 0xff; | |
577 | + ve[1] = 0x51; | |
578 | + ve[2] = 0x03; | |
579 | + uint t = (uint)tempo[i].Tempo; | |
580 | + ve[3] = (byte)(t >> 16); | |
581 | + t = (uint)(t - (ve[3] << 16)); | |
582 | + ve[4] = (byte)(t >> 8); | |
583 | + ve[5] = (byte)(t - (ve[4] << 8)); | |
584 | + ve.byteSize = 6; | |
585 | + proc.Add( ve ); | |
586 | + } | |
587 | + proc.Sort(); | |
588 | + | |
589 | + float[] left_ch_src = new float[s_sample_rate]; | |
590 | + float[] right_ch_src = new float[s_sample_rate]; | |
591 | + //float** out_buffer = { left_ch, right_ch }; | |
592 | + | |
593 | + //DWORD* wave_bufs; | |
594 | + //wave_bufs = new DWORD[g_sampleRate]; | |
595 | + | |
596 | + SetSampleRate_( (float)s_sample_rate ); | |
597 | + MainsChanged_( 1 ); | |
598 | + SetBlockSize_( s_sample_rate ); | |
599 | + | |
600 | + int cur_buf = 0; | |
601 | + | |
602 | + uint dwTempo = 500000; | |
603 | + | |
604 | + int delay = 0; | |
605 | + int duration = 0; | |
606 | + uint dwNow = 0; | |
607 | + uint dwPrev = 0; | |
608 | + uint dwDelta; | |
609 | + uint dwDelay = 0; | |
610 | + uint dwDeltaDelay = 0; | |
611 | + | |
612 | + int addr_msb = 0, addr_lsb = 0; | |
613 | + int data_msb = 0, data_lsb = 0; | |
614 | + | |
615 | +#if DEBUG | |
616 | + Console.WriteLine( "***********************************************************************" ); | |
617 | + int total_processed = 0; | |
618 | +#endif | |
619 | + | |
620 | + //MIDI_EVENT* pWork = g_ppTrackEvents[1]; | |
621 | + int eof_clock = clEos; | |
622 | + /*while ( !dwDelay && pWork ) { | |
623 | + // Delayの取得 | |
624 | + if ( (pWork->pMidiEvent[0] & 0xf0) == 0xb0 ) { | |
625 | + switch ( pWork->pMidiEvent[1] ) { | |
626 | + case 0x63: | |
627 | + addr_msb = pWork->pMidiEvent[2]; | |
628 | + addr_lsb = 0; | |
629 | + break; | |
630 | + case 0x62: | |
631 | + addr_lsb = pWork->pMidiEvent[2]; | |
632 | + break; | |
633 | + case 0x06: | |
634 | + data_msb = pWork->pMidiEvent[2]; | |
635 | + break; | |
636 | + case 0x26: | |
637 | + data_lsb = pWork->pMidiEvent[2]; | |
638 | + if ( addr_msb == 0x50 && addr_lsb == 0x01 ) { | |
639 | + delay = data_msb << 7 | data_lsb; | |
640 | +#if DEBUG | |
641 | + Console.WriteLine( "delay=" + delay ); | |
642 | +#endif | |
643 | + dwDelay = (uint)(delay * (double)m_sample_rate / 1000.0); | |
644 | + } | |
645 | + break; | |
646 | + } | |
647 | + } else if ( pWork->pMidiEvent[0] == 0xff && pWork->pMidiEvent[1] == 0x2f && pWork->pMidiEvent[2] == 0x00 ) { | |
648 | + eof_clock = pWork->clock; | |
649 | +#if DEBUG | |
650 | + Console.WriteLine( "eof_clock=" + eof_clock ); | |
651 | +#endif | |
652 | + } | |
653 | + pWork = pWork->pNext; | |
654 | + }*/ | |
655 | + | |
656 | + int current_index = 0; | |
657 | + while ( true ) { | |
658 | +#if DEBUG | |
659 | + Console.WriteLine( "-----------------------------------------------------------------------" ); | |
660 | +#endif | |
661 | + //MIDI_EVENT* pProcessEvent = current; | |
662 | + int nEvents = 0; | |
663 | + | |
664 | + while ( proc[current_index].deltaFrames == dwNow ) { | |
665 | + /*if ( current->pMidiEvent[0] == 0xFF ) { | |
666 | + //メタイベントの処理 | |
667 | + switch ( current->pMidiEvent[1] ) { | |
668 | + case 0x51: | |
669 | + dwTempo = (uint)(current->pMidiEvent[5] | (current->pMidiEvent[4] << 8) | (current->pMidiEvent[3] << 16)); | |
670 | + break; | |
671 | + } | |
672 | + }*/ | |
673 | + // durationを取得 | |
674 | + if ( proc[current_index][0] == 0xb0 ) { | |
675 | + switch ( proc[current_index][1] ) { | |
676 | + case 0x63: | |
677 | + addr_msb = proc[current_index][2]; | |
678 | + addr_lsb = 0; | |
679 | + break; | |
680 | + case 0x62: | |
681 | + addr_lsb = proc[current_index][2]; | |
682 | + break; | |
683 | + case 0x06: | |
684 | + data_msb = proc[current_index][2]; | |
685 | + break; | |
686 | + case 0x26: | |
687 | + data_lsb = proc[current_index][2]; | |
688 | + // Note Duration in millisec | |
689 | + if ( addr_msb == 0x50 && addr_lsb == 0x04 ) { | |
690 | + duration = data_msb << 7 | data_lsb; | |
691 | +#if DEBUG | |
692 | + Console.WriteLine( "duration=" + duration ); | |
693 | +#endif | |
694 | + } | |
695 | + break; | |
696 | + } | |
697 | + } | |
698 | + | |
699 | + nEvents++; | |
700 | + current_index++;// = current->pNext; | |
701 | + if ( current_index >= proc.Count ) { | |
702 | + break; | |
703 | + } | |
704 | + } | |
705 | + | |
706 | + if ( current_index >= proc.Count ) { | |
707 | + break; | |
708 | + } | |
709 | + | |
710 | +#if DEBUG | |
711 | + Console.WriteLine( "nEvents=" + nEvents ); | |
712 | +#endif | |
713 | + double msNow = totalMilliSec_from_timeCode( dwNow, tempo ); | |
714 | + double msPrev = totalMilliSec_from_timeCode( dwPrev, tempo ); | |
715 | + //double dt = DeltaToMilliSecond( dwNow - dwPrev, dwTempo ); | |
716 | + double dt = msNow - msPrev; | |
717 | + dwDelta = (uint)(dt * s_sample_rate / 1000.0); | |
718 | +#if DEBUG | |
719 | + Console.WriteLine( "dwNow=" + dwNow ); | |
720 | + Console.WriteLine( "dwPrev=" + dwPrev ); | |
721 | + Console.WriteLine( "dt=" + dt ); | |
722 | + Console.WriteLine( "dwDelta=" + dwDelta ); | |
723 | +#endif | |
724 | + | |
725 | + //VstEvents* pVSTEvents = (VstEvents*)malloc( sizeof( VstEvents ) + nEvents * sizeof( VstEvent* ) ); | |
726 | + //pVSTEvents->numEvents = 0; | |
727 | + //pVSTEvents->reserved = NULL; | |
728 | + | |
729 | + VstEvent[] events = new VstEvent[nEvents]; | |
730 | + for ( int i = 0; i < nEvents; i++ ) { | |
731 | + //BYTE event_code = pProcessEvent->pMidiEvent[0]; | |
732 | + //VstEvent* pVSTEvent = NULL; | |
733 | + //VstMidiEvent* pMidiEvent; | |
734 | + | |
735 | + /*switch ( event_code ) { | |
736 | + case 0xff: | |
737 | + break; | |
738 | + case 0xf0: | |
739 | + case 0xf7: | |
740 | + break; | |
741 | + default: | |
742 | + pMidiEvent = (VstMidiEvent*)malloc( sizeof( VstMidiEvent ) + pProcessEvent->dwDataSize * sizeof( BYTE ) ); | |
743 | + pMidiEvent->byteSize = sizeof( VstMidiEvent ); | |
744 | + pMidiEvent->deltaFrames = dwDelta; | |
745 | + pMidiEvent->detune = 0; | |
746 | + pMidiEvent->flags = 1; | |
747 | + pMidiEvent->noteLength = 0; | |
748 | + pMidiEvent->noteOffset = 0; | |
749 | + pMidiEvent->noteOffVelocity = 0; | |
750 | + pMidiEvent->reserved1 = 0; | |
751 | + pMidiEvent->reserved2 = 0; | |
752 | + pMidiEvent->type = kVstMidiType; | |
753 | + memcpy( &pMidiEvent->midiData, &pProcessEvent->pMidiEvent[pProcessEvent->dwOffset], pProcessEvent->dwDataSize ); | |
754 | + pVSTEvents->events[pVSTEvents->numEvents++] = (VstEvent*)pMidiEvent; | |
755 | + break; | |
756 | + } | |
757 | + pProcessEvent = pProcessEvent->pNext;*/ | |
758 | + int index = current_index - nEvents + i + 1; | |
759 | + events[i] = new VstEvent(); | |
760 | + events[i].flags = 0; | |
761 | + events[i].type = 0; | |
762 | + for ( int j = 0; j < proc[i].byteSize; j++ ) { | |
763 | + events[i][j] = proc[index][j]; | |
764 | + } | |
765 | + events[i].deltaFrames = (int)dwDelta; | |
766 | + } | |
767 | + fixed ( VstEvent* pVstEvent = &events[0] ) { | |
768 | + ProcessEvents_( nEvents, pVstEvent ); | |
769 | + } | |
770 | + | |
771 | + /*for ( int i = 0; i < pVSTEvents->numEvents; i++ ) { | |
772 | + if ( pVSTEvents->events[i]->type == kVstSysExType ) { | |
773 | + VstMidiSysexEvent* pSysexEvent = (VstMidiSysexEvent*)pVSTEvents->events[i]; | |
774 | + free( pSysexEvent->sysexDump ); | |
775 | + } | |
776 | + }*/ | |
777 | + | |
778 | + while ( dwDelta > 0 ) { | |
779 | + uint dwFrames = dwDelta > s_sample_rate ? (uint)s_sample_rate : dwDelta; | |
780 | + fixed ( float* left = &left_ch_src[0] ) | |
781 | + fixed ( float* right = &right_ch_src[0] ) { | |
782 | + float*[] bufa = new float*[] { left, right }; | |
783 | + fixed ( float** buf = &bufa[0] ) { | |
784 | + ProcessReplacing_( buf, (int)dwFrames ); | |
785 | + } | |
786 | + } | |
787 | +#if DEBUG | |
788 | + for( int i = 0; i < left_ch_src.Length; i++ ){ | |
789 | + if ( i >= dwFrames ){ | |
790 | + break; | |
791 | + } | |
792 | + //Console.WriteLine( left_ch_src[i] ); | |
793 | + } | |
794 | +#endif | |
795 | + /*for ( size_t j = 0; j < dwFrames; j++ ) { | |
796 | + WORD right_chx = (WORD)(32768 * out_buffer[0][j]); | |
797 | + WORD left_chx = (WORD)(32768 * out_buffer[1][j]); | |
798 | + wave_bufs[j] = MAKELONG( right_chx, left_chx ); | |
799 | + }*/ | |
800 | + | |
801 | + int iOffset = (int)(dwDelay - dwDeltaDelay); | |
802 | + if ( iOffset > dwFrames ) { | |
803 | + iOffset = (int)dwFrames; | |
804 | + } | |
805 | + if ( iOffset == 0 ) { | |
806 | + //mmioWrite( hmmio, (char*)wave_bufs, dwFrames * sizeof( DWORD ) ); | |
807 | + //mmioAscend( hmmio, &mmckData, 0 ); | |
808 | + } else { | |
809 | + dwDeltaDelay += (uint)iOffset; | |
810 | + } | |
811 | +#if DEBUG | |
812 | + total_processed += (int)dwFrames; | |
813 | +#endif | |
814 | + dwDelta -= dwFrames; | |
815 | + } | |
816 | + | |
817 | + //free( pVSTEvents ); | |
818 | + | |
819 | + dwPrev = dwNow; | |
820 | + dwNow = (uint)nrpn[current_index].Clock; // current->clock; | |
821 | + } | |
822 | + | |
823 | +#if DEBUG | |
824 | + Console.WriteLine( "dwDeltaDelay=" + dwDeltaDelay ); | |
825 | +#endif | |
826 | + // dwDelta = g_sampleRate * (double)length / 1000.0 + dwDeltaDelay; | |
827 | + double msLast = totalMilliSec_from_timeCode( dwNow, tempo ); | |
828 | + double msEof = totalMilliSec_from_timeCode( (uint)eof_clock, tempo ); | |
829 | + double msRemain = msEof - msLast; | |
830 | + msRemain = (msRemain < 0.0) ? 0.0 : msRemain; | |
831 | + uint dwDeltaDraft = (uint)(s_sample_rate * msRemain / 1000.0); | |
832 | + dwDelta = (uint)(s_sample_rate * ((double)duration + (double)delay) / 1000.0 + dwDeltaDelay); | |
833 | +#if DEBUG | |
834 | + Console.WriteLine( "dwDeltaDraft=" + dwDeltaDraft ); | |
835 | + Console.WriteLine( "dwDelta=" + dwDelta ); | |
836 | +#endif | |
837 | + dwDelta = (dwDelta > dwDeltaDraft) ? dwDelta : dwDeltaDraft; | |
838 | + while ( dwDelta > 0 ) { | |
839 | + uint dwFrames = dwDelta > (uint)s_sample_rate ? (uint)s_sample_rate : dwDelta; | |
840 | + fixed ( float* left = &left_ch_src[0] ) | |
841 | + fixed ( float* right = &right_ch_src[0] ) { | |
842 | + float*[] bufa = new float*[] { left, right }; | |
843 | + fixed ( float** buf = &bufa[0] ) { | |
844 | + ProcessReplacing_( buf, (int)dwFrames ); | |
845 | + } | |
846 | + } | |
847 | + /*for ( size_t j = 0; j < dwFrames; j++ ) { | |
848 | + WORD right_chx = (WORD)(32768 * out_buffer[0][j]); | |
849 | + WORD left_chx = (WORD)(32768 * out_buffer[1][j]); | |
850 | + wave_bufs[j] = MAKELONG( right_chx, left_chx ); | |
851 | + }*/ | |
852 | +#if DEBUG | |
853 | + for( int i = 0; i < left_ch_src.Length; i++ ){ | |
854 | + if ( i >= dwFrames ){ | |
855 | + break; | |
856 | + } | |
857 | + //Console.WriteLine( left_ch_src[i] ); | |
858 | + } | |
859 | +#endif | |
860 | + | |
861 | + //mmioWrite( hmmio, (char*)wave_bufs, dwFrames * sizeof( DWORD ) ); | |
862 | + //mmioAscend( hmmio, &mmckData, 0 ); | |
863 | + | |
864 | + dwDelta -= dwFrames; | |
865 | +#if DEBUG | |
866 | + total_processed += (int)dwFrames; | |
867 | +#endif | |
868 | + } | |
869 | + | |
870 | + MainsChanged_( 0 );// g_pAEffect->dispatcher( g_pAEffect, effMainsChanged, 0, 0, 0, 0 ); | |
871 | + | |
872 | + //mmioAscend( hmmio, &mmckRiff, 0 ); | |
873 | + //mmioClose( hmmio, 0 ); | |
874 | + | |
875 | + //free_events( lpEvents ); | |
876 | + //delete[] left_ch; | |
877 | + //delete[] right_ch; | |
878 | + //delete[] wave_bufs; | |
879 | +#if DEBUG | |
880 | + Console.WriteLine( "total_processed=" + total_processed ); | |
881 | +#endif | |
882 | + | |
883 | + return 1; | |
884 | + } | |
885 | + | |
886 | + public static void AbortRendering() { | |
887 | + try { | |
888 | + AbortRendering_(); | |
889 | + } catch ( Exception ex ) { | |
890 | +#if TEST | |
891 | + bocoree.debug.PushLog( "VSTiProxy+AbortRendering()" ); | |
892 | + bocoree.debug.PushLog( " ex=" + ex.ToString() ); | |
893 | +#endif | |
894 | + } | |
895 | + } | |
896 | + | |
897 | + private static bool LoadPlugin( int blockSize, int sampleRate ) { | |
898 | + s_dll_path = VsqUtil.VstiDllPath; | |
899 | +#if TEST | |
900 | + bocoree.debug.PushLog( "VSTiProxy+LoadPlugin(String, Int32, Int32)" ); | |
901 | + bocoree.debug.PushLog( " m_dll_path=" + s_dll_path ); | |
902 | +#endif | |
903 | + if ( s_dll_path == "" ) { | |
904 | + s_loaded = false; | |
905 | + return false; | |
906 | + } | |
907 | + if ( s_error_counter > NUM_ERR_THRESHOLD ) { | |
908 | + //throw new ApplicationException(); | |
909 | + return false; | |
910 | + } | |
911 | + int ret = 0; | |
912 | + try { | |
913 | + ret = LoadPluginW_( s_dll_path, blockSize, sampleRate ); | |
914 | + s_loaded = true; | |
915 | + return true; | |
916 | + } catch ( Exception ex ) { | |
917 | +#if TEST | |
918 | + bocoree.debug.PushLog( ex.ToString() ); | |
919 | +#endif | |
920 | + s_error_counter++; | |
921 | + } | |
922 | + if ( ret == 0 ) { | |
923 | + s_error_counter++; | |
924 | + } | |
925 | + return false; | |
926 | + } | |
927 | + | |
928 | + private static bool FreePlugin() { | |
929 | + if ( s_loaded ) { | |
930 | + try { | |
931 | + int ret = FreePlugin_(); | |
932 | + if ( ret == 0 ) { | |
933 | + return false; | |
934 | + } else { | |
935 | + return true; | |
936 | + } | |
937 | + } catch ( Exception ex ) { | |
938 | +#if TEST | |
939 | + bocoree.debug.PushLog( "VSTiProxy+FreePlugin()" ); | |
940 | + bocoree.debug.PushLog( " ex=" + ex.ToString() ); | |
941 | +#endif | |
942 | + } | |
943 | + } | |
944 | + return false; | |
945 | + } | |
946 | + | |
947 | + public unsafe static void SendEvent( byte* src, int* deltaFrames, int numEvents, VSTiProxyTrackType targetTrack ) { | |
948 | + if ( s_loaded ) { | |
949 | + int track = targetTrack == VSTiProxyTrackType.TempoTrack ? 0 : 1; | |
950 | + try { | |
951 | + int ret = SendEvent_( src, deltaFrames, numEvents, track ); | |
952 | + if ( ret == 0 ) { | |
953 | + //throw new ApplicationException(); | |
954 | + } | |
955 | + } catch ( Exception ex ) { | |
956 | +#if TEST | |
957 | + bocoree.debug.PushLog( "VSTiProxy+SendEvent(byte*, int*, int, VSTiProxyTrackType)" ); | |
958 | + bocoree.debug.PushLog( " ex=" + ex.ToString() ); | |
959 | +#endif | |
960 | + } | |
961 | + } | |
962 | + } | |
963 | + | |
964 | + public static bool RenderToWave( string name, uint eof_clock ) { | |
965 | + try { | |
966 | + int ret = RenderToWaveW_( name, eof_clock ); | |
967 | + if ( ret == 0 ) { | |
968 | + return false; | |
969 | + } | |
970 | + return true; | |
971 | + } catch ( Exception ex ) { | |
972 | +#if TEST | |
973 | + bocoree.debug.PushLog( "VSTiProxy+RenderToWave(string, uint)" ); | |
974 | + bocoree.debug.PushLog( " ex=" + ex.ToString() ); | |
975 | +#endif | |
976 | + } | |
977 | + return false; | |
978 | + } | |
979 | + | |
980 | + public static void Finalize() { | |
981 | + AbortRendering(); | |
982 | + if ( s_loaded ) { | |
983 | + FreePlugin(); | |
984 | + s_loaded = false; | |
985 | + } | |
986 | + } | |
987 | + | |
988 | + public static unsafe bool RenderToWave( NrpnData[] nrpn, TempoTableEntry[] tempo, int clEos, string file ) { | |
989 | +#if TEST | |
990 | + Console.WriteLine( "VSTiProxy.RenderToWave( NRPN[], TempoTableEntry[], int, string )" ); | |
991 | + bocoree.debug.PushLog( "VSTiPRoxy+RenderToWave(NrpnData[], TempoTableEntry[], int, string)" ); | |
992 | +#endif | |
993 | + if ( !s_loaded ) { | |
994 | + s_loaded = LoadPlugin( 44100, 44100 ); | |
995 | + } | |
996 | + if ( !s_loaded ) { | |
997 | + return false; | |
998 | + } | |
999 | + int tempo_count = tempo.Length; | |
1000 | + byte[] masterEventsSrc = new byte[tempo_count * 3]; | |
1001 | + int[] masterClocksSrc = new int[tempo_count]; | |
1002 | + int count = -3; | |
1003 | + for ( int i = 0; i < tempo.Length; i++ ) { | |
1004 | + count += 3; | |
1005 | + masterClocksSrc[i] = tempo[i].Clock; | |
1006 | + byte b0 = (byte)(tempo[i].Tempo >> 16); | |
1007 | + uint u0 = (uint)(tempo[i].Tempo - (b0 << 16)); | |
1008 | + byte b1 = (byte)(u0 >> 8); | |
1009 | + byte b2 = (byte)(u0 - (u0 << 8)); | |
1010 | +#if DEBUG | |
1011 | + Console.WriteLine( " b0-b2=0x" + Convert.ToString( b0, 16 ) + ", 0x" + Convert.ToString( b1, 16 ) + ", 0x" + Convert.ToString( b2, 16 ) ); | |
1012 | +#endif | |
1013 | + masterEventsSrc[count] = b0; | |
1014 | + masterEventsSrc[count + 1] = b1; | |
1015 | + masterEventsSrc[count + 2] = b2; | |
1016 | + } | |
1017 | + fixed ( byte* masterEvents = &masterEventsSrc[0] ) | |
1018 | + fixed ( int* masterClocks = &masterClocksSrc[0] ) { | |
1019 | + SendEvent( masterEvents, masterClocks, tempo_count, VSTiProxyTrackType.TempoTrack ); | |
1020 | + } | |
1021 | + | |
1022 | + int numEvents = nrpn.Length + 1; | |
1023 | + byte[] bodyEventsSrc = new byte[numEvents * 3]; | |
1024 | + int[] bodyClocksSrc = new int[numEvents]; | |
1025 | + count = -3; | |
1026 | + int last_clock = 0; | |
1027 | +#if DEBUG | |
1028 | + string test = Path.Combine( Path.GetDirectoryName( file ), Path.GetFileNameWithoutExtension( file ) + ".txt" ); | |
1029 | + StreamWriter sw = new StreamWriter( test, false ); | |
1030 | +#endif | |
1031 | + for ( int i = 0; i < numEvents - 1; i++ ) { | |
1032 | + count += 3; | |
1033 | + bodyEventsSrc[count] = 0xb0; | |
1034 | + bodyEventsSrc[count + 1] = nrpn[i].Parameter; | |
1035 | + bodyEventsSrc[count + 2] = nrpn[i].Value; | |
1036 | + bodyClocksSrc[i] = nrpn[i].Clock; | |
1037 | + last_clock = nrpn[i].Clock; | |
1038 | +#if DEBUG | |
1039 | + sw.WriteLine( nrpn[i].Clock + "\t0x" + Convert.ToString( nrpn[i].Parameter, 16 ) + "\t0x" + Convert.ToString( nrpn[i].Value, 16 ) ); | |
1040 | +#endif | |
1041 | + } | |
1042 | +#if DEBUG | |
1043 | + sw.Close(); | |
1044 | +#endif | |
1045 | + count += 3; | |
1046 | + bodyEventsSrc[count] = 0xff; | |
1047 | + bodyEventsSrc[count + 1] = 0x2f; | |
1048 | + bodyEventsSrc[count + 2] = 0x00; | |
1049 | + bodyClocksSrc[numEvents - 1] = clEos; | |
1050 | + int index = tempo_count - 1; | |
1051 | + for ( int i = tempo_count - 1; i >= 0; i-- ) { | |
1052 | + if ( tempo[i].Clock < last_clock ) { | |
1053 | + index = i; | |
1054 | + break; | |
1055 | + } | |
1056 | + } | |
1057 | + int last_tempo = tempo[index].Tempo; | |
1058 | + | |
1059 | + fixed ( byte* bodyEvents = &bodyEventsSrc[0] ) | |
1060 | + fixed ( int* bodyClocks = &bodyClocksSrc[0] ) { | |
1061 | + SendEvent( bodyEvents, bodyClocks, numEvents, VSTiProxyTrackType.MainTrack ); | |
1062 | + } | |
1063 | + return RenderToWave( file, (uint)clEos ); | |
1064 | + } | |
1065 | + | |
1066 | + public static unsafe void GenerateSinglePhone( int note, byte program_change, string file ) { | |
1067 | +#if TEST | |
1068 | + Common.DebugWriteLine( "VSTiProxy+GenerateSinglePhone" ); | |
1069 | + Common.DebugWriteLine( " program_change=" + program_change ); | |
1070 | +#endif | |
1071 | + #region pre-set data | |
1072 | + NrpnData[] nrps = new NrpnData[]{ | |
1073 | + new NrpnData( 0, 0x63, 0x60 ), | |
1074 | + new NrpnData( 0, 0x62, 0x00 ), | |
1075 | + new NrpnData( 0, 0x06, 0x00 ), | |
1076 | + new NrpnData( 0, 0x26, 0x00 ), | |
1077 | + new NrpnData( 0, 0x63, 0x60 ), | |
1078 | + new NrpnData( 0, 0x62, 0x01 ), | |
1079 | + new NrpnData( 0, 0x06, 0x00 ), | |
1080 | + new NrpnData( 0, 0x26, 0x00 ), | |
1081 | + new NrpnData( 0, 0x63, 0x60 ), | |
1082 | + new NrpnData( 0, 0x62, 0x02 ), | |
1083 | + new NrpnData( 0, 0x06, 0x00 ), | |
1084 | + new NrpnData( 0, 0x63, 0x53 ), | |
1085 | + new NrpnData( 0, 0x62, 0x02 ), | |
1086 | + new NrpnData( 0, 0x06, program_change ), | |
1087 | + new NrpnData( 286, 0x63, 0x50 ), | |
1088 | + new NrpnData( 286, 0x62, 0x01 ), | |
1089 | + new NrpnData( 286, 0x06, 0x02 ), | |
1090 | + new NrpnData( 286, 0x26, 0x47 ), | |
1091 | + new NrpnData( 286, 0x62, 0x02 ), | |
1092 | + new NrpnData( 286, 0x06, (byte)note ), | |
1093 | + new NrpnData( 286, 0x62, 0x03 ), | |
1094 | + new NrpnData( 286, 0x06, 0x40 ), | |
1095 | + new NrpnData( 286, 0x62, 0x04 ), | |
1096 | + new NrpnData( 286, 0x06, 0x03 ), | |
1097 | + new NrpnData( 286, 0x26, 0x74 ), | |
1098 | + new NrpnData( 286, 0x62, 0x05 ), | |
1099 | + new NrpnData( 286, 0x06, 0x03 ), | |
1100 | + new NrpnData( 286, 0x62, 0x12 ), | |
1101 | + new NrpnData( 286, 0x06, 0x01 ), | |
1102 | + new NrpnData( 286, 0x62, 0x13 ), | |
1103 | + new NrpnData( 286, 0x06, 0x61 ), | |
1104 | + new NrpnData( 286, 0x26, 0x00 ), | |
1105 | + new NrpnData( 286, 0x62, 0x4F ), | |
1106 | + new NrpnData( 286, 0x06, 0x7F ), | |
1107 | + new NrpnData( 286, 0x62, 0x50 ), | |
1108 | + new NrpnData( 286, 0x06, 0x04 ), | |
1109 | + new NrpnData( 286, 0x62, 0x51 ), | |
1110 | + new NrpnData( 286, 0x06, 0x08 ), | |
1111 | + new NrpnData( 286, 0x62, 0x52 ), | |
1112 | + new NrpnData( 286, 0x06, 0x14 ), | |
1113 | + new NrpnData( 286, 0x62, 0x53 ), | |
1114 | + new NrpnData( 286, 0x06, 0x1C ), | |
1115 | + new NrpnData( 286, 0x62, 0x54 ), | |
1116 | + new NrpnData( 286, 0x06, 0x18 ), | |
1117 | + new NrpnData( 286, 0x62, 0x55 ), | |
1118 | + new NrpnData( 286, 0x06, 0x0A ), | |
1119 | + new NrpnData( 286, 0x62, 0x56 ), | |
1120 | + new NrpnData( 286, 0x06, 0x0C ), | |
1121 | + new NrpnData( 286, 0x62, 0x57 ), | |
1122 | + new NrpnData( 286, 0x06, 0x0C ), | |
1123 | + new NrpnData( 286, 0x62, 0x58 ), | |
1124 | + new NrpnData( 286, 0x06, 0x00 ), | |
1125 | + new NrpnData( 286, 0x62, 0x59 ), | |
1126 | + new NrpnData( 286, 0x06, 0x32 ), | |
1127 | + new NrpnData( 286, 0x62, 0x5A ), | |
1128 | + new NrpnData( 286, 0x06, 0x32 ), | |
1129 | + new NrpnData( 286, 0x62, 0x7F ), | |
1130 | + new NrpnData( 286, 0x06, 0x7F ), | |
1131 | + }; | |
1132 | + #endregion | |
1133 | + if ( !s_loaded ) { | |
1134 | + try { | |
1135 | + s_loaded = LoadPlugin( 44100, 44100 ); | |
1136 | + } catch ( Exception ex ) { | |
1137 | +#if TEST | |
1138 | + bocoree.debug.PushLog( ex.ToString() ); | |
1139 | +#endif | |
1140 | + return; | |
1141 | + } | |
1142 | + if ( !s_loaded ) { | |
1143 | + return; | |
1144 | + } | |
1145 | + } | |
1146 | + byte[] masterEventsSrc = new byte[3]; | |
1147 | + masterEventsSrc[0] = 0x07; | |
1148 | + masterEventsSrc[1] = 0xa1; | |
1149 | + masterEventsSrc[2] = 0x20; | |
1150 | + int[] masterClocksSrc = new int[1]; | |
1151 | + masterClocksSrc[0] = 0; | |
1152 | + fixed ( byte* masterEvents = &masterEventsSrc[0] ) | |
1153 | + fixed ( int* masterClocks = &masterClocksSrc[0] ) { | |
1154 | + SendEvent( masterEvents, masterClocks, 1, VSTiProxyTrackType.TempoTrack ); | |
1155 | + } | |
1156 | + | |
1157 | + int numEvents = nrps.Length + 1; | |
1158 | + byte[] bodyEventsSrc = new byte[numEvents * 3]; | |
1159 | + int[] bodyClocksSrc = new int[numEvents]; | |
1160 | + int count = -3; | |
1161 | + //nrps[17].Value = (byte)note; | |
1162 | + for ( int i = 0; i < numEvents - 1; i++ ) { | |
1163 | + count += 3; | |
1164 | + bodyEventsSrc[count] = 0xb0; | |
1165 | + bodyEventsSrc[count + 1] = nrps[i].Parameter; | |
1166 | + bodyEventsSrc[count + 2] = nrps[i].Value; | |
1167 | + bodyClocksSrc[i] = nrps[i].Clock; | |
1168 | + } | |
1169 | + count += 3; | |
1170 | + bodyEventsSrc[count] = 0xff; | |
1171 | + bodyEventsSrc[count + 1] = 0x2f; | |
1172 | + bodyEventsSrc[count + 2] = 0x00; | |
1173 | + bodyClocksSrc[numEvents - 1] = 2280; | |
1174 | + fixed ( byte* bodyEvents = &bodyEventsSrc[0] ) | |
1175 | + fixed ( int* bodyClocks = &bodyClocksSrc[0] ) { | |
1176 | + SendEvent( bodyEvents, bodyClocks, numEvents, VSTiProxyTrackType.MainTrack ); | |
1177 | + } | |
1178 | + | |
1179 | + RenderToWave( file, 2280 ); | |
1180 | + } | |
1181 | + } | |
1182 | +} |