sox.git  about / heads / tags
master: git://git.code.sf.net/p/sox/code
blob f64b7d5c2787c4ae93d25a90e4aefae4b9573c69 12598 bytes (raw)
$ git show pu:src/adpcm.c	# shows this blob on the CLI

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
 
/* adpcm.c  codex functions for MS_ADPCM data
 *          (hopefully) provides interoperability with
 *          Microsoft's ADPCM format, but, as usual,
 *          see LACK-OF-WARRANTY information below.
 *
 *      Copyright (C) 1999 Stanley J. Brooks <stabro@megsinet.net>
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

/*
 * November 22, 1999
 *  specs I've seen are unclear about ADPCM supporting more than 2 channels,
 *  but these routines support more channels in a manner which looks (IMHO)
 *  like the most natural extension.
 *
 *  Remark: code still turbulent, encoding very new.
 *
 */

#include "sox_i.h"
#include "adpcm.h"

#include <sys/types.h>
#include <stdio.h>

typedef struct {
        sox_sample_t  step;      /* step size */
        short coef[2];
} MsState_t;

#define lsbshortldi(x,p) { (x)=((short)((int)(p)[0] + ((int)(p)[1]<<8))); (p) += 2; }

/*
 * Lookup tables for MS ADPCM format
 */

/* these are step-size adjust factors, where
 * 1.0 is scaled to 0x100
 */
static const
sox_sample_t stepAdjustTable[] = {
        230, 230, 230, 230, 307, 409, 512, 614,
        768, 614, 512, 409, 307, 230, 230, 230
};

/* TODO : The first 7 lsx_ms_adpcm_i_coef sets are always hardcoded and must
   appear in the actual WAVE file.  They should be read in
   in case a sound program added extras to the list. */

const short lsx_ms_adpcm_i_coef[7][2] = {
                        { 256,   0},
                        { 512,-256},
                        {   0,   0},
                        { 192,  64},
                        { 240,   0},
                        { 460,-208},
                        { 392,-232}
};

extern void *lsx_ms_adpcm_alloc(unsigned chans)
{
        return lsx_malloc(chans * sizeof(MsState_t));
}

static inline sox_sample_t AdpcmDecode(sox_sample_t c, MsState_t *state,
                               sox_sample_t sample1, sox_sample_t sample2)
{
        sox_sample_t vlin;
        sox_sample_t sample;
        sox_sample_t step;

        /** Compute next step value **/
        step = state->step;
        {
                sox_sample_t nstep;
                nstep = (stepAdjustTable[c] * step) >> 8;
                state->step = (nstep < 16)? 16:nstep;
        }

        /** make linear prediction for next sample **/
        vlin =
                        ((sample1 * state->coef[0]) +
                         (sample2 * state->coef[1])) >> 8;
        /** then add the code*step adjustment **/
        c -= (c & 0x08) << 1;
        sample = (c * step) + vlin;

        if (sample > 0x7fff) sample = 0x7fff;
        else if (sample < -0x8000) sample = -0x8000;

        return (sample);
}

/* lsx_ms_adpcm_block_expand_i() outputs interleaved samples into one output buffer */
const char *lsx_ms_adpcm_block_expand_i(
        void *priv,
        unsigned chans,          /* total channels             */
        int nCoef,
        const short *coef,
        const unsigned char *ibuff,/* input buffer[blockAlign]   */
        SAMPL *obuff,       /* output samples, n*chans    */
        int n               /* samples to decode PER channel */
)
{
  const unsigned char *ip;
  unsigned ch;
  const char *errmsg = NULL;
  MsState_t *state = priv;  /* One decompressor state for each channel */

  /* Read the four-byte header for each channel */
  ip = ibuff;
  for (ch = 0; ch < chans; ch++) {
    unsigned char bpred = *ip++;
    if (bpred >= nCoef) {
      errmsg = "MSADPCM bpred >= nCoef, arbitrarily using 0\n";
      bpred = 0;
    }
    state[ch].coef[0] = coef[(int)bpred*2+0];
    state[ch].coef[1] = coef[(int)bpred*2+1];
  }

  for (ch = 0; ch < chans; ch++)
    lsbshortldi(state[ch].step, ip);

  /* sample1's directly into obuff */
  for (ch = 0; ch < chans; ch++)
    lsbshortldi(obuff[chans+ch], ip);

  /* sample2's directly into obuff */
  for (ch = 0; ch < chans; ch++)
    lsbshortldi(obuff[ch], ip);

  {
    unsigned ch2;
    unsigned char b;
    short *op, *top, *tmp;

    /* already have 1st 2 samples from block-header */
    op = obuff + 2*chans;
    top = obuff + n*chans;

    ch2 = 0;
    while (op < top) { /*** N.B. Without int casts, crashes on 64-bit arch ***/
      b = *ip++;
      tmp = op;
      *op++ = AdpcmDecode(b >> 4, state+ch2, tmp[-(int)chans], tmp[-(int)(2*chans)]);
      if (++ch2 == chans) ch2 = 0;
      tmp = op;
      *op++ = AdpcmDecode(b&0x0f, state+ch2, tmp[-(int)chans], tmp[-(int)(2*chans)]);
      if (++ch2 == chans) ch2 = 0;
    }
  }
  return errmsg;
}

