1 /*
2
3 TiMidity -- Experimental MIDI to WAVE converter
4 Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 instrum.c
21
22 Code to load and unload GUS-compatible instrument patches.
23
24 */
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <math.h>
30
31 #include "timidity.h"
32 #include "m_swap.h"
33 #include "files.h"
34 #include "templates.h"
35 #include "gf1patch.h"
36
37 namespace Timidity
38 {
39
40 extern Instrument *load_instrument_dls(Renderer *song, int drum, int bank, int instrument);
41
Instrument()42 Instrument::Instrument()
43 : samples(0), sample(NULL)
44 {
45 }
46
~Instrument()47 Instrument::~Instrument()
48 {
49 Sample *sp;
50 int i;
51
52 for (i = samples, sp = &(sample[0]); i != 0; i--, sp++)
53 {
54 if (sp->type == INST_GUS && sp->data != NULL)
55 {
56 free(sp->data);
57 }
58 }
59 free(sample);
60 }
61
ToneBank()62 ToneBank::ToneBank()
63 {
64 tone = new ToneBankElement[128];;
65 for (int i = 0; i < MAXPROG; ++i)
66 {
67 instrument[i] = 0;
68 }
69 }
70
~ToneBank()71 ToneBank::~ToneBank()
72 {
73 delete[] tone;
74 for (int i = 0; i < MAXPROG; i++)
75 {
76 if (instrument[i] != NULL && instrument[i] != MAGIC_LOAD_INSTRUMENT)
77 {
78 delete instrument[i];
79 instrument[i] = NULL;
80 }
81 }
82 }
83
convert_tremolo_sweep(Renderer * song,BYTE sweep)84 int convert_tremolo_sweep(Renderer *song, BYTE sweep)
85 {
86 if (sweep == 0)
87 return 0;
88
89 return
90 int(((song->control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / (song->rate * sweep));
91 }
92
convert_vibrato_sweep(Renderer * song,BYTE sweep,int vib_control_ratio)93 int convert_vibrato_sweep(Renderer *song, BYTE sweep, int vib_control_ratio)
94 {
95 if (sweep == 0)
96 return 0;
97
98 return
99 (int) (FSCALE((double) (vib_control_ratio) * SWEEP_TUNING, SWEEP_SHIFT) / (song->rate * sweep));
100
101 /* this was overflowing with seashore.pat
102
103 ((vib_control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / (song->rate * sweep);
104 */
105 }
106
convert_tremolo_rate(Renderer * song,BYTE rate)107 int convert_tremolo_rate(Renderer *song, BYTE rate)
108 {
109 return
110 int(((song->control_ratio * rate) << RATE_SHIFT) / (TREMOLO_RATE_TUNING * song->rate));
111 }
112
convert_vibrato_rate(Renderer * song,BYTE rate)113 int convert_vibrato_rate(Renderer *song, BYTE rate)
114 {
115 /* Return a suitable vibrato_control_ratio value */
116 return
117 int((VIBRATO_RATE_TUNING * song->rate) / (rate * 2 * VIBRATO_SAMPLE_INCREMENTS));
118 }
119
reverse_data(sample_t * sp,int ls,int le)120 static void reverse_data(sample_t *sp, int ls, int le)
121 {
122 sample_t s, *ep = sp + le;
123 sp += ls;
124 le -= ls;
125 le /= 2;
126 while (le--)
127 {
128 s = *sp;
129 *sp++ = *ep;
130 *ep-- = s;
131 }
132 }
133
134 /*
135 If panning or note_to_use != -1, it will be used for all samples,
136 instead of the sample-specific values in the instrument file.
137
138 For note_to_use, any value <0 or >127 will be forced to 0.
139
140 For other parameters, 1 means yes, 0 means no, other values are
141 undefined.
142
143 TODO: do reverse loops right */
load_instrument(Renderer * song,const char * name,int percussion,int panning,int note_to_use,int strip_loop,int strip_envelope,int strip_tail)144 static Instrument *load_instrument(Renderer *song, const char *name, int percussion,
145 int panning, int note_to_use,
146 int strip_loop, int strip_envelope,
147 int strip_tail)
148 {
149 Instrument *ip;
150 Sample *sp;
151 FileReader *fp;
152 GF1PatchHeader header;
153 GF1InstrumentData idata;
154 GF1LayerData layer_data;
155 GF1PatchData patch_data;
156 int i, j;
157 bool noluck = false;
158
159 if (!name) return 0;
160
161 /* Open patch file */
162 if ((fp = pathExpander.openFileReader(name, NULL)) == NULL)
163 {
164 /* Try with various extensions */
165 FString tmp = name;
166 tmp += ".pat";
167 if ((fp = pathExpander.openFileReader(tmp, NULL)) == NULL)
168 {
169 #ifdef __unix__ // Windows isn't case-sensitive.
170 tmp.ToUpper();
171 if ((fp = pathExpander.openFileReader(tmp, NULL)) == NULL)
172 #endif
173 {
174 noluck = true;
175 }
176 }
177 }
178
179 if (noluck)
180 {
181 cmsg(CMSG_ERROR, VERB_NORMAL, "Instrument `%s' can't be found.\n", name);
182 return 0;
183 }
184
185 cmsg(CMSG_INFO, VERB_NOISY, "Loading instrument %s\n", name);
186
187 /* Read some headers and do cursory sanity checks. */
188
189 if (sizeof(header) != fp->Read(&header, sizeof(header)))
190 {
191 failread:
192 cmsg(CMSG_ERROR, VERB_NORMAL, "%s: Error reading instrument.\n", name);
193 delete fp;
194 return 0;
195 }
196 if (strncmp(header.Header, GF1_HEADER_TEXT, HEADER_SIZE - 4) != 0)
197 {
198 cmsg(CMSG_ERROR, VERB_NORMAL, "%s: Not an instrument.\n", name);
199 delete fp;
200 return 0;
201 }
202 if (strcmp(header.Header + 8, "110") < 0)
203 {
204 cmsg(CMSG_ERROR, VERB_NORMAL, "%s: Is an old and unsupported patch version.\n", name);
205 delete fp;
206 return 0;
207 }
208 if (sizeof(idata) != fp->Read(&idata, sizeof(idata)))
209 {
210 goto failread;
211 }
212
213 header.WaveForms = LittleShort(header.WaveForms);
214 header.MasterVolume = LittleShort(header.MasterVolume);
215 header.DataSize = LittleLong(header.DataSize);
216 idata.Instrument = LittleShort(idata.Instrument);
217
218 if (header.Instruments != 1 && header.Instruments != 0) /* instruments. To some patch makers, 0 means 1 */
219 {
220 cmsg(CMSG_ERROR, VERB_NORMAL, "Can't handle patches with %d instruments.\n", header.Instruments);
221 delete fp;
222 return 0;
223 }
224
225 if (idata.Layers != 1 && idata.Layers != 0) /* layers. What's a layer? */
226 {
227 cmsg(CMSG_ERROR, VERB_NORMAL, "Can't handle instruments with %d layers.\n", idata.Layers);
228 delete fp;
229 return 0;
230 }
231
232 if (sizeof(layer_data) != fp->Read(&layer_data, sizeof(layer_data)))
233 {
234 goto failread;
235 }
236
237 if (layer_data.Samples == 0)
238 {
239 cmsg(CMSG_ERROR, VERB_NORMAL, "Instrument has 0 samples.\n");
240 delete fp;
241 return 0;
242 }
243
244 ip = new Instrument;
245 ip->samples = layer_data.Samples;
246 ip->sample = (Sample *)safe_malloc(sizeof(Sample) * layer_data.Samples);
247 memset(ip->sample, 0, sizeof(Sample) * layer_data.Samples);
248 for (i = 0; i < layer_data.Samples; ++i)
249 {
250 if (sizeof(patch_data) != fp->Read(&patch_data, sizeof(patch_data)))
251 {
252 fail:
253 cmsg(CMSG_ERROR, VERB_NORMAL, "Error reading sample %d.\n", i);
254 delete ip;
255 delete fp;
256 return 0;
257 }
258
259 sp = &(ip->sample[i]);
260
261 sp->data_length = LittleLong(patch_data.WaveSize);
262 sp->loop_start = LittleLong(patch_data.StartLoop);
263 sp->loop_end = LittleLong(patch_data.EndLoop);
264 sp->sample_rate = LittleShort(patch_data.SampleRate);
265 sp->low_freq = float(LittleLong(patch_data.LowFrequency));
266 sp->high_freq = float(LittleLong(patch_data.HighFrequency)) + 0.9999f;
267 sp->root_freq = float(LittleLong(patch_data.RootFrequency));
268 sp->high_vel = 127;
269 sp->velocity = -1;
270 sp->type = INST_GUS;
271
272 // Expand to SF2 range.
273 if (panning == -1)
274 {
275 sp->panning = (patch_data.Balance & 0x0F) * 1000 / 15 - 500;
276 }
277 else
278 {
279 sp->panning = (panning & 0x7f) * 1000 / 127 - 500;
280 }
281 song->compute_pan((sp->panning + 500) / 1000.0, INST_GUS, sp->left_offset, sp->right_offset);
282
283 /* tremolo */
284 if (patch_data.TremoloRate == 0 || patch_data.TremoloDepth == 0)
285 {
286 sp->tremolo_sweep_increment = 0;
287 sp->tremolo_phase_increment = 0;
288 sp->tremolo_depth = 0;
289 cmsg(CMSG_INFO, VERB_DEBUG, " * no tremolo\n");
290 }
291 else
292 {
293 sp->tremolo_sweep_increment = convert_tremolo_sweep(song, patch_data.TremoloSweep);
294 sp->tremolo_phase_increment = convert_tremolo_rate(song, patch_data.TremoloRate);
295 sp->tremolo_depth = patch_data.TremoloDepth;
296 cmsg(CMSG_INFO, VERB_DEBUG, " * tremolo: sweep %d, phase %d, depth %d\n",
297 sp->tremolo_sweep_increment, sp->tremolo_phase_increment, sp->tremolo_depth);
298 }
299
300 /* vibrato */
301 if (patch_data.VibratoRate == 0 || patch_data.VibratoDepth == 0)
302 {
303 sp->vibrato_sweep_increment = 0;
304 sp->vibrato_control_ratio = 0;
305 sp->vibrato_depth = 0;
306 cmsg(CMSG_INFO, VERB_DEBUG, " * no vibrato\n");
307 }
308 else
309 {
310 sp->vibrato_control_ratio = convert_vibrato_rate(song, patch_data.VibratoRate);
311 sp->vibrato_sweep_increment = convert_vibrato_sweep(song, patch_data.VibratoSweep, sp->vibrato_control_ratio);
312 sp->vibrato_depth = patch_data.VibratoDepth;
313 cmsg(CMSG_INFO, VERB_DEBUG, " * vibrato: sweep %d, ctl %d, depth %d\n",
314 sp->vibrato_sweep_increment, sp->vibrato_control_ratio, sp->vibrato_depth);
315 }
316
317 sp->modes = patch_data.Modes;
318
319 /* Mark this as a fixed-pitch instrument if such a deed is desired. */
320 if (note_to_use != -1)
321 {
322 sp->scale_note = note_to_use;
323 sp->scale_factor = 0;
324 }
325 else
326 {
327 sp->scale_note = LittleShort(patch_data.ScaleFrequency);
328 sp->scale_factor = LittleShort(patch_data.ScaleFactor);
329 if (sp->scale_factor <= 2)
330 {
331 sp->scale_factor *= 1024;
332 }
333 else if (sp->scale_factor > 2048)
334 {
335 sp->scale_factor = 1024;
336 }
337 if (sp->scale_factor != 1024)
338 {
339 cmsg(CMSG_INFO, VERB_DEBUG, " * Scale: note %d, factor %d\n",
340 sp->scale_note, sp->scale_factor);
341 }
342 }
343
344 #if 0
345 /* seashore.pat in the Midia patch set has no Sustain. I don't
346 understand why, and fixing it by adding the Sustain flag to
347 all looped patches probably breaks something else. We do it
348 anyway. */
349
350 if (sp->modes & PATCH_LOOPEN)
351 {
352 sp->modes |= PATCH_SUSTAIN;
353 }
354 #endif
355 /* [RH] Alas, eawpats has percussion instruments with bad envelopes. :(
356 * (See cymchina.pat for one example of this sadness.)
357 * Do this logic for instruments without a description, only. Hopefully that
358 * catches all the patches that need it without including any extra.
359 */
360 for (j = 0; j < DESC_SIZE; ++j)
361 {
362 if (header.Description[j] != 0)
363 break;
364 }
365 /* Strip any loops and envelopes we're permitted to */
366 /* [RH] (But PATCH_BACKWARD isn't a loop flag at all!) */
367 if ((strip_loop == 1) &&
368 (sp->modes & (PATCH_SUSTAIN | PATCH_LOOPEN | PATCH_BIDIR | PATCH_BACKWARD)))
369 {
370 cmsg(CMSG_INFO, VERB_DEBUG, " - Removing loop and/or sustain\n");
371 if (j == DESC_SIZE)
372 {
373 sp->modes &= ~(PATCH_SUSTAIN | PATCH_LOOPEN | PATCH_BIDIR | PATCH_BACKWARD);
374 }
375 }
376
377 if (strip_envelope == 1)
378 {
379 cmsg(CMSG_INFO, VERB_DEBUG, " - Removing envelope\n");
380 /* [RH] The envelope isn't really removed, but this is the way the standard
381 * Gravis patches get that effect: All rates at maximum, and all offsets at
382 * a constant level.
383 */
384 if (j == DESC_SIZE)
385 {
386 int k;
387 for (k = 1; k < ENVELOPES; ++k)
388 { /* Find highest offset. */
389 if (patch_data.EnvelopeOffset[k] > patch_data.EnvelopeOffset[0])
390 {
391 patch_data.EnvelopeOffset[0] = patch_data.EnvelopeOffset[k];
392 }
393 }
394 for (k = 0; k < ENVELOPES; ++k)
395 {
396 patch_data.EnvelopeRate[k] = 63;
397 patch_data.EnvelopeOffset[k] = patch_data.EnvelopeOffset[0];
398 }
399 }
400 }
401
402 for (j = 0; j < 6; j++)
403 {
404 sp->envelope.gf1.rate[j] = patch_data.EnvelopeRate[j];
405 /* [RH] GF1NEW clamps the offsets to the range [5,251], so we do too. */
406 sp->envelope.gf1.offset[j] = clamp<BYTE>(patch_data.EnvelopeOffset[j], 5, 251);
407 }
408
409 /* Then read the sample data */
410 if (((sp->modes & PATCH_16) && sp->data_length/2 > MAX_SAMPLE_SIZE) ||
411 (!(sp->modes & PATCH_16) && sp->data_length > MAX_SAMPLE_SIZE))
412 {
413 goto fail;
414 }
415 sp->data = (sample_t *)safe_malloc(sp->data_length);
416
417 if (sp->data_length != fp->Read(sp->data, sp->data_length))
418 goto fail;
419
420 convert_sample_data(sp, sp->data);
421
422 /* Reverse reverse loops and pass them off as normal loops */
423 if (sp->modes & PATCH_BACKWARD)
424 {
425 int t;
426 /* The GUS apparently plays reverse loops by reversing the
427 whole sample. We do the same because the GUS does not SUCK. */
428
429 cmsg(CMSG_WARNING, VERB_NORMAL, "Reverse loop in %s\n", name);
430 reverse_data((sample_t *)sp->data, 0, sp->data_length);
431 sp->data[sp->data_length] = sp->data[sp->data_length - 1];
432
433 t = sp->loop_start;
434 sp->loop_start = sp->data_length - sp->loop_end;
435 sp->loop_end = sp->data_length - t;
436
437 sp->modes &= ~PATCH_BACKWARD;
438 sp->modes |= PATCH_LOOPEN; /* just in case */
439 }
440
441 /* Then fractional samples */
442 sp->data_length <<= FRACTION_BITS;
443 sp->loop_start <<= FRACTION_BITS;
444 sp->loop_end <<= FRACTION_BITS;
445
446 /* Adjust for fractional loop points. */
447 sp->loop_start |= (patch_data.Fractions & 0x0F) << (FRACTION_BITS-4);
448 sp->loop_end |= (patch_data.Fractions & 0xF0) << (FRACTION_BITS-4-4);
449
450 /* If this instrument will always be played on the same note,
451 and it's not looped, we can resample it now. */
452 if (sp->scale_factor == 0 && !(sp->modes & PATCH_LOOPEN))
453 {
454 pre_resample(song, sp);
455 }
456
457 if (strip_tail == 1)
458 {
459 /* Let's not really, just say we did. */
460 cmsg(CMSG_INFO, VERB_DEBUG, " - Stripping tail\n");
461 sp->data_length = sp->loop_end;
462 }
463 }
464 delete fp;
465 return ip;
466 }
467
convert_sample_data(Sample * sp,const void * data)468 void convert_sample_data(Sample *sp, const void *data)
469 {
470 /* convert everything to 32-bit floating point data */
471 sample_t *newdata = NULL;
472
473 switch (sp->modes & (PATCH_16 | PATCH_UNSIGNED))
474 {
475 case 0:
476 { /* 8-bit, signed */
477 SBYTE *cp = (SBYTE *)data;
478 newdata = (sample_t *)safe_malloc((sp->data_length + 1) * sizeof(sample_t));
479 for (int i = 0; i < sp->data_length; ++i)
480 {
481 if (cp[i] < 0)
482 {
483 newdata[i] = float(cp[i]) / 128.f;
484 }
485 else
486 {
487 newdata[i] = float(cp[i]) / 127.f;
488 }
489 }
490 break;
491 }
492
493 case PATCH_UNSIGNED:
494 { /* 8-bit, unsigned */
495 BYTE *cp = (BYTE *)data;
496 newdata = (sample_t *)safe_malloc((sp->data_length + 1) * sizeof(sample_t));
497 for (int i = 0; i < sp->data_length; ++i)
498 {
499 int c = cp[i] - 128;
500 if (c < 0)
501 {
502 newdata[i] = float(c) / 128.f;
503 }
504 else
505 {
506 newdata[i] = float(c) / 127.f;
507 }
508 }
509 break;
510 }
511
512 case PATCH_16:
513 { /* 16-bit, signed */
514 SWORD *cp = (SWORD *)data;
515 /* Convert these to samples */
516 sp->data_length >>= 1;
517 sp->loop_start >>= 1;
518 sp->loop_end >>= 1;
519 newdata = (sample_t *)safe_malloc((sp->data_length + 1) * sizeof(sample_t));
520 for (int i = 0; i < sp->data_length; ++i)
521 {
522 int c = LittleShort(cp[i]);
523 if (c < 0)
524 {
525 newdata[i] = float(c) / 32768.f;
526 }
527 else
528 {
529 newdata[i] = float(c) / 32767.f;
530 }
531 }
532 break;
533 }
534
535 case PATCH_16 | PATCH_UNSIGNED:
536 { /* 16-bit, unsigned */
537 WORD *cp = (WORD *)data;
538 /* Convert these to samples */
539 sp->data_length >>= 1;
540 sp->loop_start >>= 1;
541 sp->loop_end >>= 1;
542 newdata = (sample_t *)safe_malloc((sp->data_length + 1) * sizeof(sample_t));
543 for (int i = 0; i < sp->data_length; ++i)
544 {
545 int c = LittleShort(cp[i]) - 32768;
546 if (c < 0)
547 {
548 newdata[i] = float(c) / 32768.f;
549 }
550 else
551 {
552 newdata[i] = float(c) / 32767.f;
553 }
554 }
555 break;
556 }
557 }
558 /* Duplicate the final sample for linear interpolation. */
559 newdata[sp->data_length] = newdata[sp->data_length - 1];
560 if (sp->data != NULL)
561 {
562 free(sp->data);
563 }
564 sp->data = newdata;
565 }
566
fill_bank(Renderer * song,int dr,int b)567 static int fill_bank(Renderer *song, int dr, int b)
568 {
569 int i, errors = 0;
570 ToneBank *bank = ((dr) ? drumset[b] : tonebank[b]);
571 if (bank == NULL)
572 {
573 cmsg(CMSG_ERROR, VERB_NORMAL,
574 "Huh. Tried to load instruments in non-existent %s %d\n",
575 (dr) ? "drumset" : "tone bank", b);
576 return 0;
577 }
578 for (i = 0; i < MAXPROG; i++)
579 {
580 if (bank->instrument[i] == MAGIC_LOAD_INSTRUMENT)
581 {
582 bank->instrument[i] = NULL;
583 bank->instrument[i] = load_instrument_dls(song, dr, b, i);
584 if (bank->instrument[i] != NULL)
585 {
586 continue;
587 }
588 Instrument *ip;
589 ip = load_instrument_font_order(song, 0, dr, b, i);
590 if (ip == NULL)
591 {
592 if (bank->tone[i].fontbank >= 0)
593 {
594 ip = load_instrument_font(song, bank->tone[i].name, dr, b, i);
595 }
596 else
597 {
598 ip = load_instrument(song, bank->tone[i].name,
599 (dr) ? 1 : 0,
600 bank->tone[i].pan,
601 (bank->tone[i].note != -1) ? bank->tone[i].note : ((dr) ? i : -1),
602 (bank->tone[i].strip_loop != -1) ? bank->tone[i].strip_loop : ((dr) ? 1 : -1),
603 (bank->tone[i].strip_envelope != -1) ? bank->tone[i].strip_envelope : ((dr) ? 1 : -1),
604 bank->tone[i].strip_tail);
605 }
606 if (ip == NULL)
607 {
608 ip = load_instrument_font_order(song, 1, dr, b, i);
609 }
610 }
611 bank->instrument[i] = ip;
612 if (ip == NULL)
613 {
614 if (bank->tone[i].name.IsEmpty())
615 {
616 cmsg(CMSG_WARNING, (b != 0) ? VERB_VERBOSE : VERB_NORMAL,
617 "No instrument mapped to %s %d, program %d%s\n",
618 (dr) ? "drum set" : "tone bank", b, i,
619 (b != 0) ? "" : " - this instrument will not be heard");
620 }
621 else
622 {
623 cmsg(CMSG_ERROR, VERB_NORMAL,
624 "Couldn't load instrument %s (%s %d, program %d)\n",
625 bank->tone[i].name.GetChars(),
626 (dr) ? "drum set" : "tone bank", b, i);
627 }
628 if (b != 0)
629 {
630 /* Mark the corresponding instrument in the default
631 bank / drumset for loading (if it isn't already) */
632 if (((dr) ? drumset[0] : tonebank[0])->instrument[i] != NULL)
633 {
634 ((dr) ? drumset[0] : tonebank[0])->instrument[i] = MAGIC_LOAD_INSTRUMENT;
635 }
636 }
637 errors++;
638 }
639 }
640 }
641 return errors;
642 }
643
load_missing_instruments()644 int Renderer::load_missing_instruments()
645 {
646 int i = MAXBANK, errors = 0;
647 while (i--)
648 {
649 if (tonebank[i] != NULL)
650 errors += fill_bank(this, 0,i);
651 if (drumset[i] != NULL)
652 errors += fill_bank(this, 1,i);
653 }
654 return errors;
655 }
656
free_instruments()657 void free_instruments()
658 {
659 int i = MAXBANK;
660 while (i--)
661 {
662 if (tonebank[i] != NULL)
663 {
664 delete tonebank[i];
665 tonebank[i] = NULL;
666 }
667 if (drumset[i] != NULL)
668 {
669 delete drumset[i];
670 drumset[i] = NULL;
671 }
672 }
673 }
674
set_default_instrument(const char * name)675 int Renderer::set_default_instrument(const char *name)
676 {
677 Instrument *ip;
678 if ((ip = load_instrument(this, name, 0, -1, -1, 0, 0, 0)) == NULL)
679 {
680 return -1;
681 }
682 if (default_instrument != NULL)
683 {
684 delete default_instrument;
685 }
686 default_instrument = ip;
687 default_program = SPECIAL_PROGRAM;
688 return 0;
689 }
690
691 }
692