1 /*
2 Copyright (C) 2005 The Pentagram team
3 Copyright (C) 2010-2013 The Exult Team
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program 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. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 #include "pent_include.h"
21
22 #include "AudioChannel.h"
23 #include "AudioSample.h"
24
25 #include <cstring>
26 namespace Pentagram {
27
ReadSample(uint8 * src)28 sint16 ReadSample(uint8 *src) {
29 sint16 val;
30 std::memcpy(&val, src, sizeof(sint16));
31 return val;
32 }
33
34 // We divide the data by 2, to prevent overshots. Imagine this sample pattern:
35 // 0, 65535, 65535, 0. Now you want to compute a value between the two 65535.
36 // Obviously, it will be *bigger* than 65535 (it can get to about 80,000).
37 // It is possibly to clamp it, but that leads to a distored wave form. Compare
38 // this to turning up the volume of your stereo to much, it will start to sound
39 // bad at a certain level (depending on the power of your stereo, your speakers
40 // etc, this can be quite loud, though ;-). Hence we reduce the original range.
41 // A factor of roughly 1/1.2 = 0.8333 is sufficient. Since we want to avoid
42 // floating point, we approximate that by 27/32
43 #define RANGE_REDUX(x) (((x) * 27) >> 5)
44
AudioChannel(uint32 sample_rate_,bool stereo_)45 AudioChannel::AudioChannel(uint32 sample_rate_, bool stereo_) :
46 sample_rate(sample_rate_), stereo(stereo_)
47 {
48 }
49
~AudioChannel()50 AudioChannel::~AudioChannel()
51 {
52 if (sample && playdata) sample->freeDecompressor(decomp);
53 if (sample) sample->Release();
54 playdata.reset();
55 sample = nullptr;
56 }
57
playSample(AudioSample * sample_,int loop_,int priority_,bool paused_,uint32 pitch_shift_,int lvol_,int rvol_,sint32 instance_id_)58 void AudioChannel::playSample(AudioSample *sample_, int loop_, int priority_, bool paused_, uint32 pitch_shift_, int lvol_, int rvol_, sint32 instance_id_)
59 {
60 stop();
61 sample = sample_;
62
63 loop = loop_;
64 priority = priority_;
65 lvol = lvol_;
66 rvol = rvol_;
67 paused = paused_;
68 pitch_shift = pitch_shift_;
69 instance_id = instance_id_;
70 distance = 0;
71 balance = 0;
72
73 if (!sample) return;
74 sample->IncRef();
75
76 // Lambda to round size up to next multiple of maximum alignment size, if needed.
77 auto round_up = [](size_t val) {
78 constexpr const size_t maxalign = alignof(std::max_align_t);
79 return ((val + maxalign - 1) / maxalign) * maxalign;
80 };
81 // Persistent data for the decompressor
82 const size_t decompressor_size = round_up(sample->getDecompressorDataSize());
83 const size_t frame_size = round_up(sample->getFrameSize());
84 const size_t decompressor_align = sample->getDecompressorAlignment();
85
86 // Setup buffers
87 if ((decompressor_size + frame_size * 2) > playdata_size)
88 {
89 playdata_size = decompressor_size + frame_size * 2 + decompressor_align;
90 playdata = std::make_unique<uint8[]>(playdata_size);
91 }
92
93 size_t avail_space = playdata_size;
94 decomp = playdata.get();
95 // Align decomp to required alignment.
96 std::align(decompressor_align, decompressor_size, decomp, avail_space);
97 // Align first frame.
98 size_t delta = playdata_size - avail_space;
99 avail_space -= decompressor_size;
100 void *frameptr = playdata.get() + delta + decompressor_size;
101 std::align(alignof(std::max_align_t), frame_size, frameptr, avail_space);
102 frames[0] = static_cast<uint8*>(frameptr);
103 // Second frame should be aligned for free.
104 frames[1] = frames[0] + frame_size;
105
106 // Init the sample decompressor
107 sample->initDecompressor(decomp);
108
109 // Reset counter and stuff
110 frame_evenodd = 0;
111 position = 0;
112 fp_pos = 0;
113 fp_speed = (pitch_shift*sample->getRate())/sample_rate;
114
115 // Decompress frame 0
116 frame0_size = sample->decompressFrame(decomp, frames[0]);
117
118 // Decompress frame 1
119 DecompressNextFrame();
120
121 // Setup resampler
122 if (sample->getBits()==8 && !sample->isStereo())
123 {
124 uint8 *src = frames[0];
125 int a = *(src+0); a = (a|(a << 8))-32768;
126 int b = *(src+1); b = (a|(b << 8))-32768;
127 int c = *(src+2); c = (a|(c << 8))-32768;
128
129 interp_l.init(RANGE_REDUX(a),RANGE_REDUX(b),RANGE_REDUX(c));
130 }
131
132 }
133
resampleAndMix(sint16 * stream,uint32 bytes)134 void AudioChannel::resampleAndMix(sint16 *stream, uint32 bytes)
135 {
136 if (!sample || paused)
137 return;
138
139 // Update fp_speed
140 fp_speed = (pitch_shift*sample->getRate())/sample_rate;
141
142 // Get and Mix data
143 do
144 {
145 // 8 bit resampling
146 if (sample->getBits()==8) {
147 if (!sample->isStereo() && stereo)
148 resampleFrameM8toS(stream,bytes); // Mono Sample to Stereo Output
149 else if (!sample->isStereo() && !stereo)
150 resampleFrameM8toM(stream,bytes); // Mono Sample to Stereo Output
151 else if (sample->isStereo() && !stereo)
152 resampleFrameS8toM(stream,bytes); // Stereo Sample to Mono Output
153 else
154 resampleFrameS8toS(stream,bytes); // Stereo Sample to Stereo Output
155 }
156 else if (sample->getBits()==16) {
157 if (!sample->isStereo() && stereo)
158 resampleFrameM16toS(stream,bytes); // Mono Sample to Stereo Output
159 else if (!sample->isStereo() && !stereo)
160 resampleFrameM16toM(stream,bytes); // Mono Sample to Mono Output
161 else if (sample->isStereo() && !stereo)
162 resampleFrameS16toM(stream,bytes); // Stereo Sample to Mono Output
163 else
164 resampleFrameS16toS(stream,bytes); // Stereo Sample to Stereo Output
165 }
166
167 // We ran out of data
168 if (bytes || (position == frame0_size)) {
169
170 // No more data
171 if (!frame1_size) {
172 if (sample) sample->Release();
173 sample = nullptr;
174 return;
175 }
176
177 // Invert evenodd
178 frame_evenodd = 1-frame_evenodd;
179
180 // Set frame1 to be frame0
181 frame0_size = frame1_size;
182 position = 0;
183
184 DecompressNextFrame();
185 }
186
187 } while (bytes!=0);
188 }
189
190 // Decompress a frame
DecompressNextFrame()191 void AudioChannel::DecompressNextFrame()
192 {
193 // Get next frame of data
194 uint8 *src2 = frames[1-frame_evenodd];
195 frame1_size = sample->decompressFrame(decomp, src2);
196
197 // No stream, go back to beginning and get first frame
198 if (!frame1_size && loop) {
199 if (loop != -1) loop--;
200 sample->rewind(decomp);
201 frame1_size = sample->decompressFrame(decomp, src2);
202 }
203 else if (!frame1_size)
204 {
205 sample->freeDecompressor(decomp);
206 }
207 }
208
209 //
210 // 8 Bit
211 //
212
213 // Resample a frame of mono 8bit unsigned to Stereo 16bit
resampleFrameM8toS(sint16 * & stream,uint32 & bytes)214 void AudioChannel::resampleFrameM8toS(sint16 *&stream, uint32 &bytes)
215 {
216 uint8 *src = frames[frame_evenodd];
217 uint8 *src2 = frames[1-frame_evenodd];
218
219 uint8 *src_end = src + frame0_size;
220 uint8 *src2_end = src2 + frame1_size;
221
222 src += position;
223
224 int lvol = this->lvol;
225 int rvol = this->rvol;
226
227 calculate2DVolume(lvol,rvol);
228
229 do {
230 // Add a new src sample (if required)
231 if (fp_pos >= 0x10000)
232 {
233 if (src+2 < src_end) {
234 int c = *(src+2);
235 c = (c|(c << 8))-32768;
236 interp_l.feedData(c);
237 } else if (src2 < src2_end) {
238 int c = *(src2);
239 c = (c|(c << 8))-32768;
240 interp_l.feedData(c);
241 src2++;
242 } else {
243 interp_l.feedData();
244 }
245 src++;
246 fp_pos -= 0x10000;
247 }
248
249 if (fp_pos < 0x10000) do {
250 // Do the interpolation
251 int result = interp_l.interpolate(fp_pos);
252
253 int lresult = *(stream+0) + (result*lvol)/256;
254 int rresult = *(stream+1) + (result*rvol)/256;
255
256 // Enforce range in case of an "overshot". Shouldn't happen since we
257 // scale down already, but safe is safe.
258 if (lresult < -32768) lresult = -32768;
259 else if (lresult > 32767) lresult = 32767;
260
261 if (rresult < -32768) rresult = -32768;
262 else if (rresult > 32767) rresult = 32767;
263
264 *stream++ = lresult;
265 *stream++ = rresult;
266 bytes -= 4;
267 fp_pos += fp_speed;
268
269 } while (fp_pos < 0x10000 && bytes!=0);
270
271 } while (bytes!=0 && src != src_end);
272
273 position = frame0_size - (src_end - src);
274 }
275
276 // Resample a frame of mono 8bit unsigned to Mono 16bit
resampleFrameM8toM(sint16 * & stream,uint32 & bytes)277 void AudioChannel::resampleFrameM8toM(sint16 *&stream, uint32 &bytes)
278 {
279 uint8 *src = frames[frame_evenodd];
280 uint8 *src2 = frames[1-frame_evenodd];
281
282 uint8 *src_end = src + frame0_size;
283 uint8 *src2_end = src2 + frame1_size;
284
285 src += position;
286
287 int lvol = this->lvol;
288 int rvol = this->rvol;
289
290 calculate2DVolume(lvol,rvol);
291
292 int volume = (rvol + lvol)/2;
293
294 do {
295 // Add a new src sample (if required)
296 if (fp_pos >= 0x10000)
297 {
298 if (src+2 < src_end) {
299 int c = *(src+2);
300 c = (c|(c << 8))-32768;
301 interp_l.feedData(c);
302 } else if (src2 < src2_end) {
303 int c = *(src2);
304 c = (c|(c << 8))-32768;
305 interp_l.feedData(c);
306 src2++;
307 } else {
308 interp_l.feedData();
309 }
310 src++;
311 fp_pos -= 0x10000;
312 }
313
314 if (fp_pos < 0x10000) do {
315 // Do the interpolation
316 int result = (interp_l.interpolate(fp_pos)*volume)/256;
317
318 result += *stream;
319
320 // Enforce range in case of an "overshot". Shouldn't happen since we
321 // scale down already, but safe is safe.
322 if (result < -32768) result = -32768;
323 else if (result > 32767) result = 32767;
324
325 *stream++ = result;
326 bytes -= 2;
327 fp_pos += fp_speed;
328
329 } while (fp_pos < 0x10000 && bytes!=0);
330
331 } while (bytes!=0 && src != src_end);
332
333 position = frame0_size - (src_end - src);
334 }
335
336 // Resample a frame of stereo 8bit unsigned to Mono 16bit
resampleFrameS8toM(sint16 * & stream,uint32 & bytes)337 void AudioChannel::resampleFrameS8toM(sint16 *&stream, uint32 &bytes)
338 {
339 uint8 *src = frames[frame_evenodd];
340 uint8 *src2 = frames[1-frame_evenodd];
341
342 uint8 *src_end = src + frame0_size;
343 uint8 *src2_end = src2 + frame1_size;
344
345 src += position;
346
347 int lvol = this->lvol;
348 int rvol = this->rvol;
349
350 calculate2DVolume(lvol,rvol);
351
352 do {
353 // Add a new src sample (if required)
354 if (fp_pos >= 0x10000)
355 {
356 if (src+4 < src_end) {
357 int c = *(src+4);
358 int c2 = *(src+5);
359 interp_l.feedData((c|(c << 8))-32768);
360 interp_r.feedData((c2|(c2 << 8))-32768);
361 } else if (src2 < src2_end) {
362 int c = *(src2);
363 int c2 = *(src2+1);
364 interp_l.feedData((c|(c << 8))-32768);
365 interp_r.feedData((c2|(c2 << 8))-32768);
366 src2+=2;
367 } else {
368 interp_l.feedData();
369 interp_r.feedData();
370 }
371 src+=2;
372 fp_pos -= 0x10000;
373 }
374
375 if (fp_pos < 0x10000) do {
376 // Do the interpolation
377 int result = ((interp_l.interpolate(fp_pos)*lvol+interp_r.interpolate(fp_pos)*rvol))/512;
378
379 result += *stream;
380
381 // Enforce range in case of an "overshot". Shouldn't happen since we
382 // scale down already, but safe is safe.
383 if (result < -32768) result = -32768;
384 else if (result > 32767) result = 32767;
385
386 *stream++ = result;
387 bytes -= 2;
388 fp_pos += fp_speed;
389
390 } while (fp_pos < 0x10000 && bytes!=0);
391
392 } while (bytes!=0 && src != src_end);
393
394 position = frame0_size - (src_end - src);
395 }
396
397 // Resample a frame of stereo 8bit unsigned to Stereo 16bit
resampleFrameS8toS(sint16 * & stream,uint32 & bytes)398 void AudioChannel::resampleFrameS8toS(sint16 *&stream, uint32 &bytes)
399 {
400 uint8 *src = frames[frame_evenodd];
401 uint8 *src2 = frames[1-frame_evenodd];
402
403 uint8 *src_end = src + frame0_size;
404 uint8 *src2_end = src2 + frame1_size;
405
406 src += position;
407
408 int lvol = this->lvol;
409 int rvol = this->rvol;
410
411 calculate2DVolume(lvol,rvol);
412
413 do {
414 // Add a new src sample (if required)
415 if (fp_pos >= 0x10000)
416 {
417 if (src+4 < src_end) {
418 int c = *(src+4);
419 int c2 = *(src+5);
420 interp_l.feedData((c|(c << 8))-32768);
421 interp_r.feedData((c2|(c2 << 8))-32768);
422 } else if (src2 < src2_end) {
423 int c = *(src2);
424 int c2 = *(src2+1);
425 interp_l.feedData((c|(c << 8))-32768);
426 interp_r.feedData((c2|(c2 << 8))-32768);
427 src2+=2;
428 } else {
429 interp_l.feedData();
430 interp_r.feedData();
431 }
432 src+=2;
433 fp_pos -= 0x10000;
434 }
435
436 if (fp_pos < 0x10000) do {
437 // Do the interpolation
438 int lresult = *(stream+0) + (interp_l.interpolate(fp_pos)*lvol)/256;
439 int rresult = *(stream+1) + (interp_r.interpolate(fp_pos)*rvol)/256;
440
441 // Enforce range in case of an "overshot". Shouldn't happen since we
442 // scale down already, but safe is safe.
443 if (lresult < -32768) lresult = -32768;
444 else if (lresult > 32767) lresult = 32767;
445
446 if (rresult < -32768) rresult = -32768;
447 else if (rresult > 32767) rresult = 32767;
448
449 *stream++ = lresult;
450 *stream++ = rresult;
451 bytes -= 4;
452 fp_pos += fp_speed;
453
454 } while (fp_pos < 0x10000 && bytes!=0);
455
456 } while (bytes!=0 && src != src_end);
457
458 position = frame0_size - (src_end - src);
459 }
460
461
462 //
463 // 16 Bit
464 //
465
466 // Resample a frame of mono 16bit unsigned to Stereo 16bit
resampleFrameM16toS(sint16 * & stream,uint32 & bytes)467 void AudioChannel::resampleFrameM16toS(sint16 *&stream, uint32 &bytes)
468 {
469 uint8 *src = frames[frame_evenodd];
470 uint8 *src2 = frames[1-frame_evenodd];
471
472 uint8 *src_end = src + frame0_size;
473 uint8 *src2_end = src2 + frame1_size;
474
475 src += position;
476
477 int lvol = this->lvol;
478 int rvol = this->rvol;
479
480 calculate2DVolume(lvol,rvol);
481
482 do {
483 // Add a new src sample (if required)
484 if (fp_pos >= 0x10000)
485 {
486 if (src+4 < src_end) {
487 int c = ReadSample(src+4);
488 interp_l.feedData(c);
489 } else if (src2 < src2_end) {
490 int c = ReadSample(src2);
491 interp_l.feedData(c);
492 src2+=2;
493 } else {
494 interp_l.feedData();
495 }
496 src+=2;
497 fp_pos -= 0x10000;
498 }
499
500 if (fp_pos < 0x10000) do {
501 // Do the interpolation
502 int result = interp_l.interpolate(fp_pos);
503
504 int lresult = *(stream+0) + (result*lvol)/256;
505 int rresult = *(stream+1) + (result*rvol)/256;
506
507 // Enforce range in case of an "overshot". Shouldn't happen since we
508 // scale down already, but safe is safe.
509 if (lresult < -32768) lresult = -32768;
510 else if (lresult > 32767) lresult = 32767;
511
512 if (rresult < -32768) rresult = -32768;
513 else if (rresult > 32767) rresult = 32767;
514
515 *stream++ = lresult;
516 *stream++ = rresult;
517 bytes -= 4;
518 fp_pos += fp_speed;
519
520 } while (fp_pos < 0x10000 && bytes!=0);
521
522 } while (bytes!=0 && src != src_end);
523
524 position = frame0_size - (src_end - src);
525 }
526
527 // Resample a frame of mono 16bit unsigned to Mono 16bit
resampleFrameM16toM(sint16 * & stream,uint32 & bytes)528 void AudioChannel::resampleFrameM16toM(sint16 *&stream, uint32 &bytes)
529 {
530 uint8 *src = frames[frame_evenodd];
531 uint8 *src2 = frames[1-frame_evenodd];
532
533 uint8 *src_end = src + frame0_size;
534 uint8 *src2_end = src2 + frame1_size;
535
536 src += position;
537
538 int lvol = this->lvol;
539 int rvol = this->rvol;
540
541 calculate2DVolume(lvol,rvol);
542
543 int volume = (rvol + lvol)/2;
544
545 do {
546 // Add a new src sample (if required)
547 if (fp_pos >= 0x10000)
548 {
549 if (src+4 < src_end) {
550 int c = ReadSample(src+4);
551 interp_l.feedData(c);
552 } else if (src2 < src2_end) {
553 int c = ReadSample(src2);
554 interp_l.feedData(c);
555 src2+=2;
556 } else {
557 interp_l.feedData();
558 }
559 src+=2;
560 fp_pos -= 0x10000;
561 }
562
563 if (fp_pos < 0x10000) do {
564 // Do the interpolation
565 int result = (interp_l.interpolate(fp_pos)*volume)/256;
566
567 result += *stream;
568
569 // Enforce range in case of an "overshot". Shouldn't happen since we
570 // scale down already, but safe is safe.
571 if (result < -32768) result = -32768;
572 else if (result > 32767) result = 32767;
573
574 *stream++ = result;
575 bytes -= 2;
576 fp_pos += fp_speed;
577
578 } while (fp_pos < 0x10000 && bytes!=0);
579
580 } while (bytes!=0 && src != src_end);
581
582 position = frame0_size - (src_end - src);
583 }
584
585 // Resample a frame of stereo 16bit unsigned to Mono 16bit
resampleFrameS16toM(sint16 * & stream,uint32 & bytes)586 void AudioChannel::resampleFrameS16toM(sint16 *&stream, uint32 &bytes)
587 {
588 uint8 *src = frames[frame_evenodd];
589 uint8 *src2 = frames[1-frame_evenodd];
590
591 uint8 *src_end = src + frame0_size;
592 uint8 *src2_end = src2 + frame1_size;
593
594 src += position;
595
596 int lvol = this->lvol;
597 int rvol = this->rvol;
598
599 calculate2DVolume(lvol,rvol);
600
601 do {
602 // Add a new src sample (if required)
603 if (fp_pos >= 0x10000)
604 {
605 if (src+8 < src_end) {
606 int c = ReadSample(src+8);
607 int c2 = ReadSample(src+10);
608 interp_l.feedData(c);
609 interp_r.feedData(c2);
610 } else if (src2 < src2_end) {
611 int c = ReadSample(src2);
612 int c2 = ReadSample(src2+2);
613 interp_l.feedData(c);
614 interp_r.feedData(c2);
615 src2+=4;
616 } else {
617 interp_l.feedData();
618 interp_r.feedData();
619 }
620 src+=4;
621 fp_pos -= 0x10000;
622 }
623
624 if (fp_pos < 0x10000) do {
625 // Do the interpolation
626 int result = ((interp_l.interpolate(fp_pos)*lvol+interp_r.interpolate(fp_pos)*rvol))/512;
627
628 result += *stream;
629
630 // Enforce range in case of an "overshot". Shouldn't happen since we
631 // scale down already, but safe is safe.
632 if (result < -32768) result = -32768;
633 else if (result > 32767) result = 32767;
634
635 *stream++ = result;
636 bytes -= 2;
637 fp_pos += fp_speed;
638
639 } while (fp_pos < 0x10000 && bytes!=0);
640
641 } while (bytes!=0 && src != src_end);
642
643 position = frame0_size - (src_end - src);
644 }
645
646 // Resample a frame of stereo 16bit unsigned to Stereo 16bit
resampleFrameS16toS(sint16 * & stream,uint32 & bytes)647 void AudioChannel::resampleFrameS16toS(sint16 *&stream, uint32 &bytes)
648 {
649 uint8 *src = frames[frame_evenodd];
650 uint8 *src2 = frames[1-frame_evenodd];
651
652 uint8 *src_end = src + frame0_size;
653 uint8 *src2_end = src2 + frame1_size;
654
655 src += position;
656
657 int lvol = this->lvol;
658 int rvol = this->rvol;
659
660 calculate2DVolume(lvol,rvol);
661
662 do {
663 // Add a new src sample (if required)
664 if (fp_pos >= 0x10000)
665 {
666 if (src+8 < src_end) {
667 int c = ReadSample(src+8);
668 int c2 = ReadSample(src+10);
669 interp_l.feedData(c);
670 interp_r.feedData(c2);
671 } else if (src2 < src2_end) {
672 int c = ReadSample(src2);
673 int c2 = ReadSample(src2+2);
674 interp_l.feedData(c);
675 interp_r.feedData(c2);
676 src2+=4;
677 } else {
678 interp_l.feedData();
679 interp_r.feedData();
680 }
681 src+=4;
682 fp_pos -= 0x10000;
683 }
684
685 if (fp_pos < 0x10000) do {
686 // Do the interpolation
687 int lresult = *(stream+0) + (interp_l.interpolate(fp_pos)*lvol)/256;
688 int rresult = *(stream+1) + (interp_r.interpolate(fp_pos)*rvol)/256;
689
690 // Enforce range in case of an "overshot". Shouldn't happen since we
691 // scale down already, but safe is safe.
692 if (lresult < -32768) lresult = -32768;
693 else if (lresult > 32767) lresult = 32767;
694
695 if (rresult < -32768) rresult = -32768;
696 else if (rresult > 32767) rresult = 32767;
697
698 *stream++ = lresult;
699 *stream++ = rresult;
700 bytes -= 4;
701 fp_pos += fp_speed;
702
703 } while (fp_pos < 0x10000 && bytes!=0);
704
705 } while (bytes!=0 && src != src_end);
706
707 position = frame0_size - (src_end - src);
708 }
709
calculate2DVolume(int & lvol,int & rvol)710 void AudioChannel::calculate2DVolume(int &lvol, int &rvol)
711 {
712 if (distance > 255) {
713 lvol = 0;
714 rvol = 0;
715 return;
716 }
717
718 int lbal = 256;
719 int rbal = 256;
720
721 if (balance < 0) {
722 if (balance < -256) rbal = 0;
723 else rbal = balance + 256;
724 }
725 else if (balance > 0) {
726 if (balance > 256) lbal = 0;
727 else lbal = 256 -balance;
728 }
729
730 lvol = lvol*(256-distance)*lbal/65536;
731 rvol = rvol*(256-distance)*rbal/65536;
732 }
733
734 }
735
736