sps_common.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. #include <windows.h>
  2. #include "resource.h"
  3. #include "..\..\..\winamp\dsp.h"
  4. #include "sps_common.h"
  5. void NSEEL_HOSTSTUB_EnterMutex() {}
  6. void NSEEL_HOSTSTUB_LeaveMutex() {}
  7. extern winampDSPModule mod;
  8. char *SPSHELP_gethelptext( int sel )
  9. {
  10. int help_id[] = { IDR_GENERAL,IDR_OPERATORS,IDR_FUNCTIONS,IDR_CONSTANTS };
  11. void *data = 0;
  12. HRSRC rsrc = FindResource( WASABI_API_LNG_HINST, MAKEINTRESOURCE( help_id[ sel ] ), "TEXT" );
  13. if ( rsrc )
  14. {
  15. HGLOBAL resourceHandle = LoadResource( WASABI_API_LNG_HINST, rsrc );
  16. data = LockResource( resourceHandle );
  17. return (char *) data;
  18. }
  19. return 0;
  20. }
  21. void SPS_initcontext( SPSEffectContext *ctx )
  22. {
  23. memset( ctx, 0, sizeof( SPSEffectContext ) );
  24. InitializeCriticalSection( &ctx->code_cs );
  25. ctx->vm_ctx = NSEEL_VM_alloc();
  26. ctx->code_needrecompile = 7;
  27. }
  28. void SPS_quitcontext( SPSEffectContext *ctx )
  29. {
  30. if ( ctx->sample_buffer ) GlobalFree( ctx->sample_buffer );
  31. ctx->sample_buffer_len = 0;
  32. ctx->sample_buffer = NULL;
  33. NSEEL_code_free( ctx->code[ 0 ] );
  34. NSEEL_code_free( ctx->code[ 1 ] );
  35. NSEEL_code_free( ctx->code[ 2 ] );
  36. memset( &ctx->code, 0, sizeof( ctx->code ) );
  37. //megabuf_cleanup( ctx->vm_ctx );
  38. NSEEL_VM_free( ctx->vm_ctx );
  39. ctx->vm_ctx = 0;
  40. memset( &ctx->vars, 0, sizeof( ctx->vars ) );
  41. DeleteCriticalSection( &ctx->code_cs );
  42. }
  43. static __inline int double2int( double v )
  44. {
  45. #if 0
  46. return (int) v;
  47. #else
  48. int a;
  49. __asm
  50. {
  51. fld v;
  52. fistp a;
  53. }
  54. return a;
  55. #endif
  56. }
  57. static __inline double int24todouble( unsigned char *int24 )
  58. {
  59. unsigned int a = ( int24[ 0 ] ) | ( int24[ 1 ] << 8 ) | ( int24[ 2 ] << 16 );
  60. if ( a & 0x800000 )
  61. a |= 0xFF000000;
  62. else
  63. a &= 0xFFFFFF;
  64. return (double) ( ( (signed int) a ) + 0.5 ) / 8388607.5;
  65. }
  66. static __inline void doubletoint24( double v, unsigned char *int24 )
  67. {
  68. v = ( v * 8388607.5 ) - 0.5;
  69. if ( v <= -8388608.0 )
  70. {
  71. int24[ 0 ] = int24[ 1 ] = 0; // 0x800000 is lowest possible value
  72. int24[ 2 ] = 0x80;
  73. }
  74. else if ( v >= 8388607.0 )
  75. {
  76. int24[ 0 ] = int24[ 1 ] = 0xff; // 0x7fffff is highest possible value
  77. int24[ 2 ] = 0x7f;
  78. }
  79. else
  80. {
  81. int a = (int) v;
  82. int24[ 0 ] = a & 0xff;
  83. int24[ 1 ] = ( a >> 8 ) & 0xff;
  84. int24[ 2 ] = ( a >> 16 ) & 0xff;
  85. }
  86. }
  87. int SPS_process_samples( SPSEffectContext *ctx,
  88. void *samples, int numsamples,
  89. int isfloat, int bps, int nch,
  90. int srate, int maxout, int minout )
  91. {
  92. // can only do 1 or 2ch for now
  93. if ( nch != 1 && nch != 2 )
  94. return numsamples;
  95. int samplepair_size = ( bps / 8 ) * nch;
  96. if ( ctx->bypass )
  97. {
  98. memset( ctx->triggers, 0, sizeof( ctx->triggers ) );
  99. return numsamples;
  100. }
  101. if ( !samples || numsamples < 1 )
  102. return numsamples;
  103. int rlen = numsamples * samplepair_size;
  104. if ( !ctx->sample_buffer || ctx->sample_buffer_len < rlen )
  105. {
  106. ctx->sample_buffer_len = rlen * 2;
  107. if ( ctx->sample_buffer )
  108. GlobalFree( ctx->sample_buffer );
  109. ctx->sample_buffer = (void *) GlobalAlloc( GMEM_FIXED, ctx->sample_buffer_len );
  110. }
  111. if ( !ctx->sample_buffer )
  112. return numsamples;
  113. int needinit = ctx->last_nch != nch || ctx->last_srate != srate;
  114. ctx->last_nch = nch;
  115. ctx->last_srate = srate;
  116. if ( ctx->code_needrecompile )
  117. {
  118. EnterCriticalSection( &ctx->code_cs );
  119. if ( ctx->code_needrecompile & 1 )
  120. {
  121. //NSEEL_VM_resetvars( ctx->vm_ctx );
  122. ctx->vars.spl0 = NSEEL_VM_regvar( ctx->vm_ctx, "spl0" );
  123. ctx->vars.spl1 = NSEEL_VM_regvar( ctx->vm_ctx, "spl1" );
  124. ctx->vars.skip = NSEEL_VM_regvar( ctx->vm_ctx, "skip" );
  125. ctx->vars.repeat = NSEEL_VM_regvar( ctx->vm_ctx, "repeat" );
  126. ctx->vars.nch = NSEEL_VM_regvar( ctx->vm_ctx, "nch" );
  127. ctx->vars.srate = NSEEL_VM_regvar( ctx->vm_ctx, "srate" );
  128. ctx->vars.sliders1 = NSEEL_VM_regvar( ctx->vm_ctx, "slider1" );
  129. ctx->vars.sliders2 = NSEEL_VM_regvar( ctx->vm_ctx, "slider2" );
  130. ctx->vars.sliders3 = NSEEL_VM_regvar( ctx->vm_ctx, "slider3" );
  131. ctx->vars.sliders4 = NSEEL_VM_regvar( ctx->vm_ctx, "slider4" );
  132. ctx->vars.trigger1 = NSEEL_VM_regvar( ctx->vm_ctx, "trig1" );
  133. ctx->vars.trigger2 = NSEEL_VM_regvar( ctx->vm_ctx, "trig2" );
  134. ctx->vars.trigger3 = NSEEL_VM_regvar( ctx->vm_ctx, "trig3" );
  135. ctx->vars.trigger4 = NSEEL_VM_regvar( ctx->vm_ctx, "trig4" );
  136. needinit = 1;
  137. NSEEL_code_free( ctx->code[ 0 ] );
  138. ctx->code[ 0 ] = NSEEL_code_compile( ctx->vm_ctx, ctx->curpreset.code_text[ 0 ], 0 );
  139. }
  140. if ( ctx->code_needrecompile & 2 )
  141. {
  142. NSEEL_code_free( ctx->code[ 1 ] );
  143. ctx->code[ 1 ] = NSEEL_code_compile( ctx->vm_ctx, ctx->curpreset.code_text[ 1 ], 0 );
  144. #ifdef DEBUG
  145. if ( !ctx->code[ 1 ] && NSEEL_code_getcodeerror( ctx->vm_ctx ) && *NSEEL_code_getcodeerror( ctx->vm_ctx ) )
  146. OutputDebugString( NSEEL_code_getcodeerror( ctx->vm_ctx ) );
  147. #endif
  148. }
  149. if ( ctx->code_needrecompile & 4 )
  150. {
  151. NSEEL_code_free( ctx->code[ 2 ] );
  152. ctx->code[ 2 ] = NSEEL_code_compile( ctx->vm_ctx, ctx->curpreset.code_text[ 2 ], 0 );
  153. ctx->sliderchange = 1;
  154. }
  155. ctx->code_needrecompile = 0;
  156. LeaveCriticalSection( &ctx->code_cs );
  157. }
  158. if ( !ctx->vars.spl0 )
  159. return numsamples;
  160. *( ctx->vars.nch ) = (double) nch;
  161. *( ctx->vars.srate ) = (double) srate;
  162. int slidech = ctx->sliderchange;
  163. ctx->sliderchange = 0;
  164. if ( ctx->triggers[ 0 ] )
  165. {
  166. ctx->triggers[ 0 ]--;
  167. *( ctx->vars.trigger1 ) = 1.0;
  168. }
  169. if ( ctx->triggers[ 1 ] )
  170. {
  171. ctx->triggers[ 1 ]--;
  172. *( ctx->vars.trigger2 ) = 1.0;
  173. }
  174. if ( ctx->triggers[ 2 ] )
  175. {
  176. ctx->triggers[ 2 ]--;
  177. *( ctx->vars.trigger3 ) = 1.0;
  178. }
  179. if ( ctx->triggers[ 3 ] )
  180. {
  181. ctx->triggers[ 3 ]--;
  182. *( ctx->vars.trigger4 ) = 1.0;
  183. }
  184. if ( needinit || slidech )
  185. {
  186. *( ctx->vars.sliders1 ) = (double) ctx->curpreset.sliderpos[ 0 ] / 1000.0;
  187. *( ctx->vars.sliders2 ) = (double) ctx->curpreset.sliderpos[ 1 ] / 1000.0;
  188. *( ctx->vars.sliders3 ) = (double) ctx->curpreset.sliderpos[ 2 ] / 1000.0;
  189. *( ctx->vars.sliders4 ) = (double) ctx->curpreset.sliderpos[ 3 ] / 1000.0;
  190. }
  191. if ( needinit )
  192. {
  193. //megabuf_cleanup( ctx->vm_ctx );
  194. NSEEL_code_execute( ctx->code[ 0 ] );
  195. }
  196. if ( needinit || slidech )
  197. {
  198. NSEEL_code_execute( ctx->code[ 2 ] );
  199. }
  200. if ( !maxout )
  201. maxout = numsamples;
  202. memcpy( ctx->sample_buffer, samples, rlen );
  203. int x = 0;
  204. int outpos = 0;
  205. while ( x < numsamples && outpos < maxout )
  206. {
  207. *( ctx->vars.skip ) = 0;
  208. *( ctx->vars.repeat ) = 0;
  209. if ( isfloat )
  210. {
  211. if ( bps == 32 )
  212. {
  213. float *sbuf_float = (float *) ctx->sample_buffer;
  214. if ( nch == 2 )
  215. {
  216. *( ctx->vars.spl0 ) = (double) sbuf_float[ x + x ];
  217. *( ctx->vars.spl1 ) = (double) sbuf_float[ x + x + 1 ];
  218. }
  219. else
  220. {
  221. *( ctx->vars.spl1 ) =
  222. *( ctx->vars.spl0 ) = (double) sbuf_float[ x ];
  223. }
  224. }
  225. else if ( bps == 64 )
  226. {
  227. double *sbuf_dbl = (double *) ctx->sample_buffer;
  228. if ( nch == 2 )
  229. {
  230. *( ctx->vars.spl0 ) = sbuf_dbl[ x + x ];
  231. *( ctx->vars.spl1 ) = sbuf_dbl[ x + x + 1 ];
  232. }
  233. else
  234. {
  235. *( ctx->vars.spl1 ) =
  236. *( ctx->vars.spl0 ) = sbuf_dbl[ x ];
  237. }
  238. }
  239. else
  240. {
  241. *( ctx->vars.spl1 ) =
  242. *( ctx->vars.spl0 ) = 0.0;
  243. }
  244. }
  245. else
  246. {
  247. if ( bps == 16 )
  248. {
  249. short *sbuf_short = (short *) ctx->sample_buffer;
  250. if ( nch == 2 )
  251. {
  252. *( ctx->vars.spl0 ) = ( sbuf_short[ x + x ] + 0.5 ) / 32767.5;
  253. *( ctx->vars.spl1 ) = ( sbuf_short[ x + x + 1 ] + 0.5 ) / 32767.5;
  254. }
  255. else
  256. {
  257. *( ctx->vars.spl1 ) =
  258. *( ctx->vars.spl0 ) = ( sbuf_short[ x ] + 0.5 ) / 32767.5;
  259. }
  260. }
  261. else if ( bps == 8 )
  262. {
  263. unsigned char *sbuf_char = (unsigned char *) ctx->sample_buffer;
  264. if ( nch == 2 )
  265. {
  266. *( ctx->vars.spl0 ) = ( sbuf_char[ x + x ] - 127.5 ) / 127.5;
  267. *( ctx->vars.spl1 ) = ( sbuf_char[ x + x + 1 ] - 127.5 ) / 127.5;
  268. }
  269. else
  270. {
  271. *( ctx->vars.spl1 ) =
  272. *( ctx->vars.spl0 ) = ( sbuf_char[ x ] - 127.5 ) / 127.5;
  273. }
  274. }
  275. else if ( bps == 24 )
  276. {
  277. unsigned char *sbuf_char = (unsigned char *) ctx->sample_buffer;
  278. if ( nch == 2 )
  279. {
  280. int x6 = ( x << 2 ) + x + x;
  281. *( ctx->vars.spl0 ) = int24todouble( sbuf_char + x6 );
  282. *( ctx->vars.spl1 ) = int24todouble( sbuf_char + x6 + 3 );
  283. }
  284. else
  285. {
  286. *( ctx->vars.spl1 ) =
  287. *( ctx->vars.spl0 ) = int24todouble( sbuf_char + x + x + x );
  288. }
  289. }
  290. else
  291. {
  292. // todo: 32 bit mode?
  293. *( ctx->vars.spl1 ) =
  294. *( ctx->vars.spl0 ) = 0.0;
  295. }
  296. }
  297. NSEEL_code_execute( ctx->code[ 1 ] );
  298. if ( *( ctx->vars.skip ) < 0.0001 || (/*samples out*/ outpos + /* samples left */ ( numsamples - x ) ) < minout )
  299. {
  300. if ( isfloat )
  301. {
  302. if ( bps == 32 )
  303. {
  304. float *samples_float = (float *) samples;
  305. if ( nch == 2 )
  306. {
  307. samples_float[ outpos + outpos ] = (float) *ctx->vars.spl0;
  308. samples_float[ outpos + outpos + 1 ] = (float) *ctx->vars.spl1;
  309. }
  310. else
  311. {
  312. samples_float[ outpos ] = (float) *ctx->vars.spl0;
  313. }
  314. }
  315. else if ( bps == 64 )
  316. {
  317. double *samples_double = (double *) samples;
  318. if ( nch == 2 )
  319. {
  320. samples_double[ outpos + outpos ] = *ctx->vars.spl0;
  321. samples_double[ outpos + outpos + 1 ] = *ctx->vars.spl1;
  322. }
  323. else
  324. {
  325. samples_double[ outpos ] = *ctx->vars.spl0;
  326. }
  327. }
  328. }
  329. else
  330. {
  331. if ( bps == 16 )
  332. {
  333. short *samples_short = (short *) samples;
  334. if ( nch == 2 )
  335. {
  336. double d = *( ctx->vars.spl0 ) * 32767.5 - 0.5;
  337. if ( d <= -32768.0 )
  338. samples_short[ outpos + outpos ] = -32768;
  339. else if ( d >= 32767.0 )
  340. samples_short[ outpos + outpos ] = 32767;
  341. else
  342. samples_short[ outpos + outpos ] = double2int( d );
  343. d = *( ctx->vars.spl1 ) * 32767.5 - 0.5;
  344. if ( d <= -32768.0 )
  345. samples_short[ outpos + outpos + 1 ] = -32768;
  346. else if ( d >= 32767.0 )
  347. samples_short[ outpos + outpos + 1 ] = 32767;
  348. else
  349. samples_short[ outpos + outpos + 1 ] = double2int( d );
  350. }
  351. else
  352. {
  353. double d = *( ctx->vars.spl0 ) * 32767.5 - 0.5;
  354. if ( d <= -32768.0 )
  355. samples_short[ outpos ] = -32768;
  356. else if ( d >= 32767.0 )
  357. samples_short[ outpos ] = 32767;
  358. else
  359. samples_short[ outpos ] = double2int( d );
  360. }
  361. }
  362. else if ( bps == 8 )
  363. {
  364. unsigned char *samples_char = (unsigned char *) samples;
  365. if ( nch == 2 )
  366. {
  367. double d = *( ctx->vars.spl0 ) * 127.5 + 127.5;
  368. if ( d <= 0.0 )
  369. samples_char[ outpos + outpos ] = 0;
  370. else if ( d >= 255.0 )
  371. samples_char[ outpos + outpos ] = 255;
  372. else
  373. samples_char[ outpos + outpos ] = double2int( d );
  374. d = *( ctx->vars.spl1 ) * 127.5 + 127.5;
  375. if ( d <= 0.0 )
  376. samples_char[ outpos + outpos + 1 ] = 0;
  377. else if ( d >= 255.0 )
  378. samples_char[ outpos + outpos + 1 ] = 255;
  379. else
  380. samples_char[ outpos + outpos + 1 ] = double2int( d );
  381. }
  382. else
  383. {
  384. double d = *( ctx->vars.spl0 ) * 127.5 + 127.5;
  385. if ( d <= 0.0 )
  386. samples_char[ outpos ] = 0;
  387. else if ( d >= 255.0 )
  388. samples_char[ outpos ] = 255;
  389. else
  390. samples_char[ outpos ] = double2int( d );
  391. }
  392. }
  393. else if ( bps == 24 )
  394. {
  395. unsigned char *samples_char = (unsigned char *) samples;
  396. if ( nch == 2 )
  397. {
  398. int op6 = outpos + outpos + ( outpos << 2 );
  399. doubletoint24( *ctx->vars.spl0, samples_char + op6 );
  400. doubletoint24( *ctx->vars.spl1, samples_char + op6 + 3 );
  401. }
  402. else
  403. {
  404. doubletoint24( *ctx->vars.spl0, samples_char + outpos + outpos + outpos );
  405. }
  406. }
  407. else
  408. {
  409. memcpy( (char *) samples + outpos * samplepair_size, (char *) ctx->sample_buffer + x * samplepair_size,
  410. samplepair_size );
  411. // todo: 24/32 bit modes
  412. }
  413. }
  414. outpos++;
  415. if ( *( ctx->vars.repeat ) < 0.0001 )
  416. x++;
  417. }
  418. else x++;
  419. }
  420. return outpos;
  421. }
  422. static void WriteInt( char *section, char *name, int value, char *fn )
  423. {
  424. char str[ 128 ];
  425. wsprintf( str, "%d", value );
  426. WritePrivateProfileString( section, name, str, fn );
  427. }
  428. void SPS_save_preset( SPSEffectContext *ctx, char *filename, char *section )
  429. {
  430. SPSPresetConfig *cfg = &ctx->curpreset;
  431. WriteInt( section, "slider1", cfg->sliderpos[ 0 ], filename );
  432. WriteInt( section, "slider2", cfg->sliderpos[ 1 ], filename );
  433. WriteInt( section, "slider3", cfg->sliderpos[ 2 ], filename );
  434. WriteInt( section, "slider4", cfg->sliderpos[ 3 ], filename );
  435. int x;
  436. for ( x = 0; x < 4; x++ )
  437. {
  438. int y;
  439. for ( y = 0; y < 3; y++ )
  440. {
  441. char buf[ 64 ];
  442. wsprintf( buf, "labels_%d_%d", x, y );
  443. WritePrivateProfileString( section, buf, cfg->slider_labels[ x ][ y ], filename );
  444. }
  445. }
  446. int s = strlen( cfg->code_text[ 0 ] );
  447. WriteInt( section, "code0_size", s, filename );
  448. WritePrivateProfileStruct( section, "code0_data", cfg->code_text[ 0 ], s, filename );
  449. s = strlen( cfg->code_text[ 1 ] );
  450. WriteInt( section, "code1_size", s, filename );
  451. WritePrivateProfileStruct( section, "code1_data", cfg->code_text[ 1 ], s, filename );
  452. s = strlen( cfg->code_text[ 2 ] );
  453. WriteInt( section, "code2_size", s, filename );
  454. WritePrivateProfileStruct( section, "code2_data", cfg->code_text[ 2 ], s, filename );
  455. }
  456. void SPS_load_preset( SPSEffectContext *ctx, char *filename, char *section )
  457. {
  458. SPSPresetConfig *cfg = &ctx->curpreset;
  459. EnterCriticalSection( &ctx->code_cs );
  460. cfg->sliderpos[ 0 ] = GetPrivateProfileInt( section, "slider1", 0, filename );
  461. cfg->sliderpos[ 1 ] = GetPrivateProfileInt( section, "slider2", 0, filename );
  462. cfg->sliderpos[ 2 ] = GetPrivateProfileInt( section, "slider3", 0, filename );
  463. cfg->sliderpos[ 3 ] = GetPrivateProfileInt( section, "slider4", 0, filename );
  464. int x;
  465. for ( x = 0; x < 4; x++ )
  466. {
  467. int y;
  468. for ( y = 0; y < 3; y++ )
  469. {
  470. char buf[ 64 ];
  471. wsprintf( buf, "labels_%d_%d", x, y );
  472. GetPrivateProfileString( section, buf, "", cfg->slider_labels[ x ][ y ], MAX_LABEL_LEN, filename );
  473. }
  474. }
  475. int s = GetPrivateProfileInt( section, "code0_size", 0, filename );
  476. cfg->code_text[ 0 ][ 0 ] = 0;
  477. if ( s > 0 && s < MAX_CODE_LEN - 1 )
  478. {
  479. if ( GetPrivateProfileStruct( section, "code0_data", cfg->code_text[ 0 ], s, filename ) )
  480. cfg->code_text[ 0 ][ s ] = 0;
  481. else
  482. cfg->code_text[ 0 ][ 0 ] = 0;
  483. }
  484. s = GetPrivateProfileInt( section, "code1_size", 0, filename );
  485. cfg->code_text[ 1 ][ 0 ] = 0;
  486. if ( s > 0 && s < MAX_CODE_LEN - 1 )
  487. {
  488. if ( GetPrivateProfileStruct( section, "code1_data", cfg->code_text[ 1 ], s, filename ) )
  489. cfg->code_text[ 1 ][ s ] = 0;
  490. else
  491. cfg->code_text[ 1 ][ 0 ] = 0;
  492. }
  493. s = GetPrivateProfileInt( section, "code2_size", 0, filename );
  494. cfg->code_text[ 2 ][ 0 ] = 0;
  495. if ( s > 0 && s < MAX_CODE_LEN - 1 )
  496. {
  497. if ( GetPrivateProfileStruct( section, "code2_data", cfg->code_text[ 2 ], s, filename ) )
  498. cfg->code_text[ 2 ][ s ] = 0;
  499. else
  500. cfg->code_text[ 2 ][ 0 ] = 0;
  501. }
  502. ctx->code_needrecompile = 7;
  503. lstrcpyn( ctx->curpreset_name, filename, sizeof( ctx->curpreset_name ) );
  504. LeaveCriticalSection( &ctx->code_cs );
  505. }
  506. void SPS_initapp()
  507. {
  508. NSEEL_init();
  509. //NSEEL_addfunctionex( "megabuf", 1, (int) _asm_megabuf, (int) _asm_megabuf_end - (int) _asm_megabuf, megabuf_ppproc );
  510. }
  511. void SPS_quitapp()
  512. {
  513. NSEEL_quit();
  514. }