FreeCalypso > hg > gsm-codec-lib
annotate amrconv/ietf2cod.c @ 242:f081a6850fb5
libgsmfrp: new refined implementation
The previous implementation exhibited the following defects,
which are now fixed:
1) The last received valid SID was cached forever for the purpose of
handling future invalid SIDs - we could have received some valid
SID ages ago, then lots of speech or NO_DATA, and if we then get
an invalid SID, we would resurrect the last valid SID from ancient
history - a bad design. In our new design, we handle invalid SID
based on the current state, much like BFI.
2) GSM 06.11 spec says clearly that after the second lost SID
(received BFI=1 && TAF=1 in CN state) we need to gradually decrease
the output level, rather than jump directly to emitting silence
frames - we previously failed to implement such logic.
3) Per GSM 06.12 section 5.2, Xmaxc should be the same in all 4 subframes
in a SID frame. What should we do if we receive an otherwise valid
SID frame with different Xmaxc? Our previous approach would
replicate this Xmaxc oddity in every subsequent generated CN frame,
which is rather bad. In our new design, the very first CN frame
(which can be seen as a transformation of the SID frame itself)
retains the original 4 distinct Xmaxc, but all subsequent CN frames
are based on the Xmaxc from the last subframe of the most recent SID.
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Tue, 09 May 2023 05:16:31 +0000 |
parents | 1a7d659a952f |
children |
rev | line source |
---|---|
115
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
1 /* |
216
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
2 * This program converts an AMR-encoded speech recording from the common |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
3 * IETF RFC 4867 .amr format into the 3GPP test sequence .cod format. |
115
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
4 */ |
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
5 |
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
6 #include <stdio.h> |
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
7 #include <stdint.h> |
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
8 #include <stdlib.h> |
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
9 #include <string.h> |
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
10 #include <strings.h> |
211
78d1a6513393
amrconv: new program amr-cod-parse
Mychaela Falconia <falcon@freecalypso.org>
parents:
148
diff
changeset
|
11 #include "amr_defs.h" |
78d1a6513393
amrconv: new program amr-cod-parse
Mychaela Falconia <falcon@freecalypso.org>
parents:
148
diff
changeset
|
12 |
78d1a6513393
amrconv: new program amr-cod-parse
Mychaela Falconia <falcon@freecalypso.org>
parents:
148
diff
changeset
|
13 extern unsigned amr_bit_lengths[9]; |
215
4c4649a5fec3
amrconv: new program amr-cod2ietf
Mychaela Falconia <falcon@freecalypso.org>
parents:
212
diff
changeset
|
14 extern const char amr_file_hdr[IETF_HDR_LEN]; |
4c4649a5fec3
amrconv: new program amr-cod2ietf
Mychaela Falconia <falcon@freecalypso.org>
parents:
212
diff
changeset
|
15 extern const uint8_t extra_bytes_per_ft[9]; |
115
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
16 |
216
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
17 static void |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
18 bits2words(ser_bits, cod_words, nbits) |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
19 uint8_t *ser_bits; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
20 uint16_t *cod_words; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
21 unsigned nbits; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
22 { |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
23 uint8_t *sp = ser_bits; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
24 uint16_t *dp = cod_words; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
25 unsigned n; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
26 |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
27 for (n = 0; n < nbits; n++) |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
28 *dp++ = *sp++; |
217
1a7d659a952f
amr-ietf2cod: silly bugfix
Mychaela Falconia <falcon@freecalypso.org>
parents:
216
diff
changeset
|
29 for (; n < MAX_SERIAL_SIZE; n++) |
216
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
30 *dp++ = 0; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
31 } |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
32 |
115
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
33 main(argc, argv) |
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
34 char **argv; |
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
35 { |
215
4c4649a5fec3
amrconv: new program amr-cod2ietf
Mychaela Falconia <falcon@freecalypso.org>
parents:
212
diff
changeset
|
36 char *infname, *outfname; |
4c4649a5fec3
amrconv: new program amr-cod2ietf
Mychaela Falconia <falcon@freecalypso.org>
parents:
212
diff
changeset
|
37 FILE *inf, *outf; |
216
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
38 uint8_t frm_in[MAX_IF1_BYTES]; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
39 unsigned frame_no, mode, qbit, sti, sid_mode; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
40 uint8_t ser_bits[MAX_SERIAL_SIZE]; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
41 uint16_t cod_words[COD_FORMAT_NWORDS]; |
211
78d1a6513393
amrconv: new program amr-cod-parse
Mychaela Falconia <falcon@freecalypso.org>
parents:
148
diff
changeset
|
42 int rc; |
115
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
43 |
216
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
44 if (argc != 3) { |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
45 fprintf(stderr, "usage: %s input.amr output.cod\n", argv[0]); |
115
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
46 exit(1); |
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
47 } |
216
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
48 infname = argv[1]; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
49 outfname = argv[2]; |
148
bbe5669f0f29
gsmefr-cod-parse: add BE support
Mychaela Falconia <falcon@freecalypso.org>
parents:
147
diff
changeset
|
50 inf = fopen(infname, "r"); |
115
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
51 if (!inf) { |
148
bbe5669f0f29
gsmefr-cod-parse: add BE support
Mychaela Falconia <falcon@freecalypso.org>
parents:
147
diff
changeset
|
52 perror(infname); |
115
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
53 exit(1); |
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
54 } |
216
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
55 if (fread(frm_in, 1, IETF_HDR_LEN, inf) != IETF_HDR_LEN || |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
56 bcmp(frm_in, amr_file_hdr, IETF_HDR_LEN)) { |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
57 fprintf(stderr, "error: %s is not in IETF AMR format\n", |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
58 infname); |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
59 exit(1); |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
60 } |
215
4c4649a5fec3
amrconv: new program amr-cod2ietf
Mychaela Falconia <falcon@freecalypso.org>
parents:
212
diff
changeset
|
61 outf = fopen(outfname, "w"); |
4c4649a5fec3
amrconv: new program amr-cod2ietf
Mychaela Falconia <falcon@freecalypso.org>
parents:
212
diff
changeset
|
62 if (!outf) { |
4c4649a5fec3
amrconv: new program amr-cod2ietf
Mychaela Falconia <falcon@freecalypso.org>
parents:
212
diff
changeset
|
63 perror(outfname); |
4c4649a5fec3
amrconv: new program amr-cod2ietf
Mychaela Falconia <falcon@freecalypso.org>
parents:
212
diff
changeset
|
64 exit(1); |
4c4649a5fec3
amrconv: new program amr-cod2ietf
Mychaela Falconia <falcon@freecalypso.org>
parents:
212
diff
changeset
|
65 } |
216
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
66 /* padding words which will never be filled */ |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
67 bzero(cod_words + MAX_SERIAL_SIZE + 2, sizeof(uint16_t) * 4); |
115
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
68 for (frame_no = 0; ; frame_no++) { |
216
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
69 rc = getc(inf); |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
70 if (rc < 0) |
211
78d1a6513393
amrconv: new program amr-cod-parse
Mychaela Falconia <falcon@freecalypso.org>
parents:
148
diff
changeset
|
71 break; |
216
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
72 mode = (rc & 0x78) >> 3; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
73 qbit = (rc & 4) >> 2; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
74 if (mode == MODE_NO_DATA) { |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
75 cod_words[0] = TX_NO_DATA; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
76 bzero(cod_words+1, sizeof(uint16_t) * MAX_SERIAL_SIZE); |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
77 cod_words[245] = 0xFFFF; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
78 goto output; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
79 } |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
80 if (mode > MRDTX) { |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
81 fprintf(stderr, "error in frame #%u: invalid FT=%u\n", |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
82 frame_no, mode); |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
83 exit(1); |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
84 } |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
85 rc = fread(frm_in, 1, extra_bytes_per_ft[mode], inf); |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
86 if (rc != extra_bytes_per_ft[mode]) { |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
87 fprintf(stderr, |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
88 "error: short read from %s on frame #%u\n", |
215
4c4649a5fec3
amrconv: new program amr-cod2ietf
Mychaela Falconia <falcon@freecalypso.org>
parents:
212
diff
changeset
|
89 infname, frame_no); |
4c4649a5fec3
amrconv: new program amr-cod2ietf
Mychaela Falconia <falcon@freecalypso.org>
parents:
212
diff
changeset
|
90 exit(1); |
115
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
91 } |
216
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
92 amr_if1_unpack(frm_in, ser_bits, mode); |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
93 if (mode == MRDTX) { |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
94 sti = (frm_in[4] & 0x10) >> 4; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
95 sid_mode = 0; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
96 if (frm_in[4] & 0x08) |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
97 sid_mode |= 1; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
98 if (frm_in[4] & 0x04) |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
99 sid_mode |= 2; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
100 if (frm_in[4] & 0x02) |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
101 sid_mode |= 4; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
102 } |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
103 bits2words(ser_bits, cod_words + 1, amr_bit_lengths[mode]); |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
104 if (mode == MRDTX) { |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
105 if (qbit) |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
106 cod_words[0] = sti ? TX_SID_UPDATE |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
107 : TX_SID_FIRST; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
108 else |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
109 cod_words[0] = TX_SID_BAD; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
110 cod_words[245] = sid_mode; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
111 } else { |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
112 cod_words[0] = qbit ? TX_SPEECH_GOOD : TX_SPEECH_BAD; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
113 cod_words[245] = mode; |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
114 } |
9d59df9b0e4e
amrconv: new program amr-ietf2cod
Mychaela Falconia <falcon@freecalypso.org>
parents:
215
diff
changeset
|
115 output: fwrite(cod_words, 2, COD_FORMAT_NWORDS, outf); |
115
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
116 } |
215
4c4649a5fec3
amrconv: new program amr-cod2ietf
Mychaela Falconia <falcon@freecalypso.org>
parents:
212
diff
changeset
|
117 fclose(outf); |
211
78d1a6513393
amrconv: new program amr-cod-parse
Mychaela Falconia <falcon@freecalypso.org>
parents:
148
diff
changeset
|
118 exit(0); |
115
5a63294fa321
gsmefr-cod-parse test program written
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
119 } |