File: Dither.cpp

package info (click to toggle)
audacity 3.7.7%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 134,800 kB
  • sloc: cpp: 366,277; ansic: 198,323; lisp: 7,761; sh: 3,414; python: 1,501; xml: 1,385; perl: 854; makefile: 125
file content (418 lines) | stat: -rw-r--r-- 12,831 bytes parent folder | download | duplicates (4)
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
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
/**********************************************************************

  Audacity: A Digital Audio Editor

  Dither.cpp

  Steve Harris
  Markus Meyer

*******************************************************************//*!

\class Dither
\brief

  This class implements various functions for dithering and is derived
  from the dither code in the Ardour project, written by Steve Harris.

  Dithering is only done if it really is necessary. Otherwise (e.g.
  when the source and destination format of the samples is the same),
  the samples are only copied or converted. However, copied samples
  are always checked for out-of-bounds values and possibly clipped
  accordingly.

  These dither algorithms are currently implemented:
  - No dithering at all
  - Rectangle dithering
  - Triangle dithering
  - Noise-shaped dithering

Dither class. You must construct an instance because it keeps
state. Call Dither::Apply() to apply the dither. You can call
Reset() between subsequent dithers to reset the dither state
and get deterministic behaviour.

*//*******************************************************************/


#include "Dither.h"

#include "Internat.h"
#include "Prefs.h"

// Erik de Castro Lopo's header file that
// makes sure that we have lrint and lrintf
// (Note: this file should be included first)
#include "float_cast.h"

#include <stdlib.h>
#include <math.h>
#include <string.h>
//#include <sys/types.h>
//#include <memory.h>
//#include <assert.h>

#include <wx/defs.h>

//////////////////////////////////////////////////////////////////////////

// Constants for the noise shaping buffer
constexpr int BUF_SIZE = 8;
constexpr int BUF_MASK = 7;

// Lipshitz's minimally audible FIR
const float SHAPED_BS[] = { 2.033f, -2.165f, 1.959f, -1.590f, 0.6149f };

// Dither state
struct State {
    int mPhase;
    float mTriangleState;
    float mBuffer[8 /* = BUF_SIZE */];
} mState;

using Ditherer = float (*)(State &, float);

// This is supposed to produce white noise and no dc
static inline float DITHER_NOISE()
{
    return rand() / (float)RAND_MAX - 0.5f;
}

// Defines for sample conversion
constexpr auto CONVERT_DIV16 = float(1<<15);
constexpr auto CONVERT_DIV24 = float(1<<23);

// Dereference sample pointer and convert to float sample
static inline float FROM_INT16(const short *ptr)
{
    return *ptr / CONVERT_DIV16;
}

static inline float FROM_INT24(const int *ptr)
{
    return *ptr / CONVERT_DIV24;
}

// For float, we internally allow values greater than 1.0, which
// would blow up the dithering to int values.  FROM_FLOAT is
// only used to dither to int, so clip here.
static inline float FROM_FLOAT(const float *ptr)
{
    return *((float*)(ptr)) > 1.0
        ?  1.0 :
        *((float*)(ptr)) < -1.0
            ? -1.0 :
            *((float*)(ptr));
}

// Store float sample 'sample' into pointer 'ptr', clip it, if necessary
template<typename dst_type>
static inline void IMPLEMENT_STORE(
    dst_type *ptr, float sample, dst_type min_bound, dst_type max_bound)
{
    int x = lrintf(sample);
    if (x > max_bound)
        *ptr = max_bound;
    else if (x<(min_bound))
        *ptr = min_bound;
    else
        *ptr = static_cast<dst_type>(x);
}

// Dither single float 'sample' and store it in pointer 'dst', using 'dither' as algorithm
static inline void DITHER_TO_INT16(Ditherer dither, State &state,
   short *dst, float sample)
{
    IMPLEMENT_STORE<short>(dst,
        dither(state, sample * CONVERT_DIV16),
        short(-32768), short(32767));
}

// Dither single float 'sample' and store it in pointer 'dst', using 'dither' as algorithm
static inline void DITHER_TO_INT24(Ditherer dither, State &state,
   int *dst, float sample)
{
    IMPLEMENT_STORE<int>(dst,
        dither(state, sample * CONVERT_DIV24), -8388608, 8388607);
}

// Implement one single dither step

