0ca80aab48bcb1d0ca3a12ccf05b60110e657a20
[nihav.git] / nihav-llaudio / src / codecs / flac.rs
1 use nihav_core::codecs::*;
2 use nihav_core::io::byteio::*;
3 use nihav_core::io::bitreader::*;
4 use nihav_core::io::intcode::*;
5
6 const MAX_SAMPLES: usize = 32768;
7
8 #[derive(Clone,Copy,PartialEq)]
9 enum StereoMode {
10 Normal,
11 LeftSide,
12 SideRight,
13 MidSide,
14 }
15
16 struct FlacDecoder {
17 ainfo: NAAudioInfo,
18 chmap: NAChannelMap,
19 min_blk_size: usize,
20 max_blk_size: usize,
21 min_frm_size: usize,
22 max_frm_size: usize,
23 channels: u8,
24 bits: u8,
25 srate: u32,
26 residues: [Vec<i32>; 8],
27 }
28
29 fn decode_partition(br: &mut BitReader, dst: &mut [i32], k: u8) -> DecoderResult<()> {
30 for el in dst.iter_mut() {
31 let val = br.read_code(UintCodeType::Rice(k))?;
32 if (val & 1) == 0 {
33 *el = (val >> 1) as i32;
34 } else {
35 *el = -(((val + 1) >> 1) as i32);
36 }
37 }
38 Ok(())
39 }
40
41 fn decode_residual(br: &mut BitReader, dst: &mut [i32], order: usize) -> DecoderResult<()> {
42 let mode = br.read(2)?;
43 validate!(mode < 2);
44 let rice_k = if mode == 0 { 4 } else { 5 };
45 let esc = (1 << rice_k) - 1;
46
47 let num_partitions = 1 << br.read(4)?;
48 let tot_size = dst.len() + order;
49 validate!((tot_size % num_partitions) == 0);
50 let psize = tot_size / num_partitions;
51 validate!(psize >= order);
52 let mut off = psize - order;
53 let k = br.read(rice_k)? as u8;
54 if k != esc {
55 decode_partition(br, &mut dst[..off], k)?;
56 } else {
57 let bits = br.read(5)? as u8;
58 for el in dst.iter_mut().take(off) {
59 *el = br.read_s(bits)?;
60 }
61 }
62 for _ in 1..num_partitions {
63 let k = br.read(rice_k)? as u8;
64 if k != esc {
65 decode_partition(br, &mut dst[off..][..psize], k)?;
66 } else {
67 let bits = br.read(5)? as u8;
68 for el in dst[off..].iter_mut().take(psize) {
69 *el = br.read_s(bits)?;
70 }
71 }
72 off += psize;
73 }
74
75 Ok(())
76 }
77
78 fn apply_fixed_predictor(dst: &mut [i32], order: usize) {
79 match order {
80 1 => {
81 let mut last = dst[0];
82 for el in dst.iter_mut().skip(1) {
83 *el += last;
84 last = *el;
85 }
86 },
87 2 => {
88 let mut last0 = dst[1];
89 let mut last1 = last0 - dst[0];
90 for el in dst.iter_mut().skip(2) {
91 last1 += *el;
92 last0 += last1;
93 *el = last0;
94 }
95 },
96 3 => {
97 let mut last0 = dst[2];
98 let mut last1 = last0 - dst[1];
99 let mut last2 = last1 - dst[1] + dst[0];
100 for el in dst.iter_mut().skip(3) {
101 last2 += *el;
102 last1 += last2;
103 last0 += last1;
104 *el = last0;
105 }
106 },
107 4 => {
108 let mut last0 = dst[3];
109 let mut last1 = last0 - dst[2];
110 let mut last2 = last1 - dst[2] + dst[1];
111 let mut last3 = last2 - dst[2] + 2 * dst[1] - dst[0];
112 for el in dst.iter_mut().skip(4) {
113 last3 += *el;
114 last2 += last3;
115 last1 += last2;
116 last0 += last1;
117 *el = last0;
118 }
119 },
120 _ => unreachable!(),
121 };
122 }
123
124 fn apply_lpc(dst: &mut [i32], filt: &[i32; 32], order: usize, shift: u8) {
125 for i in order..dst.len() {
126 let mut sum = 0i64;
127 for (coef, filt) in dst[i - order..].iter().take(order).zip(filt.iter()) {
128 sum += i64::from(*coef) * i64::from(*filt);
129 }
130 dst[i] += (sum >> shift) as i32;
131 }
132 }
133
134 impl FlacDecoder {
135 fn new() -> Self {
136 Self {
137 ainfo: NAAudioInfo::new(0, 1, SND_S16P_FORMAT, 0),
138 chmap: NAChannelMap::new(),
139 min_blk_size: 0,
140 max_blk_size: 0,
141 min_frm_size: 0,
142 max_frm_size: 0,
143 channels: 0,
144 bits: 0,
145 srate: 0,
146 residues: [vec![0; MAX_SAMPLES], vec![0; MAX_SAMPLES],
147 vec![0; MAX_SAMPLES], vec![0; MAX_SAMPLES],
148 vec![0; MAX_SAMPLES], vec![0; MAX_SAMPLES],
149 vec![0; MAX_SAMPLES], vec![0; MAX_SAMPLES]],
150 }
151 }
152 fn apply_chmod(&mut self, blocksize: usize, chmod: StereoMode) {
153 match chmod {
154 StereoMode::Normal => {},
155 StereoMode::LeftSide => {
156 for i in 0..blocksize {
157 self.residues[1][i] = self.residues[0][i].wrapping_sub(self.residues[1][i]);
158 }
159 },
160 StereoMode::SideRight => {
161 for i in 0..blocksize {
162 self.residues[0][i] = self.residues[0][i].wrapping_add(self.residues[1][i]);
163 }
164 },
165 StereoMode::MidSide => {
166 for i in 0..blocksize {
167 let r = self.residues[0][i].wrapping_sub(self.residues[1][i] >> 1);
168 self.residues[0][i] = self.residues[1][i].wrapping_add(r);
169 self.residues[1][i] = r;
170 }
171 },
172 };
173 }
174 fn decode_subframe(&mut self, br: &mut BitReader, channel: usize, blocksize: usize, mut samp_bits: u8) -> DecoderResult<()> {
175 let marker = br.read(1)?;
176 validate!(marker == 0);
177 let sftype = br.read(6)?;
178
179 if br.read_bool()? {
180 let nbits = br.read_code(UintCodeType::UnaryZeroes)?;
181 validate!(nbits < 32 && samp_bits > nbits as u8);
182 samp_bits -= nbits as u8;
183 }
184
185 let dst = &mut self.residues[channel][..blocksize];
186 match sftype {
187 0x00 => {
188 let val = br.read_s(samp_bits)?;
189 for el in dst.iter_mut() {
190 *el = val;
191 }
192 },
193 0x01 => {
194 for el in dst.iter_mut() {
195 *el = br.read_s(samp_bits)?;
196 }
197 },
198 0x08..=0x0C => {
199 let order = (sftype - 0x08) as usize;
200 for el in dst.iter_mut().take(order) {
201 *el = br.read_s(samp_bits)?;
202 }
203 decode_residual(br, &mut dst[order..], order)?;
204 if order > 0 {
205 apply_fixed_predictor(dst, order);
206 }
207 },
208 0x20..=0x3F => {
209 let order = (sftype - 0x20) as usize + 1;
210 for el in dst.iter_mut().take(order) {
211 *el = br.read_s(samp_bits)?;
212 }
213 let precision = br.read(4)? as u8 + 1;
214 validate!(precision < 16);
215 let shift = br.read(5)? as u8;
216 let mut filter = [0i32; 32];
217 for el in filter[..order].iter_mut().rev() {
218 *el = br.read_s(precision)?;
219 }
220 decode_residual(br, &mut dst[order..], order)?;
221 apply_lpc(dst, &filter, order, shift);
222 },
223 _ => return Err(DecoderError::InvalidData),
224 };
225
226 Ok(())
227 }
228 }
229
230 impl NADecoder for FlacDecoder {
231 fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
232 const DEFAULT_CHANNEL_MAPS: [&str; 8] = [
233 "C",
234 "L,R",
235 "L,R,C",
236 "L,R,Ls,Rs",
237 "L,R,C,Ls,Rs",
238 "L,R,C,LFE,Ls,Rs",
239 "L,R,C,LFE,Cs,Ls,Rs",
240 "L,R,C,LFE,Ls,Rs,Lss,Rss"
241 ];
242 if let NACodecTypeInfo::Audio(_ainfo) = info.get_properties() {
243 if let Some(buf) = info.get_extradata() {
244 validate!(buf.len() >= 22);
245
246 let mut mr = MemoryReader::new_read(&buf);
247 let mut br = ByteReader::new(&mut mr);
248
249 self.min_blk_size = br.read_u16be()? as usize;
250 self.max_blk_size = br.read_u16be()? as usize;
251 self.min_frm_size = br.read_u24be()? as usize;
252 self.max_frm_size = br.read_u24be()? as usize;
253 let tmp = br.read_u64be()?;
254 self.srate = (tmp >> 44) as u32;
255 self.channels = (((tmp >> 41) & 7) + 1) as u8;
256 self.bits = (((tmp >> 36) & 0x1F) + 1) as u8;
257 //let tot_samples = tmp & ((1 << 36) - 1);
258
259 self.chmap = NAChannelMap::from_str(DEFAULT_CHANNEL_MAPS[(self.channels - 1) as usize]).unwrap();
260 let fmt = match self.bits {
261 8 | 12 | 16 => SND_S16P_FORMAT,
262 24 => SND_S32P_FORMAT,
263 _ => return Err(DecoderError::NotImplemented),
264 };
265
266 self.ainfo = NAAudioInfo::new(self.srate, self.channels as u8, fmt, self.max_blk_size.max(1));
267 Ok(())
268 } else {
269 Err(DecoderError::InvalidData)
270 }
271 } else {
272 Err(DecoderError::InvalidData)
273 }
274 }
275 fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
276 let info = pkt.get_stream().get_info();
277 if let NACodecTypeInfo::Audio(_) = info.get_properties() {
278 let pktbuf = pkt.get_buffer();
279 validate!(pktbuf.len() >= 9.max(self.min_frm_size));
280
281 let ref_crc = read_u16be(&pktbuf[pktbuf.len() - 2..]).unwrap_or(0);
282 let mut crc = 0;
283 for el in pktbuf.iter().take(pktbuf.len() - 2) {
284 crc = update_crc16(crc, *el);
285 }
286 if crc != ref_crc {
287 return Err(DecoderError::ChecksumError);
288 }
289
290 let mut br = BitReader::new(&pktbuf, BitReaderMode::BE);
291
292 let sync = br.read(14)?;
293 validate!(sync == 0x3FFE);
294 br.skip(1)?;
295 let _blocking = br.read(1)?;
296 let bsize_idx = br.read(4)?;
297 let srate_idx = br.read(4)?;
298 let chan_idx = br.read(4)?;
299 let bits_idx = br.read(3)?;
300 br.skip(1)?;
301 // UTF-8 encoded block or sample number
302 let byte = br.read(8)? as u8;
303 let len = (!byte).leading_zeros();
304 validate!(len <= 5 && len != 1);
305 if len > 1 {
306 for _ in 1..len {
307 let byte = br.read(8)?;
308 validate!((byte & 0xC0) == 0x80);
309 }
310 }
311 let blocksize = match bsize_idx {
312 0 => return Err(DecoderError::InvalidData),
313 1 => 192,
314 2..=5 => 576 << (bsize_idx - 2),
315 6 => br.read(8)? as usize + 1,
316 7 => br.read(16)? as usize + 1,
317 _ => 256 << (bsize_idx - 8),
318 };
319 let srate = match srate_idx {
320 0 => self.srate,
321 1 => 88200,
322 2 => 176400,
323 3 => 192000,
324 4 => 8000,
325 5 => 16000,
326 6 => 22050,
327 7 => 24000,
328 8 => 32000,
329 9 => 44100,
330 10 => 48000,
331 11 => 96000,
332 12 => br.read(8)? * 1000,
333 13 => br.read(16)?,
334 14 => br.read(16)? * 10,
335 _ => return Err(DecoderError::InvalidData),
336 };
337 validate!(srate != 0 && srate == self.srate);
338 let (channels, chmod) = match chan_idx {
339 0..=7 => (chan_idx as u8 + 1, StereoMode::Normal),
340 8 => (2, StereoMode::LeftSide),
341 9 => (2, StereoMode::SideRight),
342 10 => (2, StereoMode::MidSide),
343 _ => return Err(DecoderError::InvalidData),
344 };
345 validate!(channels == self.channels);
346 let bits = match bits_idx {
347 0 => self.bits,
348 1 => 8,
349 2 => 12,
350 4 => 16,
351 5 => 20,
352 6 => 24,
353 _ => return Err(DecoderError::InvalidData),
354 };
355 validate!(bits == self.bits);
356
357 let end = br.tell() / 8;
358 let ref_crc = br.read(8)? as u8;
359 let mut crc = 0;
360 for el in pktbuf.iter().take(end) {
361 crc = update_crc8(crc, *el);
362 }
363 if crc != ref_crc {
364 return Err(DecoderError::ChecksumError);
365 }
366
367 for ch in 0..(channels as usize) {
368 let samp_bits = match (chmod, ch) {
369 (StereoMode::LeftSide, 1) |
370 (StereoMode::SideRight, 0) |
371 (StereoMode::MidSide, 1) => self.bits + 1,
372 _ => self.bits,
373 };
374 validate!(samp_bits <= 32);
375 self.decode_subframe(&mut br, ch, blocksize, samp_bits)?;
376 }
377 if channels == 2 {
378 self.apply_chmod(blocksize, chmod);
379 }
380
381 let mut abuf = alloc_audio_buffer(self.ainfo, blocksize, self.chmap.clone())?;
382 let postshift = if self.bits == 24 { 8 } else { 16 - self.bits };
383 match abuf {
384 NABufferType::AudioI16(ref mut adata) => {
385 let stride = adata.get_stride();
386 let dst = adata.get_data_mut().unwrap();
387 let mut off = 0;
388 for residues in self.residues.iter().take(channels as usize) {
389 let dst = &mut dst[off..][..blocksize];
390 for (dst, src) in dst.iter_mut().zip(residues.iter()) {
391 *dst = (*src << postshift) as i16;
392 }
393 off += stride;
394 }
395 },
396 NABufferType::AudioI32(ref mut adata) => {
397 let stride = adata.get_stride();
398 let dst = adata.get_data_mut().unwrap();
399 let mut off = 0;
400 for residues in self.residues.iter().take(channels as usize) {
401 let dst = &mut dst[off..][..blocksize];
402 for (dst, src) in dst.iter_mut().zip(residues.iter()) {
403 *dst = *src << postshift;
404 }
405 off += stride;
406 }
407 },
408 _ => unreachable!(),
409 };
410
411 let mut frm = NAFrame::new_from_pkt(pkt, info, abuf);
412 frm.set_duration(Some(blocksize as u64));
413 Ok(frm.into_ref())
414 } else {
415 Err(DecoderError::InvalidData)
416 }
417 }
418 fn flush(&mut self) {
419 }
420 }
421
422 impl NAOptionHandler for FlacDecoder {
423 fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
424 fn set_options(&mut self, _options: &[NAOption]) { }
425 fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
426 }
427
428 pub fn get_decoder() -> Box<dyn NADecoder + Send> {
429 Box::new(FlacDecoder::new())
430 }
431
432 #[cfg(test)]
433 mod test {
434 use nihav_core::codecs::RegisteredDecoders;
435 use nihav_core::demuxers::RegisteredDemuxers;
436 use nihav_codec_support::test::dec_video::*;
437 use crate::llaudio_register_all_decoders;
438 use crate::llaudio_register_all_demuxers;
439 #[test]
440 fn test_flac() {
441 let mut dmx_reg = RegisteredDemuxers::new();
442 llaudio_register_all_demuxers(&mut dmx_reg);
443 let mut dec_reg = RegisteredDecoders::new();
444 llaudio_register_all_decoders(&mut dec_reg);
445
446 test_decoding("flac", "flac", "assets/LLaudio/luckynight.flac", Some(6), &dmx_reg, &dec_reg,
447 ExpectedTestResult::MD5([0xe689787a, 0x032a98f7, 0xeb6e64f4, 0xfa652132]));
448 }
449 }
450
451 fn update_crc8(crc: u8, byte: u8) -> u8 {
452 CRC8_TABLE[(crc ^ byte) as usize]
453 }
454
455 fn update_crc16(crc: u16, byte: u8) -> u16 {
456 (crc << 8) ^ CRC16_TABLE[(((crc >> 8) as u8) ^ byte) as usize]
457 }
458
459 const CRC8_TABLE: [u8; 256] = [
460 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
461 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
462 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
463 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
464 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
465 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
466 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
467 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
468 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
469 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
470 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
471 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
472 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
473 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
474 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
475 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
476 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
477 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
478 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
479 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
480 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
481 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
482 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
483 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
484 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
485 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
486 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
487 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
488 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
489 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
490 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
491 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
492 ];
493
494 const CRC16_TABLE: [u16; 256] = [
495 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,
496 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
497 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072,
498 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,
499 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2,
500 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,
501 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1,
502 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,
503 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192,
504 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,
505 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1,
506 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,
507 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151,
508 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,
509 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132,
510 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101,
511 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312,
512 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,
513 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371,
514 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,
515 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1,
516 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,
517 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2,
518 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,
519 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291,
520 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,
521 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2,
522 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,
523 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252,
524 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,
525 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231,
526 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202
527 ];