static int AdpcmMashS(
        unsigned ch,              /* channel number to encode, REQUIRE 0 <= ch < chans  */
        unsigned chans,           /* total channels */
        SAMPL v[2],          /* values to use as starting 2 */
        const short coef[2],/* lin predictor coeffs */
        const SAMPL *ibuff,  /* ibuff[] is interleaved input samples */
        int n,               /* samples to encode PER channel */
        int *iostep,         /* input/output step, REQUIRE 16 <= *st <= 0x7fff */
        unsigned char *obuff /* output buffer[blockAlign], or NULL for no output  */
)
{
        const SAMPL *ip, *itop;
        unsigned char *op;
        int ox = 0;      /*  */
        int d, v0, v1, step;
        double d2;       /* long long is okay also, speed abt the same */

        ip = ibuff + ch;       /* point ip to 1st input sample for this channel */
        itop = ibuff + n*chans;
        v0 = v[0];
        v1 = v[1];
        d = *ip - v1; ip += chans; /* 1st input sample for this channel */
        d2 = d*d;  /* d2 will be sum of squares of errors, given input v0 and *st */
        d = *ip - v0; ip += chans; /* 2nd input sample for this channel */
        d2 += d*d;

        step = *iostep;

        op = obuff;            /* output pointer (or NULL) */
        if (op) {              /* NULL means don't output, just compute the rms error */
                op += chans;         /* skip bpred indices */
                op += 2*ch;          /* channel's stepsize */
                op[0] = step; op[1] = step>>8;
                op += 2*chans;       /* skip to v0 */
                op[0] = v0; op[1] = v0>>8;
                op += 2*chans;       /* skip to v1 */
                op[0] = v1; op[1] = v1>>8;
                op = obuff+7*chans;  /* point to base of output nibbles */
                ox = 4*ch;
        }
        for (; ip < itop; ip+=chans) {
                int vlin,d3,dp,c;

          /* make linear prediction for next sample */
                vlin = (v0 * coef[0] + v1 * coef[1]) >> 8;
                d3 = *ip - vlin;  /* difference between linear prediction and current sample */
                dp = d3 + (step<<3) + (step>>1);
                c = 0;
                if (dp>0) {
                        c = dp/step;
                        if (c>15) c = 15;
                }
                c -= 8;
                dp = c * step;   /* quantized estimate of samp - vlin */
                c &= 0x0f;       /* mask to 4 bits */

                v1 = v0; /* shift history */
                v0 = vlin + dp;
                if (v0<-0x8000) v0 = -0x8000;
                else if (v0>0x7fff) v0 = 0x7fff;

                d3 = *ip - v0;
                d2 += d3*d3; /* update square-error */

                if (op) {   /* if we want output, put it in proper place */
                        op[ox>>3] |= (ox&4)? c:(c<<4);
                        ox += 4*chans;
                        lsx_debug_more("%.1x",c);

                }

                /* Update the step for the next sample */
                step = (stepAdjustTable[c] * step) >> 8;
                if (step < 16) step = 16;

        }
        if (op) lsx_debug_more("\n");
        d2 /= n; /* be sure it's non-negative */
        lsx_debug_more("ch%d: st %d->%d, d %.1f\n", ch, *iostep, step, sqrt(d2));
        *iostep = step;
        return (int) sqrt(d2);
}