// Implement a dithering loop
template<typename srcType, typename dstType>
static inline void DITHER_LOOP( Ditherer dither, State &state,
    void (*store)(Ditherer, State &, dstType *, float),
    float (*load)(const srcType *),
    samplePtr dst, sampleFormat dstFormat, size_t dstStride,
    constSamplePtr src, sampleFormat srcFormat, size_t srcStride, size_t len)
{
    char *d;
    const char *s;
    unsigned int ii;
    for (d = (char*)dst, s = (char*)src, ii = 0;
        ii < len;
        ii++, d += SAMPLE_SIZE(dstFormat) * dstStride,
        s += SAMPLE_SIZE(srcFormat) * srcStride) {
            store(dither, state, reinterpret_cast<dstType *>(d), load(reinterpret_cast<const srcType *>(s)));
        }
}

// Implement a dither. There are only 3 cases where we must dither,
// in all other cases, no dithering is necessary.
static inline void DITHER( Ditherer dither, State &state,
   samplePtr dst, sampleFormat dstFormat, size_t dstStride,
   constSamplePtr src, sampleFormat srcFormat, size_t srcStride, size_t len)
{
    if (srcFormat == int24Sample && dstFormat == int16Sample)
        DITHER_LOOP<int, short>(dither, state,
            DITHER_TO_INT16, FROM_INT24, dst,
            int16Sample, dstStride, src, int24Sample, srcStride, len);
    else if (srcFormat == floatSample && dstFormat == int16Sample)
        DITHER_LOOP<float, short>(dither, state,
            DITHER_TO_INT16, FROM_FLOAT, dst,
            int16Sample, dstStride, src, floatSample, srcStride, len);
    else if (srcFormat == floatSample && dstFormat == int24Sample)
        DITHER_LOOP<float, int>(dither, state,
            DITHER_TO_INT24, FROM_FLOAT, dst,
            int24Sample, dstStride, src, floatSample, srcStride, len);
    else { wxASSERT(false); }
}


static inline float NoDither(State &, float sample);
static inline float RectangleDither(State &, float sample);
static inline float TriangleDither(State &state, float sample);
static inline float ShapedDither(State &state, float sample);

Dither::Dither()
{
    // On startup, initialize dither by resetting values
    Reset();
}

void Dither::Reset()
{
    mState.mTriangleState = 0;
    mState.mPhase = 0;
    memset(mState.mBuffer, 0, sizeof(float) * BUF_SIZE);
}

// This only decides if we must dither at all, the dithers
// are all implemented using macros.
//
// "source" and "dest" can contain either interleaved or non-interleaved
// samples.  They do not have to be the same...one can be interleaved while
// the otner non-interleaved.
//
// The "len" argument specifies the number of samples to process.
//
// If either stride value equals 1 then the corresponding buffer contains
// non-interleaved samples.
//
// If either stride value is greater than 1 then the corresponding buffer
// contains interleaved samples and they will be processed by skipping every
// stride number of samples.

