FreeCalypso > hg > gsm-codec-lib
comparison doc/FR1-Rx-DTX @ 135:22601ae99434
doc/FR1-Rx-DTX article written
| author | Mychaela Falconia <falcon@freecalypso.org> |
|---|---|
| date | Sun, 11 Dec 2022 07:51:10 +0000 |
| parents | |
| children | aa4cdab30dc8 |
comparison
equal
deleted
inserted
replaced
| 134:170e03b20337 | 135:22601ae99434 |
|---|---|
| 1 At the level of provided functionality and architectural structure, ETSI GSM | |
| 2 specifications for DTX (discontinuous transmission) are very symmetric between | |
| 3 FR and EFR: the same DTX functionality is specified for both codecs, with the | |
| 4 same overall architecture. However, there is one important difference: in the | |
| 5 case of EFR the complete implementation of all DTX functions (for both Tx and | |
| 6 Rx) forms an integral and inseparable part of the reference codec (implemented | |
| 7 in C) from the beginning, whereas in the case of FR1 the addition of DTX is | |
| 8 somewhat of an afterthought. GSM 06.10 defines a "pure" FR codec without any | |
| 9 DTX functions, and this most basic spec can be and has been implemented in this | |
| 10 "pure" form - classic Unix libgsm from 1990s is a proper, fully compliant | |
| 11 implementation of GSM 06.10, but only this spec, without any DTX. In contrast, | |
| 12 there has never existed a "pure" implementation of GSM 06.60 EFR codec without | |
| 13 associated Tx and Rx DTX functions. Furthermore, there is an important | |
| 14 distinction between Tx and Rx DTX handlers for FR1: | |
| 15 | |
| 16 * Anyone who seeks to implement Tx DTX for FR1 would have to dig into the guts | |
| 17 of GSM 06.10 encoder and augment it with VAD and SID encoding functions per | |
| 18 GSM 06.32 and 06.12 specs. | |
| 19 | |
| 20 * In contrast, the Rx DTX handler for FR1 is modular: the way it is specified | |
| 21 in GSM 06.11, 06.12 and 06.31 is a front-end to unmodified GSM 06.10 decoder. | |
| 22 On the Rx side, the interface from the radio subsystem to the Rx DTX handler | |
| 23 consists of 260 bits of frame plus BFI and TAF flags (the spec also defines a | |
| 24 SID flag, but it is determined from frame payload bits), and then the | |
| 25 interface from the Rx DTX handler to the GSM 06.10 decoder is another FR frame | |
| 26 of 260 bits. | |
| 27 | |
| 28 What are the implications of this situation for the GSM published-source | |
| 29 software community? Prior to the present libgsmfrp offering, there has always | |
| 30 been libgsm, but no Rx DTX handler. If you are working with a GSM uplink RTP | |
| 31 stream from a BTS or a GSM downlink frame stream read out of TI Calypso DSP or | |
| 32 some other GSM MS PHY, feeding that stream directly to libgsm (without passing | |
| 33 through an Rx DTX handler) is NOT acceptable: a "bare" GSM 06.10 decoder won't | |
| 34 recognize SID frames and won't produce the expected comfort noise output, and | |
| 35 what are you going to do in those 20 ms windows in which no good traffic frame | |
| 36 was received? The situation becomes especially bad (unkind on ears) if you are | |
| 37 reading received downlink frames out of TI Calypso DSP: the DSP's buffer will | |
| 38 have *some* bit content in every 20 ms window, but naturally this bit content | |
| 39 will be garbage during those frame windows when no good frame was received; | |
| 40 feeding that garbage to libgsm produces noises that are very unkind on ears. | |
| 41 | |
| 42 The correct solution is to implement an Rx DTX handler, pass the stream of | |
| 43 frames and flags from the BTS or the MS PHY to this handler first, and then pass | |
| 44 the output of this handler to libgsm 06.10 decoder. Themyscira libgsmfrp is a | |
| 45 Free Software implementation of Rx DTX handler for GSM FR, implementing SID | |
| 46 classification, comfort noise generation and error concealment. | |
| 47 | |
| 48 Effect of extra preprocessing | |
| 49 ============================= | |
| 50 | |
| 51 One key detail deserves extra emphasis before going into library API details: | |
| 52 if the input to libgsmfrp consists entirely of good speech frames (no SID frames | |
| 53 and no BFIs), then the preprocessor becomes an identity transform. Therefore, | |
| 54 if the output of our libgsmfrp preprocessor were to be fed to an additional | |
| 55 instance of the same further down the processing chain, no extra transformation | |
| 56 of any kind will happen. | |
| 57 | |
| 58 Using libgsmfrp | |
| 59 =============== | |
| 60 | |
| 61 The external public interface to Themyscira libgsmfrp consists of a single | |
| 62 header file <gsm_fr_preproc.h>; it should be installed in the same system | |
| 63 include directory as <gsm.h> from libgsm. Please note that <gsm_fr_preproc.h> | |
| 64 includes <gsm.h>, as needed for gsm_byte and gsm_frame defined types. | |
| 65 | |
| 66 The dialect of C we chose for libgsmfrp is ANSI C (function prototypes), const | |
| 67 qualifier is used where appropriate; however, unlike libgsmefr, the interface | |
| 68 to libgsmfrp is defined in terms of gsm_byte type defined in <gsm.h>, included | |
| 69 from <gsm_fr_preproc.h>. | |
| 70 | |
| 71 State allocation and freeing | |
| 72 ============================ | |
| 73 | |
| 74 The Rx DTX handler is stateful, hence you will need to allocate a preprocessor | |
| 75 state structure in addition to the usual libgsm state structure for your GSM FR | |
| 76 Rx session. The necessary function is: | |
| 77 | |
| 78 extern struct gsmfr_preproc_state *gsmfr_preproc_create(void); | |
| 79 | |
| 80 struct gsmfr_preproc_state is an opaque structure to library users: you only get | |
| 81 a pointer which you remember and pass around, but <gsm_fr_preproc.h> does not | |
| 82 give you a full definition of this struct. As a library user, you don't even | |
| 83 get to know the size of this struct, hence the necessary malloc() operation | |
| 84 happens inside gsmfr_preproc_create(). However, the structure is malloc'ed as | |
| 85 a single chunk, hence when you are done with it, simply call free() on the | |
| 86 pointer you got from gsmfr_preproc_create(). | |
| 87 | |
| 88 gsmfr_preproc_create() can fail if the malloc() call inside fails, in which case | |
| 89 it returns NULL. | |
| 90 | |
| 91 Preprocessing good frames | |
| 92 ========================= | |
| 93 | |
| 94 For every good traffic frame (BFI=0) you receive from the radio subsystem, you | |
| 95 need to call this preprocessor function: | |
| 96 | |
| 97 extern void gsmfr_preproc_good_frame(struct gsmfr_preproc_state *state, | |
| 98 gsm_byte *frame); | |
| 99 | |
| 100 The second argument is both input and output, i.e., the frame is modified in | |
| 101 place. If the received frame is not SID (specifically, if the SID field | |
| 102 deviates from the SID codeword by 16 or more bits, per GSM 06.31 section 6.1.1), | |
| 103 then the frame (considered a good speech frame) will be left unmodified (i.e., | |
| 104 it is to be passed unchanged to the GSM 06.10 decoder), but preprocessor state | |
| 105 will be updated. OTOH, if the received frame is classified as either valid or | |
| 106 invalid SID per GSM 06.31, then the output frame will contain comfort noise | |
| 107 generated by the preprocessor using a PRNG, or a silence frame in one particular | |
| 108 corner case. | |
| 109 | |
| 110 GSM-FR RTP (or libgsm) 0xD magic: the upper nibble of the first byte can be | |
| 111 anything on input to gsmfr_preproc_good_frame(), but the output frame will | |
| 112 always have the correct magic in it. | |
| 113 | |
| 114 Handling BFI conditions | |
| 115 ======================= | |
| 116 | |
| 117 If you received a lost/missing frame indication instead of a good traffic frame, | |
| 118 call this preprocessor function: | |
| 119 | |
| 120 extern void gsmfr_preproc_bfi(struct gsmfr_preproc_state *state, int taf, | |
| 121 gsm_byte *frame_out); | |
| 122 | |
| 123 TAF is a flag defined in GSM 06.31 section 6.1.1; if you don't have this flag, | |
| 124 pass 0 - you will lose the function of comfort noise muting in the event of | |
| 125 prolonged SID loss, but all other Rx DTX functions will still work the same. | |
| 126 | |
| 127 With this function the 33-byte frame buffer is only an output, i.e., prior | |
| 128 buffer content is a don't-care and there is no provision for making any use of | |
| 129 erroneous frames like in EFR. The frame generated by the preprocessor may be | |
| 130 substitution/muting, comfort noise or silence depending on the state. | |
| 131 | |
| 132 Other miscellaneous functions | |
| 133 ============================= | |
| 134 | |
| 135 extern void gsmfr_preproc_reset(struct gsmfr_preproc_state *state); | |
| 136 | |
| 137 This function resets the preprocessor state to what it is right out of | |
| 138 gsmfr_preproc_create(), which is naturally just a combination of malloc() and | |
| 139 gsmfr_proproc_reset(). Given that our Rx DTX handler state is much simpler | |
| 140 than, for example, EFR codec state, there does not seem to be any need for | |
| 141 explicit resets, but the reset function is made public for the sake of | |
| 142 completeness. | |
| 143 | |
| 144 extern int gsmfr_preproc_sid_classify(const gsm_byte *frame); | |
| 145 | |
| 146 This function analyzes an RTP-encoded FR frame (the upper nibble of the first | |
| 147 byte is NOT checked for 0xD signature) for the SID codeword of GSM 06.12 and | |
| 148 classifies the frame as SID=0, SID=1 or SID=2 per the rules of GSM 06.31 | |
| 149 section 6.1.1. | |
| 150 | |
| 151 Silence frame datum | |
| 152 =================== | |
| 153 | |
| 154 extern const gsm_frame gsmfr_preproc_silence_frame; | |
| 155 | |
| 156 Many implementors make the mistake of thinking that a GSM FR silence frame is a | |
| 157 frame of 260 zero bits, but the official specs disagree: the silence frame given | |
| 158 in GSM 06.11 (3GPP TS 46.011, at the very end of the spec) is quite different. | |
| 159 Themyscira libgsmfrp implements the correct silence frame per the spec, and that | |
| 160 datum is also made public. |