static inline void AdpcmMashChannel(
        unsigned ch,             /* channel number to encode, REQUIRE 0 <= ch < chans  */
        unsigned chans,          /* total channels */
        const SAMPL *ip,    /* ip[] is interleaved input samples */
        int n,              /* samples to encode PER channel, REQUIRE */
        int *st,            /* input/output steps, 16<=st[i] */
        unsigned char *obuff /* output buffer[blockAlign] */
)
{
        SAMPL v[2];
        int n0,s0,s1,ss,smin;
        int dmin,k,kmin;

        n0 = n/2; if (n0>32) n0=32;
        if (*st<16) *st = 16;
        v[1] = ip[ch];
        v[0] = ip[ch+chans];

        dmin = 0; kmin = 0; smin = 0;
        /* for each of 7 standard coeff sets, we try compression
         * beginning with last step-value, and with slightly
         * forward-adjusted step-value, taking best of the 14
         */
        for (k=0; k<7; k++) {
                int d0,d1;
                ss = s0 = *st;
                d0=AdpcmMashS(ch, chans, v, lsx_ms_adpcm_i_coef[k], ip, n, &ss, NULL); /* with step s0 */

                s1 = s0;
                AdpcmMashS(ch, chans, v, lsx_ms_adpcm_i_coef[k], ip, n0, &s1, NULL);
                lsx_debug_more(" s32 %d\n",s1);
                ss = s1 = (3*s0+s1)/4;
                d1=AdpcmMashS(ch, chans, v, lsx_ms_adpcm_i_coef[k], ip, n, &ss, NULL); /* with step s1 */
                if (!k || d0<dmin || d1<dmin) {
                        kmin = k;
                        if (d0<=d1) {
                                dmin = d0;
                                smin = s0;
                        }else{
                                dmin = d1;
                                smin = s1;
                        }
                }
        }
        *st = smin;
        lsx_debug_more("kmin %d, smin %5d, ",kmin,smin);
        AdpcmMashS(ch, chans, v, lsx_ms_adpcm_i_coef[kmin], ip, n, st, obuff);
        obuff[ch] = kmin;
}

void lsx_ms_adpcm_block_mash_i(
        unsigned chans,          /* total channels */
        const SAMPL *ip,    /* ip[n*chans] is interleaved input samples */
        int n,              /* samples to encode PER channel */
        int *st,            /* input/output steps, 16<=st[i] */
        unsigned char *obuff,      /* output buffer[blockAlign]     */
        int blockAlign      /* >= 7*chans + chans*(n-2)/2.0    */
)
{
        unsigned ch;
        unsigned char *p;

        lsx_debug_more("AdpcmMashI(chans %d, ip %p, n %d, st %p, obuff %p, bA %d)\n",
            chans, (void *)ip, n, (void *)st, obuff, blockAlign);

        for (p=obuff+7*chans; p<obuff+blockAlign; p++) *p=0;

        for (ch=0; ch<chans; ch++)
                AdpcmMashChannel(ch, chans, ip, n, st+ch, obuff);
}

/*
 * lsx_ms_adpcm_samples_in(dataLen, chans, blockAlign, samplesPerBlock)
 *  returns the number of samples/channel which would be
 *  in the dataLen, given the other parameters ...
 *  if input samplesPerBlock is 0, then returns the max
 *  samplesPerBlock which would go into a block of size blockAlign
 *  Yes, it is confusing usage.
 */
size_t lsx_ms_adpcm_samples_in(
        size_t dataLen,
        size_t chans,
        size_t blockAlign,
        size_t samplesPerBlock
){
        size_t m, n;

        if (samplesPerBlock) {
                n = (dataLen / blockAlign) * samplesPerBlock;
                m = (dataLen % blockAlign);
        } else {
                n = 0;
                m = blockAlign;
        }
        if (m >= (size_t)(7*chans)) {
                m -= 7*chans;          /* bytes beyond block-header */
                m = (2*m)/chans + 2;   /* nibbles/chans + 2 in header */
                if (samplesPerBlock && m > samplesPerBlock) m = samplesPerBlock;
                n += m;
        }
        return n;
}

size_t lsx_ms_adpcm_bytes_per_block(
        size_t chans,
        size_t samplesPerBlock
)
{
        size_t n;
        n = 7*chans;  /* header */
        if (samplesPerBlock > 2)
                n += (((size_t)samplesPerBlock-2)*chans + 1)/2;
        return n;
}


git clone https://yhbt.net/sox.git