void Dither::Apply(enum DitherType ditherType,
                   constSamplePtr source, sampleFormat sourceFormat,
                   samplePtr dest, sampleFormat destFormat,
                   unsigned int len,
                   unsigned int sourceStride /* = 1 */,
                   unsigned int destStride /* = 1 */)
{
    unsigned int i;

    // This code is not designed for 16-bit or 64-bit machine
    wxASSERT(sizeof(int) == 4);
    wxASSERT(sizeof(short) == 2);

    // Check parameters
    wxASSERT(source);
    wxASSERT(dest);
    wxASSERT(len >= 0);
    wxASSERT(sourceStride > 0);
    wxASSERT(destStride > 0);

    if (len == 0)
        return; // nothing to do

    if (destFormat == sourceFormat)
    {
        // No need to dither, because source and destination
        // format are the same. Just copy samples.
        if (destStride == 1 && sourceStride == 1)
            memcpy(dest, source, len * SAMPLE_SIZE(destFormat));
        else
        {
            if (sourceFormat == floatSample)
            {
                auto d = (float*)dest;
                auto s = (const float*)source;

                for (i = 0; i < len; i++, d += destStride, s += sourceStride)
                    *d = *s;
            } else
            if (sourceFormat == int24Sample)
            {
                auto d = (int*)dest;
                auto s = (const int*)source;

                for (i = 0; i < len; i++, d += destStride, s += sourceStride)
                    *d = *s;
            } else
            if (sourceFormat == int16Sample)
            {
                auto d = (short*)dest;
                auto s = (const short*)source;

                for (i = 0; i < len; i++, d += destStride, s += sourceStride)
                    *d = *s;
            } else {
                wxASSERT(false); // source format unknown
            }
        }
    } else
    if (destFormat == floatSample)
    {
        // No need to dither, just convert samples to float.
        // No clipping should be necessary.
        auto d = (float*)dest;

        if (sourceFormat == int16Sample)
        {
            auto s = (const short*)source;
            for (i = 0; i < len; i++, d += destStride, s += sourceStride)
                *d = FROM_INT16(s);
        } else
        if (sourceFormat == int24Sample)
        {
            auto s = (const int*)source;
            for (i = 0; i < len; i++, d += destStride, s += sourceStride)
                *d = FROM_INT24(s);
        } else {
            wxASSERT(false); // source format unknown
        }
    } else
    if (destFormat == int24Sample && sourceFormat == int16Sample)
    {
        // Special case when promoting 16 bit to 24 bit
        auto d = (int*)dest;
        auto s = (const short*)source;
        for (i = 0; i < len; i++, d += destStride, s += sourceStride)
            *d = ((int)*s) << 8;
    } else
    {
        // We must do dithering
        switch (ditherType)
        {
        case DitherType::none:
            DITHER(NoDither, mState, dest, destFormat, destStride, source, sourceFormat, sourceStride, len);
            break;
        case DitherType::rectangle:
            DITHER(RectangleDither, mState, dest, destFormat, destStride, source, sourceFormat, sourceStride, len);
            break;
        case DitherType::triangle:
            Reset(); // reset dither filter for this NEW conversion
            DITHER(TriangleDither, mState, dest, destFormat, destStride, source, sourceFormat, sourceStride, len);
            break;
        case DitherType::shaped:
            Reset(); // reset dither filter for this NEW conversion
            DITHER(ShapedDither, mState, dest, destFormat, destStride, source, sourceFormat, sourceStride, len);
            break;
        default:
            wxASSERT(false); // unknown dither algorithm
        }
    }
}

// Dither implementations

// No dither, just return sample
inline float NoDither(State &, float sample)
{
    return sample;
}

// Rectangle dithering, apply one-step noise
inline float RectangleDither(State &, float sample)
{
    return sample - DITHER_NOISE();
}

// Triangle dither - high pass filtered
inline float TriangleDither(State &state, float sample)
{
    float r = DITHER_NOISE();
    float result = sample + r - state.mTriangleState;
    state.mTriangleState = r;

    return result;
}

// Shaped dither
inline float ShapedDither(State &state, float sample)
{
    // Generate triangular dither, +-1 LSB, flat psd
    float r = DITHER_NOISE() + DITHER_NOISE();
    if(sample != sample)  // test for NaN
       sample = 0; // and do the best we can with it

    // Run FIR
    float xe = sample + state.mBuffer[state.mPhase] * SHAPED_BS[0]
        + state.mBuffer[(state.mPhase - 1) & BUF_MASK] * SHAPED_BS[1]
        + state.mBuffer[(state.mPhase - 2) & BUF_MASK] * SHAPED_BS[2]
        + state.mBuffer[(state.mPhase - 3) & BUF_MASK] * SHAPED_BS[3]
        + state.mBuffer[(state.mPhase - 4) & BUF_MASK] * SHAPED_BS[4];

    // Accumulate FIR and triangular noise
    float result = xe + r;

    // Roll buffer and store last error
    state.mPhase = (state.mPhase + 1) & BUF_MASK;
    state.mBuffer[state.mPhase] = xe - lrintf(result);

    return result;
}

static const std::initializer_list<EnumValueSymbol> choicesDither{
   { XO("None") },
   { XO("Rectangle") },
   { XC("Triangle", "dither") },
   { XO("Shaped") },
};
static auto intChoicesDither = {
   DitherType::none,
   DitherType::rectangle,
   DitherType::triangle,
   DitherType::shaped,
};

EnumSetting< DitherType > Dither::FastSetting{
   wxT("Quality/DitherAlgorithmChoice"),
   choicesDither,
   0, // none

   // for migrating old preferences:
   intChoicesDither,
   wxT("Quality/DitherAlgorithm")
};

EnumSetting< DitherType > Dither::BestSetting{
   wxT("Quality/HQDitherAlgorithmChoice"),
   choicesDither,
   3, // shaped

   // for migrating old preferences:
   intChoicesDither,
   wxT("Quality/HQDitherAlgorithm")
};

DitherType Dither::FastDitherChoice()
{
   return (DitherType) FastSetting.ReadEnum();
}

DitherType Dither::BestDitherChoice()
{
   return (DitherType) BestSetting.ReadEnum();
}