summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
authorDana Conrad <dconrad@fastmail.com>2021-08-01 21:58:33 -0500
committerAidan MacDonald <amachronic@protonmail.com>2021-08-07 14:21:47 +0000
commit56b0dde5451ddedfd85c82391536e310cd05e1a8 (patch)
tree19d988f8cad0976abd086ee306ecbc840a1d477d /firmware
parent16b009825608164b17dabd877c78123a667b4981 (diff)
downloadrockbox-56b0dde5451ddedfd85c82391536e310cd05e1a8.tar.gz
rockbox-56b0dde5451ddedfd85c82391536e310cd05e1a8.zip
Higher bitdepth software volume scaling
Operates between 0 and -74 dB (mute) without issue Change-Id: I497e002bd8db43833a09ebbc29212fbb6cc8ebfd
Diffstat (limited to 'firmware')
-rw-r--r--firmware/drivers/audio/eros_qn_codec.c12
-rw-r--r--firmware/export/config/erosqnative.h3
-rw-r--r--firmware/export/eros_qn_codec.h2
-rw-r--r--firmware/pcm_sw_volume.c126
-rw-r--r--firmware/target/mips/ingenic_x1000/pcm-x1000.c7
5 files changed, 118 insertions, 32 deletions
diff --git a/firmware/drivers/audio/eros_qn_codec.c b/firmware/drivers/audio/eros_qn_codec.c
index fdf21d2f9d..17b0acf13e 100644
--- a/firmware/drivers/audio/eros_qn_codec.c
+++ b/firmware/drivers/audio/eros_qn_codec.c
@@ -70,10 +70,14 @@ void audiohw_set_volume(int vol_l, int vol_r)
70 } 70 }
71#endif 71#endif
72 72
73 l = l <= PCM5102A_VOLUME_MIN ? PCM_MUTE_LEVEL : l; 73 if (l <= PCM5102A_VOLUME_MIN || r <= PCM5102A_VOLUME_MIN)
74 r = r <= PCM5102A_VOLUME_MIN ? PCM_MUTE_LEVEL : r; 74 {
75 75 pcm_set_master_volume(PCM_MUTE_LEVEL, PCM_MUTE_LEVEL);
76 pcm_set_master_volume(l, r); 76 }
77 else
78 {
79 pcm_set_master_volume(l/20, r/20);
80 }
77} 81}
78 82
79void audiohw_mute_hp(int mute) 83void audiohw_mute_hp(int mute)
diff --git a/firmware/export/config/erosqnative.h b/firmware/export/config/erosqnative.h
index 1b37b6042a..b2b6f3f89e 100644
--- a/firmware/export/config/erosqnative.h
+++ b/firmware/export/config/erosqnative.h
@@ -64,6 +64,9 @@
64#define HAVE_SW_TONE_CONTROLS 64#define HAVE_SW_TONE_CONTROLS
65#define HAVE_SW_VOLUME_CONTROL 65#define HAVE_SW_VOLUME_CONTROL
66 66
67/* use high-bitdepth volume scaling */
68#define PCM_NATIVE_BITDEPTH 24
69
67/* Button defines */ 70/* Button defines */
68#define CONFIG_KEYPAD EROSQ_PAD 71#define CONFIG_KEYPAD EROSQ_PAD
69#define HAVE_SCROLLWHEEL 72#define HAVE_SCROLLWHEEL
diff --git a/firmware/export/eros_qn_codec.h b/firmware/export/eros_qn_codec.h
index 9c900186a8..ae04c66799 100644
--- a/firmware/export/eros_qn_codec.h
+++ b/firmware/export/eros_qn_codec.h
@@ -29,7 +29,7 @@
29/* a small DC offset appears to prevent play/pause clicking */ 29/* a small DC offset appears to prevent play/pause clicking */
30#define PCM_DC_OFFSET_VALUE -1 30#define PCM_DC_OFFSET_VALUE -1
31 31
32AUDIOHW_SETTING(VOLUME, "dB", 0, 1, PCM5102A_VOLUME_MIN/10, PCM5102A_VOLUME_MAX/10, 0) 32AUDIOHW_SETTING(VOLUME, "dB", 0, 2, PCM5102A_VOLUME_MIN/10, PCM5102A_VOLUME_MAX/10, 0)
33 33
34/* this just calls audiohw_set_volume() with the last (locally) known volume, 34/* this just calls audiohw_set_volume() with the last (locally) known volume,
35 * used for switching to/from fixed line out volume. */ 35 * used for switching to/from fixed line out volume. */
diff --git a/firmware/pcm_sw_volume.c b/firmware/pcm_sw_volume.c
index 3593c684af..646f2a680a 100644
--- a/firmware/pcm_sw_volume.c
+++ b/firmware/pcm_sw_volume.c
@@ -26,6 +26,16 @@
26#include "fixedpoint.h" 26#include "fixedpoint.h"
27#include "pcm_sw_volume.h" 27#include "pcm_sw_volume.h"
28 28
29/*
30 * NOTE: With the addition of 32-bit software scaling to this
31 * file, sometimes the "size" variable gets a little confusing.
32 *
33 * The source buffer (as of right now) is always 16-bit, and the
34 * destination buffer can potentially be 32-bit. I've tried to
35 * make it consistent: when passed in a function call, try to use
36 * the source buffer (16-bit) size.
37 */
38
29/* volume factors set by pcm_set_master_volume */ 39/* volume factors set by pcm_set_master_volume */
30static uint32_t vol_factor_l = 0, vol_factor_r = 0; 40static uint32_t vol_factor_l = 0, vol_factor_r = 0;
31 41
@@ -39,6 +49,30 @@ static uint32_t pcm_new_factor_l = 0, pcm_new_factor_r = 0;
39static uint32_t pcm_factor_l = 0, pcm_factor_r = 0; 49static uint32_t pcm_factor_l = 0, pcm_factor_r = 0;
40static typeof (memcpy) *pcm_scaling_fn = NULL; 50static typeof (memcpy) *pcm_scaling_fn = NULL;
41 51
52/* default to 16-bit volume scaling unless specified */
53#if !defined(PCM_NATIVE_BITDEPTH)
54# define PCM_NATIVE_BITDEPTH 16
55#endif
56
57/* take care of some defines for 32-bit software vol */
58#if (PCM_NATIVE_BITDEPTH > 16) /* >16-bit */
59
60# define HAVE_SWVOL_32
61# define PCM_VOL_SAMPLE_SIZE (2 * sizeof (int32_t))
62# define PCM_DBL_BUF_SIZE_T int32_t
63
64# if !defined(PCM_DC_OFFSET_VALUE)
65/* PCM_DC_OFFSET_VALUE is only needed due to hardware quirk on Eros Q */
66# define PCM_DC_OFFSET_VALUE 0
67# endif
68
69#else /* 16-BIT */
70
71# define PCM_VOL_SAMPLE_SIZE PCM_SAMPLE_SIZE
72# define PCM_DBL_BUF_SIZE_T int16_t
73
74#endif /* 16-BIT */
75
42/*** 76/***
43 ** Volume scaling routines 77 ** Volume scaling routines
44 ** If unbuffered, called externally by pcm driver 78 ** If unbuffered, called externally by pcm driver
@@ -54,54 +88,53 @@ static typeof (memcpy) *pcm_scaling_fn = NULL;
54/* Scale sample by PCM factor */ 88/* Scale sample by PCM factor */
55static inline int32_t pcm_scale_sample(PCM_F_T f, int32_t s) 89static inline int32_t pcm_scale_sample(PCM_F_T f, int32_t s)
56{ 90{
57#if defined(PCM_DC_OFFSET_VALUE) 91#if defined(HAVE_SWVOL_32)
58 return (f * s + PCM_DC_OFFSET_VALUE) >> PCM_SW_VOLUME_FRACBITS; 92 return (f * s + PCM_DC_OFFSET_VALUE) >> (32 - PCM_NATIVE_BITDEPTH);
59#else 93#else
60 return (f * s) >> PCM_SW_VOLUME_FRACBITS; 94 return (f * s) >> PCM_SW_VOLUME_FRACBITS;
61#endif 95#endif
62} 96}
63 97
64/* Both UNITY, use direct copy */
65/* static void * memcpy(void *dst, const void *src, size_t size); */
66
67/* Either cut (both <= UNITY), no clipping needed */ 98/* Either cut (both <= UNITY), no clipping needed */
68static void * pcm_scale_buffer_cut(void *dst, const void *src, size_t size) 99static void * pcm_scale_buffer_cut(void *dst, const void *src, size_t src_size)
69{ 100{
70 int16_t *d = dst; 101 PCM_DBL_BUF_SIZE_T *d = dst;
71 const int16_t *s = src; 102 const int16_t *s = src;
72 uint32_t factor_l = pcm_factor_l, factor_r = pcm_factor_r; 103 uint32_t factor_l = pcm_factor_l, factor_r = pcm_factor_r;
73 104
74 while (size) 105 while (src_size)
75 { 106 {
76 *d++ = pcm_scale_sample(factor_l, *s++); 107 *d++ = pcm_scale_sample(factor_l, *s++);
77 *d++ = pcm_scale_sample(factor_r, *s++); 108 *d++ = pcm_scale_sample(factor_r, *s++);
78 size -= PCM_SAMPLE_SIZE; 109 src_size -= PCM_SAMPLE_SIZE;
79 } 110 }
80 111
81 return dst; 112 return dst;
82} 113}
83 114
115#if !defined(HAVE_SWVOL_32) /* NOTE: 32-bit scaling is hardcoded to the cut function! */
84/* Either boost (any > UNITY) requires clipping */ 116/* Either boost (any > UNITY) requires clipping */
85static void * pcm_scale_buffer_boost(void *dst, const void *src, size_t size) 117static void * pcm_scale_buffer_boost(void *dst, const void *src, size_t src_size)
86{ 118{
87 int16_t *d = dst; 119 int16_t *d = dst;
88 const int16_t *s = src; 120 const int16_t *s = src;
89 uint32_t factor_l = pcm_factor_l, factor_r = pcm_factor_r; 121 uint32_t factor_l = pcm_factor_l, factor_r = pcm_factor_r;
90 122
91 while (size) 123 while (src_size)
92 { 124 {
93 *d++ = clip_sample_16(pcm_scale_sample(factor_l, *s++)); 125 *d++ = clip_sample_16(pcm_scale_sample(factor_l, *s++));
94 *d++ = clip_sample_16(pcm_scale_sample(factor_r, *s++)); 126 *d++ = clip_sample_16(pcm_scale_sample(factor_r, *s++));
95 size -= PCM_SAMPLE_SIZE; 127 src_size -= PCM_SAMPLE_SIZE;
96 } 128 }
97 129
98 return dst; 130 return dst;
99} 131}
132#endif
100 133
101/* Transition the volume change smoothly across a frame */ 134/* Transition the volume change smoothly across a frame */
102static void * pcm_scale_buffer_trans(void *dst, const void *src, size_t size) 135static void * pcm_scale_buffer_trans(void *dst, const void *src, size_t src_size)
103{ 136{
104 int16_t *d = dst; 137 PCM_DBL_BUF_SIZE_T *d = dst;
105 const int16_t *s = src; 138 const int16_t *s = src;
106 uint32_t factor_l = pcm_factor_l, factor_r = pcm_factor_r; 139 uint32_t factor_l = pcm_factor_l, factor_r = pcm_factor_r;
107 140
@@ -114,13 +147,19 @@ static void * pcm_scale_buffer_trans(void *dst, const void *src, size_t size)
114 int32_t diff_l = (int32_t)new_factor_l - (int32_t)factor_l; 147 int32_t diff_l = (int32_t)new_factor_l - (int32_t)factor_l;
115 int32_t diff_r = (int32_t)new_factor_r - (int32_t)factor_r; 148 int32_t diff_r = (int32_t)new_factor_r - (int32_t)factor_r;
116 149
117 for (size_t done = 0; done < size; done += PCM_SAMPLE_SIZE) 150 for (size_t done = 0; done < src_size; done += PCM_SAMPLE_SIZE)
118 { 151 {
119 int32_t sweep = (1 << 14) - fp14_cos(180*done / size); /* 0.0..2.0 */ 152 int32_t sweep = (1 << 14) - fp14_cos(180*done / src_size); /* 0.0..2.0 */
120 uint32_t f_l = fp_mul(sweep, diff_l, 15) + factor_l; 153 uint32_t f_l = fp_mul(sweep, diff_l, 15) + factor_l;
121 uint32_t f_r = fp_mul(sweep, diff_r, 15) + factor_r; 154 uint32_t f_r = fp_mul(sweep, diff_r, 15) + factor_r;
155#if defined(HAVE_SWVOL_32)
156 /* do not clip to 16 bits */
157 *d++ = pcm_scale_sample(f_l, *s++);
158 *d++ = pcm_scale_sample(f_r, *s++);
159#else
122 *d++ = clip_sample_16(pcm_scale_sample(f_l, *s++)); 160 *d++ = clip_sample_16(pcm_scale_sample(f_l, *s++));
123 *d++ = clip_sample_16(pcm_scale_sample(f_r, *s++)); 161 *d++ = clip_sample_16(pcm_scale_sample(f_r, *s++));
162#endif
124 } 163 }
125 164
126 /* Select steady-state operation */ 165 /* Select steady-state operation */
@@ -133,9 +172,9 @@ static void * pcm_scale_buffer_trans(void *dst, const void *src, size_t size)
133#ifndef PCM_SW_VOLUME_UNBUFFERED 172#ifndef PCM_SW_VOLUME_UNBUFFERED
134static inline 173static inline
135#endif 174#endif
136void pcm_sw_volume_copy_buffer(void *dst, const void *src, size_t size) 175void pcm_sw_volume_copy_buffer(void *dst, const void *src, size_t src_size)
137{ 176{
138 pcm_scaling_fn(dst, src, size); 177 pcm_scaling_fn(dst, src, src_size);
139} 178}
140 179
141/* Assign the new scaling function for normal steady-state operation */ 180/* Assign the new scaling function for normal steady-state operation */
@@ -147,6 +186,13 @@ void pcm_sync_pcm_factors(void)
147 pcm_factor_l = new_factor_l; 186 pcm_factor_l = new_factor_l;
148 pcm_factor_r = new_factor_r; 187 pcm_factor_r = new_factor_r;
149 188
189/* NOTE: 32-bit scaling is limited to 0 db <--> -74 db, we will hardcode to cut.
190 * MEMCPY CANNOT BE USED, because we do need to at minimum multiply each
191 * sample up to 32-bit size. */
192#if defined(HAVE_SWVOL_32)
193 pcm_scaling_fn = pcm_scale_buffer_cut;
194#else
195
150 if (new_factor_l == PCM_FACTOR_UNITY && 196 if (new_factor_l == PCM_FACTOR_UNITY &&
151 new_factor_r == PCM_FACTOR_UNITY) 197 new_factor_r == PCM_FACTOR_UNITY)
152 { 198 {
@@ -161,6 +207,7 @@ void pcm_sync_pcm_factors(void)
161 { 207 {
162 pcm_scaling_fn = pcm_scale_buffer_boost; 208 pcm_scaling_fn = pcm_scale_buffer_boost;
163 } 209 }
210#endif
164} 211}
165 212
166#ifndef PCM_SW_VOLUME_UNBUFFERED 213#ifndef PCM_SW_VOLUME_UNBUFFERED
@@ -168,10 +215,10 @@ void pcm_sync_pcm_factors(void)
168static const void * volatile src_buf_addr = NULL; 215static const void * volatile src_buf_addr = NULL;
169static size_t volatile src_buf_rem = 0; 216static size_t volatile src_buf_rem = 0;
170 217
171#define PCM_PLAY_DBL_BUF_SIZE (PCM_PLAY_DBL_BUF_SAMPLE*PCM_SAMPLE_SIZE) 218#define PCM_PLAY_DBL_BUF_SIZE (PCM_PLAY_DBL_BUF_SAMPLE*PCM_VOL_SAMPLE_SIZE)
172 219
173/* double buffer and frame length control */ 220/* double buffer and frame length control */
174static int16_t pcm_dbl_buf[2][PCM_PLAY_DBL_BUF_SAMPLES*2] 221static PCM_DBL_BUF_SIZE_T pcm_dbl_buf[2][PCM_PLAY_DBL_BUF_SAMPLES*2]
175 PCM_DBL_BUF_BSS MEM_ALIGN_ATTR; 222 PCM_DBL_BUF_BSS MEM_ALIGN_ATTR;
176static size_t pcm_dbl_buf_size[2]; 223static size_t pcm_dbl_buf_size[2];
177static int pcm_dbl_buf_num = 0; 224static int pcm_dbl_buf_num = 0;
@@ -209,11 +256,12 @@ bool pcm_play_dma_complete_callback(enum pcm_dma_status status,
209 in one chunk */ 256 in one chunk */
210static void update_frame_params(size_t size) 257static void update_frame_params(size_t size)
211{ 258{
212 int count = size / PCM_SAMPLE_SIZE; 259 /* multiply by 2 for 32 bit, optimize away to 1 for 16 bit */
260 int count = (size * (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t))) / PCM_VOL_SAMPLE_SIZE;
213 frame_count = (count + PCM_PLAY_DBL_BUF_SAMPLES - 1) / 261 frame_count = (count + PCM_PLAY_DBL_BUF_SAMPLES - 1) /
214 PCM_PLAY_DBL_BUF_SAMPLES; 262 PCM_PLAY_DBL_BUF_SAMPLES;
215 int perframe = count / frame_count; 263 int perframe = count / frame_count;
216 frame_size = perframe * PCM_SAMPLE_SIZE; 264 frame_size = perframe * PCM_VOL_SAMPLE_SIZE;
217 frame_frac = count - perframe * frame_count; 265 frame_frac = count - perframe * frame_count;
218 frame_err = 0; 266 frame_err = 0;
219} 267}
@@ -225,7 +273,8 @@ pcm_play_dma_status_callback_int(enum pcm_dma_status status)
225 if (status != PCM_DMAST_STARTED) 273 if (status != PCM_DMAST_STARTED)
226 return status; 274 return status;
227 275
228 size_t size = pcm_dbl_buf_size[pcm_dbl_buf_num]; 276 /* divide by 2 for 32 bit, optimize away to 1 for 16 bit */
277 size_t size = pcm_dbl_buf_size[pcm_dbl_buf_num] / (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t));
229 const void *addr = src_buf_addr + size; 278 const void *addr = src_buf_addr + size;
230 279
231 size = src_buf_rem - size; 280 size = src_buf_rem - size;
@@ -241,7 +290,8 @@ pcm_play_dma_status_callback_int(enum pcm_dma_status status)
241 290
242 if (size != 0) 291 if (size != 0)
243 { 292 {
244 size = frame_size; 293 /* multiply by 2 for 32 bit, optimize away to 1 for 16 bit */
294 size = frame_size / (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t));
245 295
246 if ((frame_err += frame_frac) >= frame_count) 296 if ((frame_err += frame_frac) >= frame_count)
247 { 297 {
@@ -251,7 +301,8 @@ pcm_play_dma_status_callback_int(enum pcm_dma_status status)
251 } 301 }
252 302
253 pcm_dbl_buf_num ^= 1; 303 pcm_dbl_buf_num ^= 1;
254 pcm_dbl_buf_size[pcm_dbl_buf_num] = size; 304 /* multiply by 2 for 32 bit, optimize away to 1 for 16 bit */
305 pcm_dbl_buf_size[pcm_dbl_buf_num] = size * (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t));
255 pcm_sw_volume_copy_buffer(pcm_dbl_buf[pcm_dbl_buf_num], addr, size); 306 pcm_sw_volume_copy_buffer(pcm_dbl_buf[pcm_dbl_buf_num], addr, size);
256 307
257 return PCM_DMAST_OK; 308 return PCM_DMAST_OK;
@@ -269,6 +320,7 @@ static void start_pcm(bool reframe)
269 if (reframe) 320 if (reframe)
270 update_frame_params(src_buf_rem); 321 update_frame_params(src_buf_rem);
271 322
323
272 pcm_play_dma_status_callback(PCM_DMAST_STARTED); 324 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
273 pcm_play_dma_status_callback(PCM_DMAST_STARTED); 325 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
274 326
@@ -278,7 +330,8 @@ static void start_pcm(bool reframe)
278void pcm_play_dma_start_int(const void *addr, size_t size) 330void pcm_play_dma_start_int(const void *addr, size_t size)
279{ 331{
280 src_buf_addr = addr; 332 src_buf_addr = addr;
281 src_buf_rem = size; 333 /* divide by 2 for 32 bit, optimize away to 1 for 16 bit */
334 src_buf_rem = size / (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t));
282 start_pcm(true); 335 start_pcm(true);
283} 336}
284 337
@@ -299,13 +352,32 @@ static uint32_t pcm_centibels_to_factor(int volume)
299{ 352{
300 if (volume == PCM_MUTE_LEVEL) 353 if (volume == PCM_MUTE_LEVEL)
301 return 0; /* mute */ 354 return 0; /* mute */
302 355#if defined(HAVE_SWVOL_32)
356 /*
357 * 32-bit software volume taken from pcm-alsa.c
358 */
359 volume += 48; /* -42dB .. 0dB => 5dB .. 48dB */
360 /* NOTE if vol_dB = 5 then vol_shift = 1 but r = 1 so we do vol_shift - 1 >= 0
361 * otherwise vol_dB >= 0 implies vol_shift >= 2 so vol_shift - 2 >= 0 */
362 int vol_shift = volume / 3;
363 int r = volume % 3;
364 int32_t dig_vol_mult;
365 if(r == 0)
366 dig_vol_mult = 1 << vol_shift;
367 else if(r == 1)
368 dig_vol_mult = 1 << vol_shift | 1 << (vol_shift - 2);
369 else
370 dig_vol_mult = 1 << vol_shift | 1 << (vol_shift - 1);
371 return dig_vol_mult;
372#else /* standard software volume */
303 /* Centibels -> fixedpoint */ 373 /* Centibels -> fixedpoint */
304 return (uint32_t)fp_factor(fp_div(volume, 10, PCM_SW_VOLUME_FRACBITS), 374 return (uint32_t)fp_factor(fp_div(volume, 10, PCM_SW_VOLUME_FRACBITS),
305 PCM_SW_VOLUME_FRACBITS); 375 PCM_SW_VOLUME_FRACBITS);
376#endif /* HAVE_SWVOL_32 */
306} 377}
307 378
308 379
380
309/** Public functions **/ 381/** Public functions **/
310 382
311/* Produce final pcm scale factor */ 383/* Produce final pcm scale factor */
diff --git a/firmware/target/mips/ingenic_x1000/pcm-x1000.c b/firmware/target/mips/ingenic_x1000/pcm-x1000.c
index a3da3411f2..ce2fbb17a9 100644
--- a/firmware/target/mips/ingenic_x1000/pcm-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/pcm-x1000.c
@@ -66,10 +66,17 @@ void pcm_play_dma_init(void)
66 /* Let the target initialize its hardware and setup the AIC */ 66 /* Let the target initialize its hardware and setup the AIC */
67 audiohw_init(); 67 audiohw_init();
68 68
69#if (PCM_NATIVE_BITDEPTH > 16)
70 /* Program audio format (stereo, 24 bit samples) */
71 jz_writef(AIC_CCR, PACK16(0), CHANNEL_V(STEREO),
72 OSS_V(24BIT), ISS_V(24BIT), M2S(0));
73 jz_writef(AIC_I2SCR, SWLH(0));
74#else
69 /* Program audio format (stereo, packed 16 bit samples) */ 75 /* Program audio format (stereo, packed 16 bit samples) */
70 jz_writef(AIC_CCR, PACK16(1), CHANNEL_V(STEREO), 76 jz_writef(AIC_CCR, PACK16(1), CHANNEL_V(STEREO),
71 OSS_V(16BIT), ISS_V(16BIT), M2S(0)); 77 OSS_V(16BIT), ISS_V(16BIT), M2S(0));
72 jz_writef(AIC_I2SCR, SWLH(0)); 78 jz_writef(AIC_I2SCR, SWLH(0));
79#endif
73 80
74 /* Set DMA settings */ 81 /* Set DMA settings */
75 jz_writef(AIC_CFG, TFTH(16), RFTH(16)); 82 jz_writef(AIC_CFG, TFTH(16), RFTH(16));