X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Fra144.c;h=95d9e70cbdd380a61e0de6e4101f2981c5f6f570;hb=2d73e6c8d7fe460877760ee295fcbe1f64a308b8;hp=c820c732baa6915d413ead601a19d2aa82efe230;hpb=aee481cebe8f95ce3789bdead6fb8ddfb142c37f;p=ffmpeg diff --git a/libavcodec/ra144.c b/libavcodec/ra144.c index c820c732baa..95d9e70cbdd 100644 --- a/libavcodec/ra144.c +++ b/libavcodec/ra144.c @@ -20,13 +20,9 @@ */ #include "avcodec.h" +#include "bitstream.h" #include "ra144.h" -#define DATABLOCK1 20 /* size of 14.4 input block in bytes */ -#define DATACHUNK1 1440 /* size of 14.4 input chunk in bytes */ -#define AUDIOBLOCK 160 /* size of output block in 16-bit words (320 bytes) */ -#define AUDIOBUFFER 12288 /* size of output buffer in 16-bit words (24576 bytes) */ -/* consts */ #define NBLOCKS 4 /* number of segments within a block */ #define BLOCKSIZE 40 /* (quarter) block size in 16-bit words (80 bytes) */ #define HALFBLOCK 20 /* BLOCKSIZE/2 */ @@ -35,472 +31,359 @@ /* internal globals */ typedef struct { - unsigned int resetflag, val, oldval; - unsigned int unpacked[28]; /* buffer for unpacked input */ - unsigned int *iptr; /* pointer to current input (from unpacked) */ - unsigned int gval; - unsigned short *gsp; - unsigned int gbuf1[8]; - unsigned short gbuf2[120]; - signed short output_buffer[40]; - unsigned int *decptr; /* decoder ptr */ - signed short *decsp; - - /* the swapped buffers */ - unsigned int swapb1a[10]; - unsigned int swapb2a[10]; - unsigned int swapb1b[10]; - unsigned int swapb2b[10]; - unsigned int *swapbuf1; - unsigned int *swapbuf2; - unsigned int *swapbuf1alt; - unsigned int *swapbuf2alt; - - unsigned int buffer[5]; - unsigned short int buffer_2[148]; - unsigned short int buffer_a[40]; - unsigned short int buffer_b[40]; - unsigned short int buffer_c[40]; - unsigned short int buffer_d[40]; - - unsigned short int work[50]; - unsigned short *sptr; - - int buffer1[10]; - int buffer2[10]; - - signed short wavtable1[2304]; - unsigned short wavtable2[2304]; -} Real144_internal; + unsigned int old_energy; ///< previous frame energy + + /* the swapped buffers */ + unsigned int lpc_tables[4][10]; + unsigned int *lpc_refl; ///< LPC reflection coefficients + unsigned int *lpc_coef; ///< LPC coefficients + unsigned int *lpc_refl_old; ///< previous frame LPC reflection coefs + unsigned int *lpc_coef_old; ///< previous frame LPC coefficients + + unsigned int buffer[5]; + uint16_t adapt_cb[148]; ///< adaptive codebook +} RA144Context; static int ra144_decode_init(AVCodecContext * avctx) { - Real144_internal *glob=avctx->priv_data; - - memset(glob,0,sizeof(Real144_internal)); - glob->resetflag=1; - glob->swapbuf1=glob->swapb1a; - glob->swapbuf2=glob->swapb2a; - glob->swapbuf1alt=glob->swapb1b; - glob->swapbuf2alt=glob->swapb2b; + RA144Context *ractx = avctx->priv_data; - memcpy(glob->wavtable1,wavtable1,sizeof(wavtable1)); - memcpy(glob->wavtable2,wavtable2,sizeof(wavtable2)); + ractx->lpc_refl = ractx->lpc_tables[0]; + ractx->lpc_coef = ractx->lpc_tables[1]; + ractx->lpc_refl_old = ractx->lpc_tables[2]; + ractx->lpc_coef_old = ractx->lpc_tables[3]; - return 0; + return 0; } -static void final(Real144_internal *glob, short *i1, short *i2, void *out, int *statbuf, int len); -static void add_wav(Real144_internal *glob, int n, int f, int m1, int m2, int m3, short *s1, short *s2, short *s3, short *dest); -static int irms(short *data, int factor); -static void rotate_block(short *source, short *target, int offset); -/* lookup square roots in table */ +/** + * Evaluate sqrt(x << 24). x must fit in 20 bits. This value is evaluated in an + * odd way to make the output identical to the binary decoder. + */ static int t_sqrt(unsigned int x) { - int s=0; - while (x>0xfff) { s++; x=x>>2; } - return (sqrt_table[x]< 0xfff) { + s++; + x = x >> 2; + } + + return (ff_sqrt(x << 20) << s) << 2; } -/* do 'voice' */ -static void do_voice(int *a1, int *a2) +/** + * Evaluate the LPC filter coefficients from the reflection coefficients. + * Does the inverse of the eval_refl() function. + */ +static void eval_coefs(const int *refl, int *coefs) { - int buffer[10]; - int *b1,*b2; - int x,y; - int *ptr,*tmp; + int buffer[10]; + int *b1 = buffer; + int *b2 = coefs; + int x, y; - b1=buffer; - b2=a2; + for (x=0; x < 10; x++) { + b1[x] = refl[x] << 4; - for (x=0;x<10;x++) { - b1[x]=(*a1)<<4; + for (y=0; y < x; y++) + b1[y] = ((refl[x] * b2[x-y-1]) >> 12) + b2[y]; - if(x>0) { - ptr=b2+x; - for (y=0;y<=x-1;y++) - b1[y]=(((*a1)*(*(--ptr)))>>12)+b2[y]; + FFSWAP(int *, b1, b2); } - tmp=b1; - b1=b2; - b2=tmp; - a1++; - } - ptr=a2+10; - while (ptr>a2) (*a2++)>>=4; -} - -/* do quarter-block output */ -static void do_output_subblock(Real144_internal *glob, unsigned int x) -{ - int a,b,c,d,e,f,g; - - if (x==1) memset(glob->buffer,0,20); - if ((*glob->iptr)==0) a=0; - else a=(*glob->iptr)+HALFBLOCK-1; - glob->iptr++; - b=*(glob->iptr++); - c=*(glob->iptr++); - d=*(glob->iptr++); - if (a) rotate_block(glob->buffer_2,glob->buffer_a,a); - memcpy(glob->buffer_b,etable1+b*BLOCKSIZE,BLOCKSIZE*2); - e=((ftable1[b]>>4)*glob->gval)>>8; - memcpy(glob->buffer_c,etable2+c*BLOCKSIZE,BLOCKSIZE*2); - f=((ftable2[c]>>4)*glob->gval)>>8; - if (a) g=irms(glob->buffer_a,glob->gval)>>12; - else g=0; - add_wav(glob,d,a,g,e,f,glob->buffer_a,glob->buffer_b,glob->buffer_c,glob->buffer_d); - memmove(glob->buffer_2,glob->buffer_2+BLOCKSIZE,(BUFFERSIZE-BLOCKSIZE)*2); - memcpy(glob->buffer_2+BUFFERSIZE-BLOCKSIZE,glob->buffer_d,BLOCKSIZE*2); - final(glob,glob->gsp,glob->buffer_d,glob->output_buffer,glob->buffer,BLOCKSIZE); + for (x=0; x < 10; x++) + coefs[x] >>= 4; } /* rotate block */ -static void rotate_block(short *source, short *target, int offset) +static void rotate_block(const int16_t *source, int16_t *target, int offset) { - short *end; - short *ptr1; - short *ptr2; - short *ptr3; - ptr2=source+BUFFERSIZE; - ptr3=ptr1=ptr2-offset; - end=target+BLOCKSIZE; - while (targetp1;p1++) sum+=(*p1)*(*p1); - if (sum==0) return 0; /* OOPS - division by zero */ - return (0x20000000/(t_sqrt(sum)>>8))*factor; + unsigned int i, sum = 0; + + for (i=0; i < BLOCKSIZE; i++) + sum += data[i] * data[i]; + + if (sum == 0) + return 0; /* OOPS - division by zero */ + + return (0x20000000 / (t_sqrt(sum) >> 8)) * factor; } /* multiply/add wavetable */ -static void add_wav(Real144_internal *glob, int n, int f, int m1, int m2, int m3, short *s1, short *s2, short *s3, short *dest) +static void add_wav(int n, int skip_first, int *m, const int16_t *s1, + const int8_t *s2, const int8_t *s3, int16_t *dest) { - int a,b,c; - short *ptr,*ptr2; - - ptr=glob->wavtable1+n*9; - ptr2=glob->wavtable2+n*9; - if (f!=0) { - a=((*ptr)*m1)>>((*ptr2)+1); - } else { - a=0; - } - ptr++;ptr2++; - b=((*ptr)*m2)>>((*ptr2)+1); - ptr++;ptr2++; - c=((*ptr)*m3)>>((*ptr2)+1); - ptr2=(ptr=dest)+BLOCKSIZE; - if (f!=0) - while (ptr>12; - else - while (ptr>12; + int i; + int v[3]; + + v[0] = 0; + for (i=!skip_first; i<3; i++) + v[i] = (wavtable1[n][i] * m[i]) >> (wavtable2[n][i] + 1); + + for (i=0; i < BLOCKSIZE; i++) + dest[i] = ((*(s1++))*v[0] + (*(s2++))*v[1] + (*(s3++))*v[2]) >> 12; } -static void final(Real144_internal *glob, short *i1, short *i2, void *out, int *statbuf, int len) +static void final(const int16_t *i1, const int16_t *i2, + void *out, int *statbuf, int len) { - int x,sum; - int buffer[10]; - short *ptr; - short *ptr2; - - memcpy(glob->work,statbuf,20); - memcpy(glob->work+10,i2,len*2); - - buffer[9]=i1[0]; - buffer[8]=i1[1]; - buffer[7]=i1[2]; - buffer[6]=i1[3]; - buffer[5]=i1[4]; - buffer[4]=i1[5]; - buffer[3]=i1[6]; - buffer[2]=i1[7]; - buffer[1]=i1[8]; - buffer[0]=i1[9]; - - ptr2=(ptr=glob->work)+len; - while (ptr>12; - x=ptr[10]-sum; - if (x<-32768 || x>32767) - { - memset(out,0,len*2); - memset(statbuf,0,20); - return; + int x, i; + uint16_t work[50]; + int16_t *ptr = work; + + memcpy(work, statbuf,20); + memcpy(work + 10, i2, len * 2); + + for (i=0; i>= 12; + + new_val = ptr[10] - sum; + + if (new_val < -32768 || new_val > 32767) { + memset(out, 0, len * 2); + memset(statbuf, 0, 20); + return; + } + + ptr[10] = new_val; + ptr++; } - ptr[10]=x; - ptr++; - } - memcpy(out,ptr+10-len,len*2); - memcpy(statbuf,ptr,20); + + memcpy(out, work+10, len * 2); + memcpy(statbuf, work + 40, 20); } -/* Decode 20-byte input */ -static void unpack_input(unsigned char *input, unsigned int *output) +static unsigned int rms(const int *data, int f, RA144Context *ractx) { - unsigned int outbuffer[28]; - unsigned short inbuffer[10]; - unsigned int x; - unsigned int *ptr; - - /* fix endianness */ - for (x=0;x<20;x+=2) - inbuffer[x/2]=(input[x]<<8)+input[x+1]; - - /* unpack */ - ptr=outbuffer; - *(ptr++)=27; - *(ptr++)=(inbuffer[0]>>10)&0x3f; - *(ptr++)=(inbuffer[0]>>5)&0x1f; - *(ptr++)=inbuffer[0]&0x1f; - *(ptr++)=(inbuffer[1]>>12)&0xf; - *(ptr++)=(inbuffer[1]>>8)&0xf; - *(ptr++)=(inbuffer[1]>>5)&7; - *(ptr++)=(inbuffer[1]>>2)&7; - *(ptr++)=((inbuffer[1]<<1)&6)|((inbuffer[2]>>15)&1); - *(ptr++)=(inbuffer[2]>>12)&7; - *(ptr++)=(inbuffer[2]>>10)&3; - *(ptr++)=(inbuffer[2]>>5)&0x1f; - *(ptr++)=((inbuffer[2]<<2)&0x7c)|((inbuffer[3]>>14)&3); - *(ptr++)=(inbuffer[3]>>6)&0xff; - *(ptr++)=((inbuffer[3]<<1)&0x7e)|((inbuffer[4]>>15)&1); - *(ptr++)=(inbuffer[4]>>8)&0x7f; - *(ptr++)=(inbuffer[4]>>1)&0x7f; - *(ptr++)=((inbuffer[4]<<7)&0x80)|((inbuffer[5]>>9)&0x7f); - *(ptr++)=(inbuffer[5]>>2)&0x7f; - *(ptr++)=((inbuffer[5]<<5)&0x60)|((inbuffer[6]>>11)&0x1f); - *(ptr++)=(inbuffer[6]>>4)&0x7f; - *(ptr++)=((inbuffer[6]<<4)&0xf0)|((inbuffer[7]>>12)&0xf); - *(ptr++)=(inbuffer[7]>>5)&0x7f; - *(ptr++)=((inbuffer[7]<<2)&0x7c)|((inbuffer[8]>>14)&3); - *(ptr++)=(inbuffer[8]>>7)&0x7f; - *(ptr++)=((inbuffer[8]<<1)&0xfe)|((inbuffer[9]>>15)&1); - *(ptr++)=(inbuffer[9]>>8)&0x7f; - *(ptr++)=(inbuffer[9]>>1)&0x7f; - - *(output++)=outbuffer[11]; - for (x=1;x<11;*(output++)=outbuffer[x++]); - ptr=outbuffer+12; - for (x=0;x<16;x+=4) - { - *(output++)=ptr[x]; - *(output++)=ptr[x+2]; - *(output++)=ptr[x+3]; - *(output++)=ptr[x+1]; - } + int x; + unsigned int res = 0x10000; + int b = 0; + + for (x=0; x<10; x++) { + res = (((0x1000000 - (*data) * (*data)) >> 12) * res) >> 12; + + if (res == 0) + return 0; + + if (res > 0x10000) { + av_log(ractx, AV_LOG_ERROR, "Overflow. Broken sample?\n"); + return 0; + } + + while (res <= 0x3fff) { + b++; + res <<= 2; + } + data++; + } + + if (res > 0) + res = t_sqrt(res); + + res >>= (b + 10); + res = (res * f) >> 10; + return res; } -static unsigned int rms(int *data, int f) +/* do quarter-block output */ +static void do_output_subblock(RA144Context *ractx, + const uint16_t *lpc_coefs, unsigned int gval, + int16_t *output_buffer, GetBitContext *gb) { - int *c; - int x; - unsigned int res; - int b; - - c=data; - b=0; - res=0x10000; - for (x=0;x<10;x++) - { - res=(((0x1000000-(*c)*(*c))>>12)*res)>>12; - if (res==0) return 0; - if (res<=0x3fff) - { - while (res<=0x3fff) - { - b++; - res<<=2; - } + uint16_t buffer_a[40]; + uint16_t *block; + int cba_idx = get_bits(gb, 7); // index of the adaptive CB, 0 if none + int gain = get_bits(gb, 8); + int cb1_idx = get_bits(gb, 7); + int cb2_idx = get_bits(gb, 7); + int m[3]; + + if (cba_idx) { + cba_idx += HALFBLOCK - 1; + rotate_block(ractx->adapt_cb, buffer_a, cba_idx); + m[0] = irms(buffer_a, gval) >> 12; } else { - if (res>0x10000) - return 0; /* We're screwed, might as well go out with a bang. :P */ + m[0] = 0; } - c++; - } - if (res>0) res=t_sqrt(res); - res>>=(b+10); - res=(res*f)>>10; - return res; + m[1] = ((ftable1[cb1_idx] >> 4) * gval) >> 8; + m[2] = ((ftable2[cb2_idx] >> 4) * gval) >> 8; + + memmove(ractx->adapt_cb, ractx->adapt_cb + BLOCKSIZE, + (BUFFERSIZE - BLOCKSIZE) * 2); + + block = ractx->adapt_cb + BUFFERSIZE - BLOCKSIZE; + + add_wav(gain, cba_idx, m, buffer_a, etable1[cb1_idx], etable2[cb2_idx], + block); + + final(lpc_coefs, block, output_buffer, ractx->buffer, BLOCKSIZE); } -static void dec1(Real144_internal *glob, int *data, int *inp, int n, int f) +static void int_to_int16(int16_t *decsp, const int *inp) { - short *ptr,*end; + int i; - *(glob->decptr++)=rms(data,f); - glob->decptr++; - end=(ptr=glob->decsp)+(n*10); - while (ptrbuffer1; - bp2=glob->buffer2; - ptr2=(ptr3=glob->buffer2)+9; - sptr=in; - while (ptr2>=ptr3) - *(ptr3++)=*(sptr++); - - target+=9; - a=bp2[9]; - *target=a; - if (a+0x1000>0x1fff) - return 0; /* We're screwed, might as well go out with a bang. :P */ - c=8;u=a; - while (c>=0) - { - if (u==0x1000) u++; - if (u==0xfffff000) u--; - b=0x1000-((u*u)>>12); - if (b==0) b++; - ptr2=bp1; - ptr1=(ptr3=bp2)+c; - for (u=0;u<=c;u++) - *(ptr2++)=((*(ptr3++)-(((*target)*(*(ptr1--)))>>12))*(0x1000000/b))>>12; - *(--target)=u=bp1[(c--)]; - if ((u+0x1000)>0x1fff) retval=1; - temp=bp2; - bp2=bp1; - bp1=temp; - } - return retval; + int retval = 0; + int b, c, i; + unsigned int u; + int buffer1[10]; + int buffer2[10]; + int *bp1 = buffer1; + int *bp2 = buffer2; + + for (i=0; i < 10; i++) + buffer2[i] = coefs[i]; + + u = refl[9] = bp2[9]; + + if (u + 0x1000 > 0x1fff) { + av_log(ractx, AV_LOG_ERROR, "Overflow. Broken sample?\n"); + return 0; + } + + for (c=8; c >= 0; c--) { + if (u == 0x1000) + u++; + + if (u == 0xfffff000) + u--; + + b = 0x1000-((u * u) >> 12); + + if (b == 0) + b++; + + for (u=0; u<=c; u++) + bp1[u] = ((bp2[u] - ((refl[c+1] * bp2[c-u]) >> 12)) * (0x1000000 / b)) >> 12; + + refl[c] = u = bp1[c]; + + if ((u + 0x1000) > 0x1fff) + retval = 1; + + FFSWAP(int *, bp1, bp2); + } + return retval; } -static void dec2(Real144_internal *glob, int *data, int *inp, int n, int f, int *inp2, int l) +static int interp(RA144Context *ractx, int16_t *decsp, int block_num, + int copynew, int energy) { - unsigned int *ptr1,*ptr2; - int work[10]; - int a,b; - int x; - int result; - - if(l+1decsp=glob->sptr=glob->gbuf2; - glob->decptr=glob->gbuf1; - } - ptr1=inp; - ptr2=inp2; - for (x=0;x<10*n;x++) - *(glob->sptr++)=(a*(*ptr1++)+b*(*ptr2++))>>2; - result=eq(glob,glob->decsp,work); - if (result==1) - { - dec1(glob,data,inp,n,f); - } else { - *(glob->decptr++)=rms(work,f); - glob->decptr++; - } - glob->decsp+=n*10; + int work[10]; + int a = block_num + 1; + int b = NBLOCKS - a; + int x; + + // Interpolate block coefficients from the this frame forth block and + // last frame forth block + for (x=0; x<30; x++) + decsp[x] = (a * ractx->lpc_coef[x] + b * ractx->lpc_coef_old[x])>> 2; + + if (eval_refl(decsp, work, ractx)) { + // The interpolated coefficients are unstable, copy either new or old + // coefficients + if (copynew) { + int_to_int16(decsp, ractx->lpc_coef); + return rms(ractx->lpc_refl, energy, ractx); + } else { + int_to_int16(decsp, ractx->lpc_coef_old); + return rms(ractx->lpc_refl_old, energy, ractx); + } + } else { + return rms(work, energy, ractx); + } } /* Uncompress one block (20 bytes -> 160*2 bytes) */ static int ra144_decode_frame(AVCodecContext * avctx, - void *vdata, int *data_size, - uint8_t * buf, int buf_size) + void *vdata, int *data_size, + const uint8_t * buf, int buf_size) { - unsigned int a,b,c; - long s; - signed short *shptr; - unsigned int *lptr,*temp; - const short **dptr; - int16_t *datao; - int16_t *data = vdata; - Real144_internal *glob=avctx->priv_data; + static const uint8_t sizes[10] = {6, 5, 5, 4, 4, 3, 3, 3, 3, 2}; + unsigned int refl_rms[4]; // RMS of the reflection coefficients + uint16_t block_coefs[4][30]; // LPC coefficients of each sub-block + int i, c; + int16_t *data = vdata; + unsigned int energy; + + RA144Context *ractx = avctx->priv_data; + GetBitContext gb; + + if(buf_size < 20) { + av_log(avctx, AV_LOG_ERROR, + "Frame too small (%d bytes). Truncated file?\n", buf_size); + return buf_size; + } + init_get_bits(&gb, buf, 20 * 8); - if(buf_size==0) - return 0; + for (i=0; i<10; i++) + // "<< 1"? Doesn't this make one value out of two of the table useless? + ractx->lpc_refl[i] = decodetable[i][get_bits(&gb, sizes[i]) << 1]; - datao = data; - unpack_input(buf,glob->unpacked); + eval_coefs(ractx->lpc_refl, ractx->lpc_coef); - glob->iptr=glob->unpacked; - glob->val=decodetable[0][(*(glob->iptr++))<<1]; + energy = decodeval[get_bits(&gb, 5) << 1]; // Useless table entries? - dptr=decodetable+1; - lptr=glob->swapbuf1; - while (lptrswapbuf1+10) - *(lptr++)=(*(dptr++))[(*(glob->iptr++))<<1]; + refl_rms[0] = interp(ractx, block_coefs[0], 0, 0, ractx->old_energy); + refl_rms[1] = interp(ractx, block_coefs[1], 1, energy > ractx->old_energy, + t_sqrt(energy*ractx->old_energy) >> 12); + refl_rms[2] = interp(ractx, block_coefs[2], 2, 1, energy); + refl_rms[3] = rms(ractx->lpc_refl, energy, ractx); - do_voice(glob->swapbuf1,glob->swapbuf2); + int_to_int16(block_coefs[3], ractx->lpc_coef); - a=t_sqrt(glob->val*glob->oldval)>>12; + /* do output */ + for (c=0; c<4; c++) { + do_output_subblock(ractx, block_coefs[c], refl_rms[c], data, &gb); - for (c=0;cswapbuf1,glob->swapbuf2,3,glob->val); - } else { - if (c*2==(NBLOCKS-2)) { - if (glob->oldvalval) { - dec2(glob,glob->swapbuf1,glob->swapbuf2,3,a,glob->swapbuf2alt,c); - } else { - dec2(glob,glob->swapbuf1alt,glob->swapbuf2alt,3,a,glob->swapbuf2,c); - } - } else { - if (c*2<(NBLOCKS-2)) { - dec2(glob,glob->swapbuf1alt,glob->swapbuf2alt,3,glob->oldval,glob->swapbuf2,c); - } else { - dec2(glob,glob->swapbuf1,glob->swapbuf2,3,glob->val,glob->swapbuf2alt,c); + for (i=0; igval=glob->gbuf1[c*2]; - glob->gsp=glob->gbuf2+b; - do_output_subblock(glob,glob->resetflag); - glob->resetflag=0; - - shptr=glob->output_buffer; - while (shptroutput_buffer+BLOCKSIZE) { - s=*(shptr++)<<2; - *data=av_clip_int16(s); - data++; - } - b+=30; - } - - glob->oldval=glob->val; - temp=glob->swapbuf1alt; - glob->swapbuf1alt=glob->swapbuf1; - glob->swapbuf1=temp; - temp=glob->swapbuf2alt; - glob->swapbuf2alt=glob->swapbuf2; - glob->swapbuf2=temp; - *data_size=(data-datao)*sizeof(*data); - return 20; + + ractx->old_energy = energy; + + FFSWAP(unsigned int *, ractx->lpc_refl_old, ractx->lpc_refl); + FFSWAP(unsigned int *, ractx->lpc_coef_old, ractx->lpc_coef); + + *data_size = 2*160; + return 20; } @@ -509,9 +392,10 @@ AVCodec ra_144_decoder = "real_144", CODEC_TYPE_AUDIO, CODEC_ID_RA_144, - sizeof(Real144_internal), + sizeof(RA144Context), ra144_decode_init, NULL, NULL, ra144_decode_frame, + .long_name = "RealAudio 1.0 (14.4K)", };