• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kunitoki / VelociLoops / 25003749265

27 Apr 2026 03:21PM UTC coverage: 94.38% (-5.6%) from 100.0%
25003749265

push

github

kunitoki
More fixes

1293 of 1370 relevant lines covered (94.38%)

1528636.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.38
/src/velociloops.cpp
1
#include "velociloops.h"
2

3
#include <algorithm>
4
#include <cassert>
5
#include <cmath>
6
#include <cstring>
7
#include <fstream>
8
#include <memory>
9
#include <new>
10
#include <string>
11
#include <vector>
12

13
/* -----------------------------------------------------------------------
14
   DWOP decompressor
15
   ----------------------------------------------------------------------- */
16

17
namespace
18
{
19

20
struct VLChannelState
21
{
22
    int32_t deltas[5] = {0, 0, 0, 0, 0};
23
    uint32_t averages[5] = {2560, 2560, 2560, 2560, 2560};
24
};
25

26
class VLDWOPDecompressor
27
{
28
public:
29
    VLChannelState ch[2];
30
    uint32_t currentWord = 0;
31
    int32_t bitsLeft = 0;
32
    const uint32_t* inputPtr = nullptr;
33
    const uint32_t* endPtr = nullptr;
34
    std::vector<uint32_t> buf;
35

36
    void init(const uint8_t* data, size_t size)
75✔
37
    {
38
        const size_t words = size / 4;
75✔
39
        buf.resize(words);
75✔
40

41
        for (size_t i = 0; i < words; ++i)
2,503,530✔
42
        {
43
            const size_t b = i * 4;
2,503,455✔
44
            buf[i] = ((uint32_t)data[b] << 24) | ((uint32_t)data[b + 1] << 16) | ((uint32_t)data[b + 2] << 8) | (uint32_t)data[b + 3];
2,503,455✔
45
        }
46

47
        inputPtr = buf.data();
75✔
48
        endPtr = buf.data() + buf.size();
75✔
49
        currentWord = 0;
75✔
50
        bitsLeft = 0;
75✔
51
    }
75✔
52

53
    bool decompressMono(uint32_t frameCount, int16_t* out)
60✔
54
    {
55
        if (!out || frameCount == 0)
60✔
56
            return false;
×
57

58
        int32_t d0 = ch[0].deltas[0] * 2, d1 = ch[0].deltas[1] * 2, d2 = ch[0].deltas[2] * 2, d3 = ch[0].deltas[3] * 2, d4 = ch[0].deltas[4] * 2;
60✔
59

60
        uint32_t a0 = ch[0].averages[0], a1 = ch[0].averages[1], a2 = ch[0].averages[2], a3 = ch[0].averages[3], a4 = ch[0].averages[4];
60✔
61

62
        uint32_t j = 2;
60✔
63
        int32_t rbits = 0;
60✔
64

65
        uint32_t cw = currentWord;
60✔
66
        int32_t bl = bitsLeft;
60✔
67
        const uint32_t* inp = inputPtr;
60✔
68
        bool eof = false;
60✔
69

70
        for (uint32_t f = 0; f < frameCount && !eof; ++f)
6,971,899✔
71
        {
72
            uint32_t minAvg = a0;
6,971,839✔
73
            int minIdx = 0;
6,971,839✔
74

75
            if (a1 < minAvg)
6,971,839✔
76
            {
77
                minAvg = a1;
6,545,881✔
78
                minIdx = 1;
6,545,881✔
79
            }
80

81
            if (a2 < minAvg)
6,971,839✔
82
            {
83
                minAvg = a2;
2,999,423✔
84
                minIdx = 2;
2,999,423✔
85
            }
86

87
            if (a3 < minAvg)
6,971,839✔
88
            {
89
                minAvg = a3;
1,816,293✔
90
                minIdx = 3;
1,816,293✔
91
            }
92

93
            if (a4 < minAvg)
6,971,839✔
94
            {
95
                minAvg = a4;
1,126,298✔
96
                minIdx = 4;
1,126,298✔
97
            }
98

99
            uint32_t step = ((minAvg * 3u) + 36u) >> 7;
6,971,839✔
100
            uint32_t prefixSum = 0;
6,971,839✔
101
            int zerosWin = 7;
6,971,839✔
102

103
            while (true)
104
            {
105
                bool bit = readBit(cw, bl, inp, eof);
13,002,540✔
106

107
                if (eof)
13,002,540✔
108
                    break;
×
109

110
                if (bit)
13,002,540✔
111
                    break;
6,971,839✔
112

113
                if (step > 0 && prefixSum > 0xFFFFFFFFu - step)
6,030,701✔
114
                {
115
                    eof = true;
×
116
                    break;
×
117
                }
118

119
                prefixSum += step;
6,030,701✔
120
                if (--zerosWin == 0)
6,030,701✔
121
                {
122
                    step <<= 2;
43,373✔
123
                    zerosWin = 7;
43,373✔
124
                }
125
            }
6,030,701✔
126

127
            if (eof)
6,971,839✔
128
                break;
×
129

130
            adjustJRbits(step, j, rbits);
6,971,839✔
131

132
            uint32_t rem = (rbits > 0) ? readBits(rbits, cw, bl, inp, eof) : 0;
6,971,839✔
133
            if (eof)
6,971,839✔
134
                break;
×
135

136
            const int32_t thresh = (int32_t)j - (int32_t)step;
6,971,839✔
137
            if ((int32_t)rem - thresh >= 0)
6,971,839✔
138
            {
139
                const uint32_t extra = readBits(1, cw, bl, inp, eof);
3,409,245✔
140

141
                if (eof)
3,409,245✔
142
                    break;
×
143

144
                rem = rem * 2u - (uint32_t)thresh + extra;
3,409,245✔
145
            }
146

147
            const uint32_t codeVal = rem + prefixSum;
6,971,839✔
148
            const int32_t signed2x = -(int32_t)(codeVal & 1u) ^ (int32_t)codeVal;
6,971,839✔
149
            const int32_t s2x = applyPredictor(minIdx, signed2x, d0, d1, d2, d3, d4);
6,971,839✔
150
            out[f] = clampSample(s2x >> 1);
6,971,839✔
151
            updateAverages(a0, a1, a2, a3, a4, d0, d1, d2, d3, d4);
6,971,839✔
152
        }
153

154
        ch[0].deltas[0] = d0 >> 1;
60✔
155
        ch[0].deltas[1] = d1 >> 1;
60✔
156
        ch[0].deltas[2] = d2 >> 1;
60✔
157
        ch[0].deltas[3] = d3 >> 1;
60✔
158
        ch[0].deltas[4] = d4 >> 1;
60✔
159
        ch[0].averages[0] = a0;
60✔
160
        ch[0].averages[1] = a1;
60✔
161
        ch[0].averages[2] = a2;
60✔
162
        ch[0].averages[3] = a3;
60✔
163
        ch[0].averages[4] = a4;
60✔
164
        currentWord = cw;
60✔
165
        bitsLeft = bl;
60✔
166
        inputPtr = inp;
60✔
167
        return !eof;
60✔
168
    }
169

170
    bool decompressStereo(uint32_t frameCount, int16_t* out)
15✔
171
    {
172
        if (!out || frameCount == 0)
15✔
173
            return false;
×
174

175
        int32_t d[2][5];
176
        uint32_t a[2][5];
177
        for (int c = 0; c < 2; ++c)
45✔
178
        {
179
            for (int i = 0; i < 5; ++i)
180✔
180
            {
181
                d[c][i] = ch[c].deltas[i] * 2;
150✔
182
                a[c][i] = ch[c].averages[i];
150✔
183
            }
184
        }
185

186
        uint32_t j[2] = {2, 2};
15✔
187
        int32_t rbits[2] = {0, 0};
15✔
188

189
        uint32_t cw = currentWord;
15✔
190
        int32_t bl = bitsLeft;
15✔
191
        const uint32_t* inp = inputPtr;
15✔
192
        bool eof = false;
15✔
193

194
        for (uint32_t f = 0; f < frameCount && !eof; ++f)
1,101,460✔
195
        {
196
            int32_t ch2x[2] = {0, 0};
1,101,447✔
197
            for (int c = 0; c < 2 && !eof; ++c)
3,304,337✔
198
            {
199
                uint32_t minAvg = a[c][0];
2,202,892✔
200
                int minIdx = 0;
2,202,892✔
201
                if (a[c][1] < minAvg)
2,202,892✔
202
                {
203
                    minAvg = a[c][1];
1,746,098✔
204
                    minIdx = 1;
1,746,098✔
205
                }
206

207
                if (a[c][2] < minAvg)
2,202,892✔
208
                {
209
                    minAvg = a[c][2];
761,660✔
210
                    minIdx = 2;
761,660✔
211
                }
212

213
                if (a[c][3] < minAvg)
2,202,892✔
214
                {
215
                    minAvg = a[c][3];
134,042✔
216
                    minIdx = 3;
134,042✔
217
                }
218

219
                if (a[c][4] < minAvg)
2,202,892✔
220
                {
221
                    minAvg = a[c][4];
95,402✔
222
                    minIdx = 4;
95,402✔
223
                }
224

225
                uint32_t step = ((minAvg * 3u) + 36u) >> 7;
2,202,892✔
226
                uint32_t prefixSum = 0;
2,202,892✔
227
                int zerosWin = 7;
2,202,892✔
228

229
                while (true)
230
                {
231
                    bool bit = readBit(cw, bl, inp, eof);
3,856,442✔
232

233
                    if (eof)
3,856,442✔
234
                        break;
×
235

236
                    if (bit)
3,856,442✔
237
                        break;
2,202,890✔
238

239
                    if (step > 0 && prefixSum > 0xFFFFFFFFu - step)
1,653,552✔
240
                    {
241
                        eof = true;
2✔
242
                        break;
2✔
243
                    }
244

245
                    prefixSum += step;
1,653,550✔
246
                    if (--zerosWin == 0)
1,653,550✔
247
                    {
248
                        step <<= 2;
1,399✔
249
                        zerosWin = 7;
1,399✔
250
                    }
251
                }
1,653,550✔
252

253
                if (eof)
2,202,892✔
254
                    break;
2✔
255

256
                adjustJRbits(step, j[c], rbits[c]);
2,202,890✔
257

258
                uint32_t rem = (rbits[c] > 0) ? readBits(rbits[c], cw, bl, inp, eof) : 0;
2,202,890✔
259
                if (eof)
2,202,890✔
260
                    break;
×
261

262
                const int32_t thresh = (int32_t)j[c] - (int32_t)step;
2,202,890✔
263
                if ((int32_t)rem - thresh >= 0)
2,202,890✔
264
                {
265
                    const uint32_t extra = readBits(1, cw, bl, inp, eof);
937,495✔
266

267
                    if (eof)
937,495✔
268
                        break;
×
269

270
                    rem = rem * 2u - (uint32_t)thresh + extra;
937,495✔
271
                }
272

273
                const uint32_t codeVal = rem + prefixSum;
2,202,890✔
274
                const int32_t signed2x = -(int32_t)(codeVal & 1u) ^ (int32_t)codeVal;
2,202,890✔
275
                ch2x[c] = applyPredictor(minIdx, signed2x, d[c][0], d[c][1], d[c][2], d[c][3], d[c][4]);
2,202,890✔
276
                updateAverages(a[c][0], a[c][1], a[c][2], a[c][3], a[c][4], d[c][0], d[c][1], d[c][2], d[c][3], d[c][4]);
2,202,890✔
277
            }
278

279
            if (eof)
1,101,447✔
280
                break;
2✔
281

282
            out[f * 2 + 0] = clampSample(ch2x[0] >> 1);
1,101,445✔
283
            out[f * 2 + 1] = clampSample((int32_t)(((int64_t)ch2x[0] + (int64_t)ch2x[1]) >> 1));
1,101,445✔
284
        }
285

286
        for (int c = 0; c < 2; ++c)
45✔
287
        {
288
            for (int i = 0; i < 5; ++i)
180✔
289
            {
290
                ch[c].deltas[i] = d[c][i] >> 1;
150✔
291
                ch[c].averages[i] = a[c][i];
150✔
292
            }
293
        }
294
        currentWord = cw;
15✔
295
        bitsLeft = bl;
15✔
296
        inputPtr = inp;
15✔
297
        return !eof;
15✔
298
    }
299

300
private:
301
    static int16_t clampSample(int32_t v)
9,174,729✔
302
    {
303
        if (v > 32767)
9,174,729✔
304
            return 32767;
186,729✔
305

306
        if (v < -32768)
8,988,000✔
307
            return -32768;
237,248✔
308

309
        return (int16_t)v;
8,750,752✔
310
    }
311

312
    bool readBit(uint32_t& cw, int32_t& bl, const uint32_t*& inp, bool& eof)
16,858,982✔
313
    {
314
        const int32_t blBefore = bl--;
16,858,982✔
315
        if (blBefore - 1 < 0)
16,858,982✔
316
        {
317
            if (inp >= endPtr)
530,473✔
318
            {
319
                eof = true;
×
320
                return false;
×
321
            }
322

323
            cw = *inp++;
530,473✔
324
            bl = 0x1f;
530,473✔
325
        }
326

327
        const bool bit = (int32_t)cw < 0;
16,858,982✔
328
        cw <<= 1;
16,858,982✔
329
        return bit;
16,858,982✔
330
    }
331

332
    uint32_t readBits(int n, uint32_t& cw, int32_t& bl, const uint32_t*& inp, bool& eof)
12,825,161✔
333
    {
334
        if (n <= 0 || n > 31)
12,825,161✔
335
        {
336
            eof = true;
×
337
            return 0;
×
338
        }
339

340
        uint32_t result = cw >> (32 - n);
12,825,161✔
341
        cw <<= n;
12,825,161✔
342

343
        const int32_t blBefore = bl;
12,825,161✔
344
        bl -= n;
12,825,161✔
345

346
        if (blBefore - n < 0)
12,825,161✔
347
        {
348
            if (inp >= endPtr)
1,830,762✔
349
            {
350
                eof = true;
×
351
                return result;
×
352
            }
353

354
            const uint32_t next = *inp++;
1,830,762✔
355
            bl += 32;
1,830,762✔
356
            result |= next >> bl;
1,830,762✔
357
            cw = next << (32 - bl);
1,830,762✔
358
        }
359

360
        return result;
12,825,161✔
361
    }
362

363
    static void adjustJRbits(uint32_t step, uint32_t& j, int32_t& rbits)
9,174,729✔
364
    {
365
        if (step < j)
9,174,729✔
366
        {
367
            for (uint32_t jt = j >> 1; step < jt; jt >>= 1)
9,205,696✔
368
            {
369
                j = jt;
164,388✔
370
                --rbits;
164,388✔
371
            }
372
        }
373
        else
374
        {
375
            while (step >= j)
298,354✔
376
            {
377
                const uint32_t prev = j;
164,933✔
378

379
                j <<= 1;
164,933✔
380
                ++rbits;
164,933✔
381

382
                if (j <= prev)
164,933✔
383
                {
384
                    j = prev;
×
385
                    break;
×
386
                }
387
            }
388
        }
389
    }
9,174,729✔
390

391
    static int32_t applyPredictor(int idx, int32_t s2x, int32_t& d0, int32_t& d1, int32_t& d2, int32_t& d3, int32_t& d4)
9,174,729✔
392
    {
393
        switch (idx)
9,174,729✔
394
        {
395
            case 0:
882,749✔
396
            {
397
                const int32_t t0 = s2x - d0, t1 = t0 - d1, t2 = t1 - d2;
882,749✔
398
                d4 = t2 - d3;
882,749✔
399
                d3 = t2;
882,749✔
400
                d2 = t1;
882,749✔
401
                d1 = t0;
882,749✔
402
                d0 = s2x;
882,749✔
403
                return s2x;
882,749✔
404
            }
405

406
            case 1:
4,530,782✔
407
            {
408
                const int32_t t1 = s2x - d1, t2 = t1 - d2, nd0 = d0 + s2x;
4,530,782✔
409
                d4 = t2 - d3;
4,530,782✔
410
                d3 = t2;
4,530,782✔
411
                d2 = t1;
4,530,782✔
412
                d1 = s2x;
4,530,782✔
413
                d0 = nd0;
4,530,782✔
414
                return nd0;
4,530,782✔
415
            }
416

417
            case 2:
1,810,813✔
418
            {
419
                const int32_t nd1 = d1 + s2x, nd0 = d0 + nd1, t = s2x - d2;
1,810,813✔
420
                d4 = t - d3;
1,810,813✔
421
                d3 = t;
1,810,813✔
422
                d2 = s2x;
1,810,813✔
423
                d1 = nd1;
1,810,813✔
424
                d0 = nd0;
1,810,813✔
425
                return nd0;
1,810,813✔
426
            }
427

428
            case 3:
728,685✔
429
            {
430
                const int32_t nd2 = d2 + s2x, nd1 = d1 + nd2, nd0 = d0 + nd1;
728,685✔
431
                d4 = s2x - d3;
728,685✔
432
                d3 = s2x;
728,685✔
433
                d2 = nd2;
728,685✔
434
                d1 = nd1;
728,685✔
435
                d0 = nd0;
728,685✔
436
                return nd0;
728,685✔
437
            }
438

439
            case 4:
1,221,700✔
440
            {
441
                const int32_t nd3 = d3 + s2x, nd2 = d2 + nd3, nd1 = d1 + nd2, nd0 = d0 + nd1;
1,221,700✔
442
                d4 = s2x;
1,221,700✔
443
                d3 = nd3;
1,221,700✔
444
                d2 = nd2;
1,221,700✔
445
                d1 = nd1;
1,221,700✔
446
                d0 = nd0;
1,221,700✔
447
                return nd0;
1,221,700✔
448
            }
449

450
            default:
×
451
                return d0; // LCOV_EXCL_LINE idx comes from a 0..4 minimum search.
452
        }
453
    }
454

455
    static void updateAverages(uint32_t& a0, uint32_t& a1, uint32_t& a2, uint32_t& a3, uint32_t& a4, int32_t d0, int32_t d1, int32_t d2, int32_t d3, int32_t d4)
9,174,729✔
456
    {
457
        auto mag = [](int32_t v) -> uint32_t
45,873,645✔
458
        {
459
            return (uint32_t)(v ^ (v >> 31));
45,873,645✔
460
        };
461

462
        a0 = a0 + mag(d0) - (a0 >> 5);
9,174,729✔
463
        a1 = a1 + mag(d1) - (a1 >> 5);
9,174,729✔
464
        a2 = a2 + mag(d2) - (a2 >> 5);
9,174,729✔
465
        a3 = a3 + mag(d3) - (a3 >> 5);
9,174,729✔
466
        a4 = a4 + mag(d4) - (a4 >> 5);
9,174,729✔
467
    }
9,174,729✔
468
};
469

470
class VLBitWriter
471
{
472
public:
473
    void writeBit(bool bit)
44,585,933✔
474
    {
475
        if (bit)
44,585,933✔
476
            currentWord |= (uint32_t)1u << (31 - bitCount);
22,460,750✔
477
        if (++bitCount == 32)
44,585,933✔
478
            flushWord();
1,393,286✔
479
    }
44,585,933✔
480

481
    void writeBits(uint32_t value, int count)
5,187,181✔
482
    {
483
        for (int i = count - 1; i >= 0; --i)
36,382,561✔
484
            writeBit(((value >> i) & 1u) != 0);
31,195,380✔
485
    }
5,187,181✔
486

487
    std::vector<uint8_t> finish()
64✔
488
    {
489
        if (bitCount > 0)
64✔
490
            flushWord();
58✔
491

492
        return bytes;
64✔
493
    }
494

495
private:
496
    std::vector<uint8_t> bytes;
497
    uint32_t currentWord = 0;
498
    int bitCount = 0;
499

500
    void flushWord()
1,393,344✔
501
    {
502
        bytes.push_back((uint8_t)(currentWord >> 24));
1,393,344✔
503
        bytes.push_back((uint8_t)(currentWord >> 16));
1,393,344✔
504
        bytes.push_back((uint8_t)(currentWord >> 8));
1,393,344✔
505
        bytes.push_back((uint8_t)currentWord);
1,393,344✔
506
        currentWord = 0;
1,393,344✔
507
        bitCount = 0;
1,393,344✔
508
    }
1,393,344✔
509
};
510

511
class VLDWOPCompressor
512
{
513
public:
514
    VLChannelState ch[2];
515

516
    std::vector<uint8_t> compressMono(const int16_t* in, uint32_t frameCount)
56✔
517
    {
518
        reset();
56✔
519

520
        VLBitWriter bw;
56✔
521
        int32_t d[5] = {};
56✔
522
        uint32_t a[5] = {2560, 2560, 2560, 2560, 2560};
56✔
523
        uint32_t j = 2;
56✔
524
        int32_t rbits = 0;
56✔
525

526
        for (uint32_t f = 0; f < frameCount; ++f)
5,143,091✔
527
            encodeChannel((int32_t)in[f] * 2, d, a, j, rbits, bw);
5,143,035✔
528

529
        return bw.finish();
112✔
530
    }
56✔
531

532
    std::vector<uint8_t> compressStereo(const int16_t* in, uint32_t frameCount)
8✔
533
    {
534
        reset();
8✔
535

536
        VLBitWriter bw;
8✔
537
        int32_t d[2][5] = {};
8✔
538
        uint32_t a[2][5] = {{2560, 2560, 2560, 2560, 2560}, {2560, 2560, 2560, 2560, 2560}};
8✔
539
        uint32_t j[2] = {2, 2};
8✔
540
        int32_t rbits[2] = {0, 0};
8✔
541

542
        for (uint32_t f = 0; f < frameCount; ++f)
563,302✔
543
        {
544
            const int32_t left2x = (int32_t)in[(size_t)f * 2] * 2;
563,294✔
545
            const int32_t right2x = (int32_t)in[(size_t)f * 2 + 1] * 2;
563,294✔
546
            encodeChannel(left2x, d[0], a[0], j[0], rbits[0], bw);
563,294✔
547
            encodeChannel(right2x - left2x, d[1], a[1], j[1], rbits[1], bw);
563,294✔
548
        }
549

550
        return bw.finish();
16✔
551
    }
8✔
552

553
private:
554
    void reset()
64✔
555
    {
556
        for (auto& c : ch)
192✔
557
        {
558
            std::fill(c.deltas, c.deltas + 5, 0);
128✔
559
            std::fill(c.averages, c.averages + 5, 2560);
128✔
560
        }
561
    }
64✔
562

563
    static uint32_t toCodeValue(int32_t signed2x)
6,269,623✔
564
    {
565
        if (signed2x >= 0)
6,269,623✔
566
            return (uint32_t)signed2x;
3,800,304✔
567

568
        return (uint32_t)(-signed2x - 1);
2,469,319✔
569
    }
570

571
    static int minAverageIndex(const uint32_t a[5])
6,269,623✔
572
    {
573
        int idx = 0;
6,269,623✔
574

575
        for (int i = 1; i < 5; ++i)
31,348,115✔
576
        {
577
            if (a[i] < a[idx])
25,078,492✔
578
                idx = i;
9,401,586✔
579
        }
580

581
        return idx;
6,269,623✔
582
    }
583

584
    static int32_t predictorResidual(int idx, int32_t sample2x, const int32_t d[5])
6,269,623✔
585
    {
586
        switch (idx)
6,269,623✔
587
        {
588
            case 0:
1,185,020✔
589
                return sample2x;
1,185,020✔
590

591
            case 1:
2,796,825✔
592
                return sample2x - d[0];
2,796,825✔
593

594
            case 2:
1,005,583✔
595
                return sample2x - d[0] - d[1];
1,005,583✔
596

597
            case 3:
535,092✔
598
                return sample2x - d[0] - d[1] - d[2];
535,092✔
599

600
            case 4:
747,103✔
601
                return sample2x - d[0] - d[1] - d[2] - d[3];
747,103✔
602

603
            default:
×
604
                return sample2x; // LCOV_EXCL_LINE idx comes from a 0..4 minimum search.
605
        }
606
    }
607

608
    static int32_t applyPredictor(int idx, int32_t s2x, int32_t d[5])
6,269,623✔
609
    {
610
        switch (idx)
6,269,623✔
611
        {
612
            case 0:
1,185,020✔
613
            {
614
                const int32_t t0 = s2x - d[0], t1 = t0 - d[1], t2 = t1 - d[2];
1,185,020✔
615
                d[4] = t2 - d[3];
1,185,020✔
616
                d[3] = t2;
1,185,020✔
617
                d[2] = t1;
1,185,020✔
618
                d[1] = t0;
1,185,020✔
619
                d[0] = s2x;
1,185,020✔
620
                return s2x;
1,185,020✔
621
            }
622

623
            case 1:
2,796,825✔
624
            {
625
                const int32_t t1 = s2x - d[1], t2 = t1 - d[2], nd0 = d[0] + s2x;
2,796,825✔
626
                d[4] = t2 - d[3];
2,796,825✔
627
                d[3] = t2;
2,796,825✔
628
                d[2] = t1;
2,796,825✔
629
                d[1] = s2x;
2,796,825✔
630
                d[0] = nd0;
2,796,825✔
631
                return nd0;
2,796,825✔
632
            }
633

634
            case 2:
1,005,583✔
635
            {
636
                const int32_t nd1 = d[1] + s2x, nd0 = d[0] + nd1, t = s2x - d[2];
1,005,583✔
637
                d[4] = t - d[3];
1,005,583✔
638
                d[3] = t;
1,005,583✔
639
                d[2] = s2x;
1,005,583✔
640
                d[1] = nd1;
1,005,583✔
641
                d[0] = nd0;
1,005,583✔
642
                return nd0;
1,005,583✔
643
            }
644

645
            case 3:
535,092✔
646
            {
647
                const int32_t nd2 = d[2] + s2x, nd1 = d[1] + nd2, nd0 = d[0] + nd1;
535,092✔
648
                d[4] = s2x - d[3];
535,092✔
649
                d[3] = s2x;
535,092✔
650
                d[2] = nd2;
535,092✔
651
                d[1] = nd1;
535,092✔
652
                d[0] = nd0;
535,092✔
653
                return nd0;
535,092✔
654
            }
655

656
            case 4:
747,103✔
657
            {
658
                const int32_t nd3 = d[3] + s2x, nd2 = d[2] + nd3, nd1 = d[1] + nd2, nd0 = d[0] + nd1;
747,103✔
659
                d[4] = s2x;
747,103✔
660
                d[3] = nd3;
747,103✔
661
                d[2] = nd2;
747,103✔
662
                d[1] = nd1;
747,103✔
663
                d[0] = nd0;
747,103✔
664
                return nd0;
747,103✔
665
            }
666

667
            default:
×
668
                return d[0]; // LCOV_EXCL_LINE idx comes from a 0..4 minimum search.
669
        }
670
    }
671

672
    static void updateAverages(uint32_t a[5], const int32_t d[5])
6,269,623✔
673
    {
674
        auto mag = [](int32_t v) -> uint32_t
31,348,115✔
675
        {
676
            return (uint32_t)(v ^ (v >> 31));
31,348,115✔
677
        };
678

679
        for (int i = 0; i < 5; ++i)
37,617,738✔
680
        {
681
            a[i] = a[i] + mag(d[i]) - (a[i] >> 5);
31,348,115✔
682
        }
683
    }
6,269,623✔
684

685
    static void adjustJRbits(uint32_t step, uint32_t& j, int32_t& rbits)
10,959,791✔
686
    {
687
        if (step < j)
10,959,791✔
688
        {
689
            for (uint32_t jt = j >> 1; step < jt; jt >>= 1)
11,239,067✔
690
            {
691
                j = jt;
483,715✔
692
                --rbits;
483,715✔
693
            }
694
        }
695
        else
696
        {
697
            while (step >= j)
530,949✔
698
            {
699
                const uint32_t prev = j;
326,510✔
700

701
                j <<= 1;
326,510✔
702
                ++rbits;
326,510✔
703

704
                if (j <= prev)
326,510✔
705
                {
706
                    j = prev;
×
707
                    break;
×
708
                }
709
            }
710
        }
711
    }
10,959,791✔
712

713
    static bool encodeRemainder(uint32_t raw, uint32_t step, uint32_t j, int32_t rbits, uint32_t& remBits, bool& hasExtra, bool& extraBit)
10,959,791✔
714
    {
715
        if (rbits < 0 || rbits > 31)
10,959,791✔
716
            return false;
×
717

718
        const uint32_t limit = rbits == 31 ? 0x80000000u : (1u << rbits);
10,959,791✔
719
        const int32_t threshSigned = (int32_t)j - (int32_t)step;
10,959,791✔
720
        if (threshSigned < 0)
10,959,791✔
721
            return false;
×
722

723
        const uint32_t thresh = (uint32_t)threshSigned;
10,959,791✔
724

725
        if (raw < thresh)
10,959,791✔
726
        {
727
            if (raw >= limit)
3,838,861✔
728
                return false;
×
729

730
            remBits = raw;
3,838,861✔
731
            hasExtra = false;
3,838,861✔
732
            extraBit = false;
3,838,861✔
733
            return true;
3,838,861✔
734
        }
735

736
        const uint32_t folded = raw + thresh;
7,120,930✔
737
        remBits = folded >> 1;
7,120,930✔
738
        hasExtra = true;
7,120,930✔
739
        extraBit = (folded & 1u) != 0;
7,120,930✔
740
        return remBits >= thresh && remBits < limit;
7,120,930✔
741
    }
742

743
    static void writeCodeValue(uint32_t codeVal, uint32_t baseStep, uint32_t& j, int32_t& rbits, VLBitWriter& bw)
6,269,623✔
744
    {
745
        uint32_t prefixSum = 0;
6,269,623✔
746
        uint32_t step = baseStep;
6,269,623✔
747
        int zerosWin = 7;
6,269,623✔
748

749
        for (uint32_t zeros = 0; zeros < 0x100000; ++zeros)
10,959,791✔
750
        {
751
            if (codeVal >= prefixSum)
10,959,791✔
752
            {
753
                uint32_t trialJ = j;
10,959,791✔
754
                int32_t trialRbits = rbits;
10,959,791✔
755
                adjustJRbits(step, trialJ, trialRbits);
10,959,791✔
756

757
                uint32_t remBits = 0;
10,959,791✔
758
                bool hasExtra = false;
10,959,791✔
759
                bool extraBit = false;
10,959,791✔
760
                if (encodeRemainder(codeVal - prefixSum, step, trialJ, trialRbits, remBits, hasExtra, extraBit))
10,959,791✔
761
                {
762
                    for (uint32_t i = 0; i < zeros; ++i)
10,959,791✔
763
                        bw.writeBit(false);
4,690,168✔
764

765
                    bw.writeBit(true);
6,269,623✔
766

767
                    if (trialRbits > 0)
6,269,623✔
768
                        bw.writeBits(remBits, trialRbits);
5,187,181✔
769

770
                    if (hasExtra)
6,269,623✔
771
                        bw.writeBit(extraBit);
2,430,762✔
772

773
                    j = trialJ;
6,269,623✔
774
                    rbits = trialRbits;
6,269,623✔
775
                    return;
6,269,623✔
776
                }
777
            }
778

779
            if (baseStep == 0)
4,690,168✔
780
                break;
×
781

782
            if (UINT32_MAX - prefixSum < step)
4,690,168✔
783
                break;
×
784

785
            prefixSum += step;
4,690,168✔
786
            if (prefixSum > codeVal && step != 0)
4,690,168✔
787
                break;
×
788

789
            if (--zerosWin == 0)
4,690,168✔
790
            {
791
                step <<= 2;
43,193✔
792
                zerosWin = 7;
43,193✔
793
            }
794
        }
795

796
        bw.writeBit(true); // LCOV_EXCL_LINE emergency fallback; valid sample residuals encode above.
797
    }
798

799
    static void encodeChannel(int32_t sample2x, int32_t d[5], uint32_t a[5], uint32_t& j, int32_t& rbits, VLBitWriter& bw)
6,269,623✔
800
    {
801
        const int idx = minAverageIndex(a);
6,269,623✔
802
        const uint32_t baseStep = ((a[idx] * 3u) + 36u) >> 7;
6,269,623✔
803
        const int32_t residual = predictorResidual(idx, sample2x, d);
6,269,623✔
804
        writeCodeValue(toCodeValue(residual), baseStep, j, rbits, bw);
6,269,623✔
805
        applyPredictor(idx, residual, d);
6,269,623✔
806
        updateAverages(a, d);
6,269,623✔
807
    }
6,269,623✔
808
};
809

810
class VLIFFWriter
811
{
812
public:
813
    std::vector<uint8_t> data;
814

815
    size_t beginChunk(const char id[4])
2,663✔
816
    {
817
        const size_t start = data.size();
2,663✔
818
        data.insert(data.end(), id, id + 4);
2,663✔
819
        put32(0);
2,663✔
820
        return start;
2,663✔
821
    }
822

823
    size_t beginCat(const char type[4])
192✔
824
    {
825
        const size_t start = beginChunk("CAT ");
192✔
826
        data.insert(data.end(), type, type + 4);
192✔
827
        return start;
192✔
828
    }
829

830
    void endChunk(size_t start)
2,663✔
831
    {
832
        const size_t payloadStart = start + 8;
2,663✔
833
        const uint32_t size = (uint32_t)(data.size() - payloadStart);
2,663✔
834

835
        data[start + 4] = (uint8_t)(size >> 24);
2,663✔
836
        data[start + 5] = (uint8_t)(size >> 16);
2,663✔
837
        data[start + 6] = (uint8_t)(size >> 8);
2,663✔
838
        data[start + 7] = (uint8_t)size;
2,663✔
839

840
        if (data.size() & 1u)
2,663✔
841
            data.push_back(0);
2,271✔
842
    }
2,663✔
843

844
    void put8(uint8_t v)
3,359✔
845
    {
846
        data.push_back(v);
3,359✔
847
    }
3,359✔
848

849
    void put16(uint16_t v)
2,335✔
850
    {
851
        data.push_back((uint8_t)(v >> 8));
2,335✔
852
        data.push_back((uint8_t)v);
2,335✔
853
    }
2,335✔
854

855
    void put32(uint32_t v)
7,053✔
856
    {
857
        data.push_back((uint8_t)(v >> 24));
7,053✔
858
        data.push_back((uint8_t)(v >> 16));
7,053✔
859
        data.push_back((uint8_t)(v >> 8));
7,053✔
860
        data.push_back((uint8_t)v);
7,053✔
861
    }
7,053✔
862

863
    void putBytes(const uint8_t* p, size_t n)
290✔
864
    {
865
        data.insert(data.end(), p, p + n);
290✔
866
    }
290✔
867
};
868

869
/* -----------------------------------------------------------------------
870
   REX2 file parser + DWOP decompressor wrapper
871
   ----------------------------------------------------------------------- */
872

873
enum VLSliceState
874
{
875
    kSliceNormal = 1,
876
    kSliceMuted = 2,
877
    kSliceLocked = 3
878
};
879

880
struct VLSliceEntry
881
{
882
    uint32_t ppq_pos = 0;
883
    uint32_t sample_length = 0;
884
    uint32_t rendered_length = 0;
885
    uint32_t sample_start = 0;
886
    uint16_t points = 0x7fff;
887
    uint8_t selected_flag = 0;
888
    VLSliceState state = kSliceNormal;
889
    bool synthetic_leading = false;
890
};
891

892
template <typename T> class VLHeapArray
893
{
894
public:
895
    VLHeapArray() = default;
128✔
896

897
    VLHeapArray(const VLHeapArray&) = delete;
898
    VLHeapArray& operator=(const VLHeapArray&) = delete;
899

900
    VLHeapArray(VLHeapArray&& o) noexcept : ptr_(std::move(o.ptr_)), size_(o.size_)
901
    {
902
        o.size_ = 0;
903
    }
904

905
    VLHeapArray& operator=(VLHeapArray&& o) noexcept
906
    {
907
        ptr_ = std::move(o.ptr_);
908
        size_ = o.size_;
909
        o.size_ = 0;
910
        return *this;
911
    }
912

913
    [[nodiscard]] bool assign(std::size_t n, T val) noexcept
75✔
914
    {
915
        if (n == 0)
75✔
916
        {
917
            ptr_.reset();
×
918
            size_ = 0;
×
919
            return true;
×
920
        }
921

922
        T* raw = new (std::nothrow) T[n];
75✔
923
        if (!raw)
75✔
924
            return false;
×
925

926
        std::fill_n(raw, n, val);
75✔
927
        ptr_.reset(raw);
75✔
928
        size_ = n;
75✔
929

930
        return true;
75✔
931
    }
932

933
    [[nodiscard]] bool resize(std::size_t n, T val) noexcept
37✔
934
    {
935
        if (n == size_)
37✔
936
            return true;
×
937

938
        if (n == 0)
37✔
939
        {
940
            ptr_.reset();
×
941
            size_ = 0;
×
942
            return true;
×
943
        }
944

945
        T* raw = new (std::nothrow) T[n];
37✔
946
        if (!raw)
37✔
947
            return false;
×
948

949
        const std::size_t keep = std::min(size_, n);
37✔
950
        if (keep)
37✔
951
            std::memcpy(raw, ptr_.get(), keep * sizeof(T));
14✔
952

953
        if (n > size_)
37✔
954
            std::fill_n(raw + size_, n - size_, val);
37✔
955

956
        ptr_.reset(raw);
37✔
957
        size_ = n;
37✔
958
        return true;
37✔
959
    }
960

961
    T& operator[](std::size_t i) noexcept
2,253,151✔
962
    {
963
        assert(i < size_);
2,253,151✔
964
        return ptr_[i];
2,253,151✔
965
    }
966

967
    const T& operator[](std::size_t i) const noexcept
7,197,570✔
968
    {
969
        assert(i < size_);
7,197,570✔
970
        return ptr_[i];
7,197,570✔
971
    }
972

973
    T* data() noexcept
64✔
974
    {
975
        return ptr_.get();
64✔
976
    }
977

978
    const T* data() const noexcept
979
    {
980
        return ptr_.get();
981
    }
982

983
    std::size_t size() const noexcept
7,198,938✔
984
    {
985
        return size_;
7,198,938✔
986
    }
987

988
    bool empty() const noexcept
1,380✔
989
    {
990
        return size_ == 0;
1,380✔
991
    }
992

993
    static constexpr std::size_t max_size() noexcept
652✔
994
    {
995
        return std::numeric_limits<std::size_t>::max() / sizeof(T);
652✔
996
    }
997

998
private:
999
    std::unique_ptr<T[]> ptr_;
1000
    std::size_t size_ = 0;
1001
};
1002

1003
class VLFileImpl
1004
{
1005
public:
1006
    VLFileInfo info = {};
1007
    VLCreatorInfo creator = {};
1008
    std::vector<VLSliceEntry> slices;
1009
    std::vector<uint8_t> fileData;
1010
    VLHeapArray<int16_t> pcm;
1011
    uint32_t totalFrames = 0;
1012
    uint32_t loopStart = 0;
1013
    uint32_t loopEnd = 0;
1014
    bool transientEnabled = true;
1015
    uint16_t transientAttack = 0x15;
1016
    uint16_t transientDecay = 0x3ff;
1017
    uint16_t transientStretch = 0x28;
1018
    uint16_t processingGain = 1000;
1019
    bool silenceSelected = false;
1020
    bool headerValid = true;
1021

1022
    static constexpr int32_t kREXPPQ = 15360;
1023

1024
    bool loadFromBuffer(const char* buf, size_t size)
104✔
1025
    {
1026
        fileData.assign((const uint8_t*)buf, (const uint8_t*)buf + size);
104✔
1027

1028
        info.channels = 1;
104✔
1029
        info.sample_rate = 44100;
104✔
1030
        info.slice_count = 0;
104✔
1031
        info.tempo = 120000;
104✔
1032
        info.original_tempo = 120000;
104✔
1033
        info.ppq_length = 61440;
104✔
1034
        info.time_sig_num = 4;
104✔
1035
        info.time_sig_den = 4;
104✔
1036
        info.bit_depth = 16;
104✔
1037
        info.total_frames = 0;
104✔
1038
        info.loop_start = 0;
104✔
1039
        info.loop_end = 0;
104✔
1040
        info.processing_gain = processingGain;
104✔
1041
        info.transient_enabled = transientEnabled ? 1 : 0;
104✔
1042
        info.transient_attack = transientAttack;
104✔
1043
        info.transient_decay = transientDecay;
104✔
1044
        info.transient_stretch = transientStretch;
104✔
1045
        info.silence_selected = silenceSelected ? 1 : 0;
104✔
1046
        headerValid = true;
104✔
1047

1048
        std::memset(&creator, 0, sizeof(creator));
104✔
1049

1050
        if (fileData.size() < 12)
104✔
1051
            return false;
1✔
1052

1053
        if (fileData[0] != 'C' || fileData[1] != 'A' || fileData[2] != 'T' || fileData[3] != ' ')
103✔
1054
            return false;
24✔
1055

1056
        size_t dwopOffset = 0, dwopSize = 0;
79✔
1057
        bool hasDWOP = false;
79✔
1058

1059
        parseIFF(8 + 4, fileData.size(), dwopOffset, dwopSize, hasDWOP);
79✔
1060
        finalizeSlices();
79✔
1061

1062
        if (!headerValid)
79✔
1063
            return false;
2✔
1064

1065
        if (!hasDWOP || dwopSize == 0)
77✔
1066
            return false;
2✔
1067

1068
        if (dwopOffset + dwopSize > fileData.size())
75✔
1069
            return false;
×
1070

1071
        if (totalFrames == 0)
75✔
1072
            return false;
×
1073

1074
        // 3600 * 192000 = 691,200,000 — exactly 1 hour at 192 kHz max sample rate.
1075
        static constexpr uint32_t kMaxTotalFrames = 3600u * 192000u;
1076
        if (totalFrames > kMaxTotalFrames)
75✔
1077
            return false;
×
1078

1079
        const size_t pcmElements = (size_t)totalFrames * (size_t)info.channels;
75✔
1080

1081
        if (!pcm.assign(pcmElements, 0))
75✔
1082
            return false;
×
1083

1084
        VLDWOPDecompressor dec;
75✔
1085
        dec.init(&fileData[dwopOffset], dwopSize);
75✔
1086

1087
        uint32_t done = 0;
75✔
1088
        bool ok = true;
75✔
1089
        while (done < totalFrames && ok)
150✔
1090
        {
1091
            const uint32_t chunk = std::min<uint32_t>(0x100000, totalFrames - done);
75✔
1092

1093
            if (info.channels == 1)
75✔
1094
                ok = dec.decompressMono(chunk, &pcm[done]);
60✔
1095
            else
1096
                ok = dec.decompressStereo(chunk, &pcm[(size_t)done * 2]);
15✔
1097

1098
            done += chunk;
75✔
1099
        }
1100

1101
        if (!ok)
75✔
1102
            return false;
2✔
1103

1104
        finalizeRenderedLengths();
73✔
1105
        return true;
73✔
1106
    }
75✔
1107

1108
private:
1109
    static uint32_t be32(const uint8_t* p)
11,469✔
1110
    {
1111
        return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | p[3];
11,469✔
1112
    }
1113

1114
    static uint16_t be16(const uint8_t* p)
3,648✔
1115
    {
1116
        return (uint16_t)(((uint16_t)p[0] << 8) | p[1]);
3,648✔
1117
    }
1118

1119
    void parseIFF(size_t start, size_t end, size_t& dwopOffset, size_t& dwopSize, bool& hasDWOP)
233✔
1120
    {
1121
        size_t off = start;
233✔
1122
        while (off + 8 < end && off + 8 < fileData.size())
4,364✔
1123
        {
1124
            char id[5] = {};
4,135✔
1125
            std::memcpy(id, &fileData[off], 4);
4,135✔
1126
            off += 4;
4,135✔
1127

1128
            const uint32_t sz = be32(&fileData[off]);
4,135✔
1129
            off += 4;
4,135✔
1130

1131
            if (off + sz > fileData.size())
4,135✔
1132
                break;
4✔
1133

1134
            if (std::strcmp(id, "HEAD") == 0)
4,131✔
1135
                parseHEAD(&fileData[off], sz);
79✔
1136

1137
            else if (std::strcmp(id, "CREI") == 0)
4,052✔
1138
                parseCREI(&fileData[off], sz);
8✔
1139

1140
            else if (std::strcmp(id, "SINF") == 0)
4,044✔
1141
                parseSINF(&fileData[off], sz);
77✔
1142

1143
            else if (std::strcmp(id, "GLOB") == 0)
3,967✔
1144
                parseGLOB(&fileData[off], sz);
79✔
1145

1146
            else if (std::strcmp(id, "TRSH") == 0)
3,888✔
1147
                parseTRSH(&fileData[off], sz);
77✔
1148

1149
            else if (std::strcmp(id, "RECY") == 0)
3,811✔
1150
                parseRECY(&fileData[off], sz);
77✔
1151

1152
            else if (std::strcmp(id, "SLCE") == 0)
3,734✔
1153
                parseSLCE(&fileData[off], sz);
3,340✔
1154

1155
            else if ((std::strcmp(id, "SDAT") == 0 || std::strcmp(id, "DWOP") == 0) && !hasDWOP)
394✔
1156
            {
1157
                dwopOffset = off;
77✔
1158
                dwopSize = sz;
77✔
1159
                hasDWOP = true;
77✔
1160
            }
1161

1162
            else if (std::strcmp(id, "CAT ") == 0 && sz >= 4)
317✔
1163
            {
1164
                parseIFF(off + 4, off + sz, dwopOffset, dwopSize, hasDWOP);
154✔
1165
            }
1166

1167
            off += sz;
4,131✔
1168
            if (off & 1)
4,131✔
1169
                ++off;
3,701✔
1170
        }
1171
    }
233✔
1172

1173
    void parseSINF(const uint8_t* d, uint32_t sz)
77✔
1174
    {
1175
        if (sz < 18)
77✔
1176
            return;
×
1177

1178
        info.channels = d[0];
77✔
1179
        const uint8_t bd = d[1];
77✔
1180
        info.sample_rate = (int32_t)be32(d + 2);
77✔
1181
        totalFrames = be32(d + 6);
77✔
1182
        loopStart = be32(d + 10);
77✔
1183
        loopEnd = be32(d + 14);
77✔
1184
        info.total_frames = (int32_t)totalFrames;
77✔
1185
        info.loop_start = (int32_t)loopStart;
77✔
1186
        info.loop_end = (int32_t)loopEnd;
77✔
1187

1188
        switch (bd)
77✔
1189
        {
1190
            case 1:
1✔
1191
                info.bit_depth = 8;
1✔
1192
                break;
1✔
1193

1194
            case 3:
69✔
1195
                info.bit_depth = 16;
69✔
1196
                break;
69✔
1197

1198
            case 5:
5✔
1199
                info.bit_depth = 24;
5✔
1200
                break;
5✔
1201

1202
            case 7:
1✔
1203
                info.bit_depth = 32;
1✔
1204
                break;
1✔
1205

1206
            default:
1✔
1207
                info.bit_depth = 16;
1✔
1208
                break;
1✔
1209
        }
1210

1211
        const uint32_t frames = (loopEnd > loopStart) ? (loopEnd - loopStart) : totalFrames;
77✔
1212
        if (frames > 0 && info.sample_rate > 0 && info.ppq_length > 0)
77✔
1213
        {
1214
            const double beats = (double)info.ppq_length / (double)kREXPPQ;
77✔
1215
            const double bpm = beats * 60.0 * (double)info.sample_rate / (double)frames;
77✔
1216

1217
            const int32_t t = (int32_t)std::lround(bpm * 1000.0);
77✔
1218
            if (t > 0)
77✔
1219
                info.original_tempo = t;
77✔
1220
        }
1221

1222
        if (info.original_tempo == 0)
77✔
1223
            info.original_tempo = info.tempo;
×
1224

1225
        if (info.channels != 1 && info.channels != 2)
77✔
1226
            info.channels = 1;
×
1227
    }
1228

1229
    void parseHEAD(const uint8_t* d, uint32_t sz)
79✔
1230
    {
1231
        if (sz < 6 || be32(d) != 0x490cf18du)
79✔
1232
        {
1233
            headerValid = false;
×
1234
            return;
×
1235
        }
1236

1237
        if (d[4] != 0xbc || d[5] > 0x03)
79✔
1238
        {
1239
            headerValid = false;
2✔
1240
        }
1241
    }
1242

1243
    void parseGLOB(const uint8_t* d, uint32_t sz)
79✔
1244
    {
1245
        if (sz < 22)
79✔
1246
            return;
2✔
1247

1248
        info.slice_count = (int32_t)be32(d);
77✔
1249
        info.time_sig_num = d[7];
77✔
1250
        info.time_sig_den = d[8];
77✔
1251
        info.tempo = (int32_t)be32(d + 16);
77✔
1252
        info.ppq_length = 61440;
77✔
1253
        processingGain = be16(d + 12);
77✔
1254
        silenceSelected = d[21] != 0;
77✔
1255
        info.processing_gain = processingGain;
77✔
1256
        info.silence_selected = silenceSelected ? 1 : 0;
77✔
1257
    }
1258

1259
    void parseCREI(const uint8_t* d, uint32_t sz)
8✔
1260
    {
1261
        uint32_t off = 0;
8✔
1262
        auto readString = [&](char* dst, size_t dstSize)
40✔
1263
        {
1264
            if (!dst || dstSize == 0)
40✔
1265
                return;
×
1266

1267
            dst[0] = '\0';
40✔
1268
            if (off + 4 > sz)
40✔
1269
                return;
4✔
1270

1271
            const uint32_t n = be32(d + off);
36✔
1272
            off += 4;
36✔
1273
            if (off + n > sz)
36✔
1274
            {
1275
                off = sz;
1✔
1276
                return;
1✔
1277
            }
1278

1279
            const size_t copy = std::min<size_t>(n, dstSize - 1);
35✔
1280
            if (copy)
35✔
1281
                std::memcpy(dst, d + off, copy);
29✔
1282

1283
            dst[copy] = '\0';
35✔
1284
            off += n;
35✔
1285
        };
8✔
1286

1287
        readString(creator.name, sizeof(creator.name));
8✔
1288
        readString(creator.copyright, sizeof(creator.copyright));
8✔
1289
        readString(creator.url, sizeof(creator.url));
8✔
1290
        readString(creator.email, sizeof(creator.email));
8✔
1291
        readString(creator.free_text, sizeof(creator.free_text));
8✔
1292
    }
8✔
1293

1294
    void parseTRSH(const uint8_t* d, uint32_t sz)
77✔
1295
    {
1296
        if (sz < 7)
77✔
1297
            return;
×
1298

1299
        transientEnabled = d[0] != 0;
77✔
1300
        transientAttack = be16(d + 1);
77✔
1301
        transientDecay = be16(d + 3);
77✔
1302
        transientStretch = be16(d + 5);
77✔
1303
        info.transient_enabled = transientEnabled ? 1 : 0;
77✔
1304
        info.transient_attack = transientAttack;
77✔
1305
        info.transient_decay = transientDecay;
77✔
1306
        info.transient_stretch = transientStretch;
77✔
1307
    }
1308

1309
    void parseRECY(const uint8_t* d, uint32_t sz)
77✔
1310
    {
1311
        if (sz < 12)
77✔
1312
            return;
×
1313

1314
        const int32_t t = (int32_t)be32(d + 8);
77✔
1315
        if (t > 0)
77✔
1316
            info.original_tempo = t;
74✔
1317
    }
1318

1319
    void parseSLCE(const uint8_t* d, uint32_t sz)
3,340✔
1320
    {
1321
        static constexpr size_t kMaxSlices = 1024;
1322

1323
        if (slices.size() >= kMaxSlices)
3,340✔
1324
            return;
×
1325

1326
        if (sz < 8)
3,340✔
1327
            return;
×
1328

1329
        VLSliceEntry s;
3,340✔
1330
        s.sample_start = be32(d);
3,340✔
1331
        s.sample_length = be32(d + 4);
3,340✔
1332
        s.points = be16(d + 8);
3,340✔
1333

1334
        const uint8_t flags = sz > 10 ? d[10] : 0;
3,340✔
1335
        s.selected_flag = (flags & 0x04) ? 1 : 0;
3,340✔
1336

1337
        if (flags & 0x02)
3,340✔
1338
            s.state = kSliceLocked;
5✔
1339
        else if (flags & 0x01)
3,335✔
1340
            s.state = kSliceMuted;
×
1341
        else
1342
            s.state = kSliceNormal;
3,335✔
1343

1344
        slices.push_back(s);
3,340✔
1345
        info.slice_count = (int32_t)slices.size();
3,340✔
1346
    }
1347

1348
    void finalizeSlices()
79✔
1349
    {
1350
        const uint32_t denom = (loopEnd > loopStart) ? (loopEnd - loopStart) : (totalFrames ? totalFrames : 1u);
79✔
1351

1352
        std::sort(slices.begin(), slices.end(),
79✔
1353
                  [](const VLSliceEntry& a, const VLSliceEntry& b)
22,209✔
1354
                  {
1355
                      return a.sample_start < b.sample_start;
22,209✔
1356
                  });
1357

1358
        std::vector<VLSliceEntry> out;
79✔
1359
        for (auto s : slices)
3,419✔
1360
        {
1361
            if (s.sample_length <= 1)
3,340✔
1362
                continue;
747✔
1363

1364
            const uint32_t rel = (s.sample_start > loopStart) ? (s.sample_start - loopStart) : 0;
2,593✔
1365

1366
            s.ppq_pos = (uint32_t)(((uint64_t)rel * info.ppq_length + denom / 2) / denom);
2,593✔
1367

1368
            out.push_back(s);
2,593✔
1369
        }
1370

1371
        if (!out.empty() && loopStart > 0 && loopEnd > loopStart && out.front().sample_start > loopStart && out.front().sample_start <= totalFrames)
79✔
1372
        {
1373
            VLSliceEntry leading;
7✔
1374
            leading.ppq_pos = 0;
7✔
1375
            leading.sample_start = loopStart;
7✔
1376
            leading.sample_length = out.front().sample_start - loopStart;
7✔
1377
            leading.points = 0x7fff;
7✔
1378
            leading.selected_flag = 1;
7✔
1379
            leading.state = kSliceNormal;
7✔
1380
            leading.synthetic_leading = true;
7✔
1381

1382
            out.insert(out.begin(), leading);
7✔
1383
        }
1384

1385
        slices.swap(out);
79✔
1386
        info.slice_count = (int32_t)slices.size();
79✔
1387
    }
79✔
1388

1389
public:
1390
    struct SegLoop
1391
    {
1392
        uint32_t start = 0;
1393
        uint32_t end = 0;
1394
        float volumeCompensation = 1.0f;
1395
    };
1396

1397
    SegLoop findSegmentLoop(uint32_t start, uint32_t end) const
5,833✔
1398
    {
1399
        SegLoop r{start, end};
5,833✔
1400

1401
        const uint32_t sr = info.sample_rate ? (uint32_t)info.sample_rate : 44100u;
5,833✔
1402
        const uint32_t srch = std::max(1u, (400u * sr) / 44100u);
5,833✔
1403
        const uint32_t mhl = std::max(1u, (20000u * sr) / 44100u);
5,833✔
1404

1405
        if (end <= start || end - start < srch * 3u)
5,833✔
1406
            return r;
2,141✔
1407

1408
        const uint32_t ch = std::max(1, info.channels);
3,692✔
1409
        auto leftAbs = [&](uint32_t f) -> int
2,678,775✔
1410
        {
1411
            const size_t i = (size_t)f * ch;
2,678,775✔
1412
            return i < pcm.size() ? std::abs((int)pcm[i]) : 0;
2,678,775✔
1413
        };
3,692✔
1414

1415
        uint32_t loopEnd = end - srch;
3,692✔
1416
        int peak = -1;
3,692✔
1417
        for (uint32_t i = 0, f = end - srch; i < srch && f > start; ++i, --f)
1,477,792✔
1418
        {
1419
            const int p = leftAbs(f);
1,474,100✔
1420
            if (p > peak)
1,474,100✔
1421
            {
1422
                peak = p;
83,153✔
1423
                loopEnd = f;
83,153✔
1424
            }
1425
        }
1426

1427
        uint32_t hl = std::min((loopEnd - start) / 2u, mhl);
3,692✔
1428
        uint32_t ls = loopEnd - hl;
3,692✔
1429
        uint32_t loopStart = ls;
3,692✔
1430
        int lspeak = -1;
3,692✔
1431
        for (uint32_t i = 0, f = ls; i < srch && f >= start; ++i)
1,208,367✔
1432
        {
1433
            const int p = leftAbs(f);
1,204,675✔
1434
            if (p > lspeak)
1,204,675✔
1435
            {
1436
                lspeak = p;
52,327✔
1437
                loopStart = f;
52,327✔
1438
            }
1439

1440
            if (f == 0)
1,204,675✔
1441
                break;
×
1442

1443
            --f;
1,204,675✔
1444
        }
1445

1446
        r.start = std::clamp(loopStart, start, end - 1u);
3,692✔
1447
        r.end = std::clamp(loopEnd, r.start + 1u, end);
3,692✔
1448

1449
        if (lspeak > 0 && peak > 0)
3,692✔
1450
            r.volumeCompensation = std::min(10.0f, (float)peak / (float)lspeak);
3,576✔
1451

1452
        return r;
3,692✔
1453
    }
1454

1455
private:
1456
    uint32_t calcRenderedLength(const VLSliceEntry& s) const
4,547✔
1457
    {
1458
        const uint32_t start = s.sample_start;
4,547✔
1459
        uint32_t end = std::min(start + s.sample_length, totalFrames);
4,547✔
1460

1461
        if (loopEnd > loopStart && start < loopEnd)
4,547✔
1462
            end = std::min(end, loopEnd);
4,383✔
1463

1464
        if (end <= start)
4,547✔
1465
            return 1u;
1✔
1466

1467
        const uint32_t segLen = end - start;
4,546✔
1468
        const uint32_t loopE = findSegmentLoop(start, end).end;
4,546✔
1469
        const uint32_t stretchN = transientEnabled ? (uint32_t)transientStretch + 1u : 1u;
4,546✔
1470
        const uint32_t stretchT = (uint32_t)(((uint64_t)(loopE - start) * stretchN) / 100u);
4,546✔
1471

1472
        return std::max(1u, segLen + stretchT);
4,546✔
1473
    }
1474

1475
public:
1476
    void finalizeRenderedLengths()
137✔
1477
    {
1478
        for (auto& s : slices)
4,684✔
1479
            s.rendered_length = calcRenderedLength(s);
4,547✔
1480
    }
137✔
1481
};
1482

1483
static int16_t floatToS16(float s)
2,253,076✔
1484
{
1485
    if (s > 1.0f)
2,253,076✔
1486
        s = 1.0f;
64✔
1487

1488
    if (s < -1.0f)
2,253,076✔
1489
        s = -1.0f;
64✔
1490

1491
    const float scaled = s >= 0.0f ? s * 32767.0f : s * 32768.0f;
2,253,076✔
1492
    return (int16_t)std::lround(scaled);
2,253,076✔
1493
}
1494

1495
static uint8_t bitDepthCode(int32_t bitDepth)
64✔
1496
{
1497
    switch (bitDepth)
64✔
1498
    {
1499
        case 8:
×
1500
            return 1; // LCOV_EXCL_LINE saves normalize authored audio to 16-bit PCM.
1501

1502
        case 16:
64✔
1503
            return 3;
64✔
1504

1505
        case 24:
×
1506
            return 5; // LCOV_EXCL_LINE saves normalize authored audio to 16-bit PCM.
1507

1508
        case 32:
×
1509
            return 7; // LCOV_EXCL_LINE saves normalize authored audio to 16-bit PCM.
1510

1511
        default:
×
1512
            return 3; // LCOV_EXCL_LINE saves normalize authored audio to 16-bit PCM.
1513
    }
1514
}
1515

1516
static bool hasCreatorInfo(const VLCreatorInfo& c)
64✔
1517
{
1518
    return c.name[0] || c.copyright[0] || c.url[0] || c.email[0] || c.free_text[0];
64✔
1519
}
1520

1521
static void writeCStringChunkString(VLIFFWriter& w, const char* s)
40✔
1522
{
1523
    const size_t n = s ? std::min<size_t>(std::strlen(s), 255) : 0;
40✔
1524

1525
    w.put32((uint32_t)n);
40✔
1526

1527
    if (n)
40✔
1528
        w.putBytes((const uint8_t*)s, n);
34✔
1529
}
40✔
1530

1531
static void writeSimpleChunk(VLIFFWriter& w, const char id[4], const std::vector<uint8_t>& payload)
64✔
1532
{
1533
    const size_t c = w.beginChunk(id);
64✔
1534

1535
    if (!payload.empty())
64✔
1536
        w.putBytes(payload.data(), payload.size());
64✔
1537

1538
    w.endChunk(c);
64✔
1539
}
64✔
1540

1541
static uint32_t calcSampleStartFromPPQ(const VLFileImpl& impl, uint32_t ppq)
655✔
1542
{
1543
    const VLFileInfo& info = impl.info;
655✔
1544
    if (info.ppq_length > 0 && info.loop_end > info.loop_start)
655✔
1545
    {
1546
        const uint32_t denom = (uint32_t)(info.loop_end - info.loop_start);
643✔
1547
        return (uint32_t)info.loop_start + (uint32_t)(((uint64_t)ppq * denom + (uint32_t)info.ppq_length / 2u) / (uint32_t)info.ppq_length);
643✔
1548
    }
1549

1550
    const uint32_t tempo = info.tempo > 0 ? (uint32_t)info.tempo : 120000u;
12✔
1551
    const uint32_t sr = info.sample_rate > 0 ? (uint32_t)info.sample_rate : 44100u;
12✔
1552
    return (uint32_t)(((uint64_t)ppq * sr * 60000u + (uint64_t)tempo * VLFileImpl::kREXPPQ / 2u) / ((uint64_t)tempo * VLFileImpl::kREXPPQ));
12✔
1553
}
1554

1555
static void normaliseInfoForSave(VLFileImpl& impl)
64✔
1556
{
1557
    impl.info.channels = std::clamp(impl.info.channels, 1, 2);
64✔
1558

1559
    if (impl.info.sample_rate <= 0)
64✔
1560
        impl.info.sample_rate = 44100;
×
1561

1562
    if (impl.info.tempo <= 0)
64✔
1563
        impl.info.tempo = 120000;
×
1564

1565
    if (impl.info.original_tempo <= 0)
64✔
1566
        impl.info.original_tempo = impl.info.tempo;
2✔
1567

1568
    if (impl.info.time_sig_num <= 0)
64✔
1569
        impl.info.time_sig_num = 4;
2✔
1570

1571
    if (impl.info.time_sig_den <= 0)
64✔
1572
        impl.info.time_sig_den = 4;
2✔
1573

1574
    impl.info.bit_depth = 16;
64✔
1575
    impl.info.slice_count = (int32_t)impl.slices.size();
64✔
1576

1577
    impl.totalFrames = (uint32_t)(impl.pcm.size() / (size_t)impl.info.channels);
64✔
1578
    impl.info.total_frames = (int32_t)impl.totalFrames;
64✔
1579

1580
    if (impl.info.loop_start < 0 || (uint32_t)impl.info.loop_start >= impl.totalFrames)
64✔
1581
        impl.info.loop_start = 0;
1✔
1582

1583
    if (impl.info.loop_end <= impl.info.loop_start || (uint32_t)impl.info.loop_end > impl.totalFrames)
64✔
1584
        impl.info.loop_end = (int32_t)impl.totalFrames;
1✔
1585

1586
    impl.loopStart = (uint32_t)std::max(0, impl.info.loop_start);
64✔
1587
    impl.loopEnd = (uint32_t)std::max(impl.info.loop_start, impl.info.loop_end);
64✔
1588

1589
    if (impl.info.ppq_length <= 0)
64✔
1590
    {
1591
        const uint32_t frames = impl.loopEnd > impl.loopStart ? impl.loopEnd - impl.loopStart : impl.totalFrames;
2✔
1592

1593
        const double beats = frames > 0 ? ((double)frames * (double)impl.info.tempo) / (60000.0 * (double)impl.info.sample_rate) : 4.0;
2✔
1594

1595
        impl.info.ppq_length = std::max(1, (int32_t)std::lround(beats * VLFileImpl::kREXPPQ));
2✔
1596
    }
1597

1598
    impl.processingGain = (uint16_t)std::clamp(impl.info.processing_gain > 0 ? impl.info.processing_gain : 1000, 0, 1000);
64✔
1599
    impl.transientEnabled = impl.info.transient_enabled != 0;
64✔
1600
    impl.transientAttack = (uint16_t)std::clamp(impl.info.transient_attack, 0, 1023);
64✔
1601
    impl.transientDecay = (uint16_t)std::clamp(impl.info.transient_decay > 0 ? impl.info.transient_decay : 1023, 0, 1023);
64✔
1602
    impl.transientStretch = (uint16_t)std::clamp(impl.info.transient_stretch, 0, 100);
64✔
1603
    impl.silenceSelected = impl.info.silence_selected != 0;
64✔
1604
    impl.finalizeRenderedLengths();
64✔
1605
}
64✔
1606

1607
static std::vector<uint8_t> buildREX2File(VLFileImpl& impl)
64✔
1608
{
1609
    normaliseInfoForSave(impl);
64✔
1610

1611
    VLDWOPCompressor comp;
64✔
1612
    const std::vector<uint8_t> sdat =
1613
        impl.info.channels == 2 ? comp.compressStereo(impl.pcm.data(), impl.totalFrames) : comp.compressMono(impl.pcm.data(), impl.totalFrames);
64✔
1614

1615
    VLIFFWriter w;
64✔
1616
    const size_t root = w.beginCat("REX2");
64✔
1617

1618
    {
1619
        const size_t c = w.beginChunk("HEAD");
64✔
1620
        const uint8_t head[] = {0x49, 0x0c, 0xf1, 0x8d, 0xbc, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
64✔
1621
                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1622
        w.putBytes(head, sizeof(head));
64✔
1623
        w.endChunk(c);
64✔
1624
    }
1625

1626
    if (hasCreatorInfo(impl.creator))
64✔
1627
    {
1628
        const size_t c = w.beginChunk("CREI");
8✔
1629
        writeCStringChunkString(w, impl.creator.name);
8✔
1630
        writeCStringChunkString(w, impl.creator.copyright);
8✔
1631
        writeCStringChunkString(w, impl.creator.url);
8✔
1632
        writeCStringChunkString(w, impl.creator.email);
8✔
1633
        writeCStringChunkString(w, impl.creator.free_text);
8✔
1634
        w.endChunk(c);
8✔
1635
    }
1636

1637
    {
1638
        const size_t c = w.beginChunk("GLOB");
64✔
1639
        w.put32((uint32_t)impl.slices.size());
64✔
1640
        w.put8(0);
64✔
1641
        w.put8(1);
64✔
1642
        w.put8(0);
64✔
1643
        w.put8((uint8_t)impl.info.time_sig_num);
64✔
1644
        w.put8((uint8_t)impl.info.time_sig_den);
64✔
1645
        w.put8(0x4e);
64✔
1646
        w.put8(0);
64✔
1647
        w.put8(0);
64✔
1648
        w.put16(impl.processingGain);
64✔
1649
        w.put16(1);
64✔
1650
        w.put32((uint32_t)impl.info.tempo);
64✔
1651
        w.put8(1);
64✔
1652
        w.put8(impl.silenceSelected ? 1 : 0);
64✔
1653
        w.endChunk(c);
64✔
1654
    }
1655

1656
    {
1657
        const size_t c = w.beginChunk("RECY");
64✔
1658
        w.put8(0xbc);
64✔
1659
        w.put8(0x02);
64✔
1660
        w.put8(0);
64✔
1661
        w.put8(0);
64✔
1662
        w.put8(0);
64✔
1663
        w.put8(1);
64✔
1664
        w.put8(0);
64✔
1665
        w.put8(0);
64✔
1666
        w.put32((uint32_t)impl.info.original_tempo);
64✔
1667
        w.put16(0);
64✔
1668
        w.put8(8);
64✔
1669
        w.endChunk(c);
64✔
1670
    }
1671

1672
    {
1673
        const size_t devl = w.beginCat("DEVL");
64✔
1674
        const size_t trsh = w.beginChunk("TRSH");
64✔
1675
        w.put8(impl.transientEnabled ? 1 : 0);
64✔
1676
        w.put16(impl.transientAttack);
64✔
1677
        w.put16(impl.transientDecay);
64✔
1678
        w.put16(impl.transientStretch);
64✔
1679
        w.endChunk(trsh);
64✔
1680

1681
        const size_t eq = w.beginChunk("EQ  ");
64✔
1682
        const uint8_t eqPayload[] = {0x00, 0x00, 0x0f, 0x00, 0x64, 0x00, 0x00, 0x03, 0xe8, 0x09, 0xc4, 0x00, 0x00, 0x03, 0xe8, 0x4e, 0x20};
64✔
1683
        w.putBytes(eqPayload, sizeof(eqPayload));
64✔
1684
        w.endChunk(eq);
64✔
1685

1686
        const size_t comp = w.beginChunk("COMP");
64✔
1687
        const uint8_t compPayload[] = {0x00, 0x00, 0x4d, 0x00, 0x27, 0x00, 0x42, 0x00, 0x38};
64✔
1688
        w.putBytes(compPayload, sizeof(compPayload));
64✔
1689
        w.endChunk(comp);
64✔
1690

1691
        w.endChunk(devl);
64✔
1692
    }
1693

1694
    {
1695
        const size_t slcl = w.beginCat("SLCL");
64✔
1696
        std::vector<VLSliceEntry> sorted = impl.slices;
64✔
1697
        std::sort(sorted.begin(), sorted.end(),
64✔
1698
                  [](const VLSliceEntry& a, const VLSliceEntry& b)
14,493✔
1699
                  {
1700
                      return a.sample_start < b.sample_start;
14,493✔
1701
                  });
1702
        for (const auto& s : sorted)
2,015✔
1703
        {
1704
            const size_t c = w.beginChunk("SLCE");
1,951✔
1705
            w.put32(s.sample_start);
1,951✔
1706
            w.put32(std::max<uint32_t>(1, s.sample_length));
1,951✔
1707
            w.put16(s.points);
1,951✔
1708
            uint8_t flags = 0;
1,951✔
1709
            if (s.state == kSliceMuted)
1,951✔
1710
                flags |= 0x01;
×
1711
            else if (s.state == kSliceLocked)
1,951✔
1712
                flags |= 0x02;
×
1713
            if (s.selected_flag)
1,951✔
1714
                flags |= 0x04;
×
1715
            w.put8(flags);
1,951✔
1716
            w.endChunk(c);
1,951✔
1717
        }
1718
        w.endChunk(slcl);
64✔
1719
    }
64✔
1720

1721
    {
1722
        const size_t c = w.beginChunk("SINF");
64✔
1723
        w.put8((uint8_t)impl.info.channels);
64✔
1724
        w.put8(bitDepthCode(impl.info.bit_depth));
64✔
1725
        w.put32((uint32_t)impl.info.sample_rate);
64✔
1726
        w.put32(impl.totalFrames);
64✔
1727
        w.put32(impl.loopStart);
64✔
1728
        w.put32(impl.loopEnd);
64✔
1729
        w.endChunk(c);
64✔
1730
    }
1731

1732
    writeSimpleChunk(w, "SDAT", sdat);
64✔
1733
    w.endChunk(root);
64✔
1734
    return w.data;
128✔
1735
}
64✔
1736

1737
} // anonymous namespace
1738

1739
/* -----------------------------------------------------------------------
1740
   VLFile_s  —  the opaque handle
1741
   ----------------------------------------------------------------------- */
1742

1743
struct VLFile_s
1744
{
1745
    VLFileImpl impl;
1746
    int32_t outputSampleRate = 44100;
1747
    bool isNew = false;
1748
    bool dirty = false;
1749
};
1750

1751
/* -----------------------------------------------------------------------
1752
   Open / close
1753
   ----------------------------------------------------------------------- */
1754

1755
VLFile vl_open(const char* path, VLError* err)
60✔
1756
{
1757
    auto set = [&](VLError e)
2✔
1758
    {
1759
        if (err)
2✔
1760
            *err = e;
2✔
1761
    };
62✔
1762

1763
    if (!path)
60✔
1764
    {
1765
        set(VL_ERROR_INVALID_ARG);
1✔
1766
        return nullptr;
1✔
1767
    }
1768

1769
    std::ifstream f(path, std::ios::binary | std::ios::ate);
59✔
1770
    if (!f)
59✔
1771
    {
1772
        set(VL_ERROR_FILE_NOT_FOUND);
1✔
1773
        return nullptr;
1✔
1774
    }
1775

1776
    const std::streamsize sz = f.tellg();
58✔
1777
    f.seekg(0);
58✔
1778

1779
    std::vector<char> buf((size_t)sz);
58✔
1780
    if (!f.read(buf.data(), sz))
58✔
1781
    {
1782
        set(VL_ERROR_FILE_CORRUPT);
×
1783
        return nullptr;
×
1784
    }
1785

1786
    return vl_open_from_memory(buf.data(), (size_t)sz, err);
58✔
1787
}
59✔
1788

1789
VLFile vl_open_from_memory(const void* data, size_t size, VLError* err)
105✔
1790
{
1791
    auto set = [&](VLError e)
105✔
1792
    {
1793
        if (err)
105✔
1794
            *err = e;
105✔
1795
    };
210✔
1796

1797
    if (!data || size == 0)
105✔
1798
    {
1799
        set(VL_ERROR_INVALID_ARG);
1✔
1800
        return nullptr;
1✔
1801
    }
1802

1803
    VLFile_s* h = new (std::nothrow) VLFile_s();
104✔
1804
    if (!h)
104✔
1805
    {
1806
        set(VL_ERROR_OUT_OF_MEMORY);
×
1807
        return nullptr;
×
1808
    }
1809

1810
    if (!h->impl.loadFromBuffer((const char*)data, size))
104✔
1811
    {
1812
        delete h;
31✔
1813
        set(VL_ERROR_FILE_CORRUPT);
31✔
1814
        return nullptr;
31✔
1815
    }
1816

1817
    h->outputSampleRate = h->impl.info.sample_rate ? h->impl.info.sample_rate : 44100;
73✔
1818
    set(VL_OK);
73✔
1819
    return h;
73✔
1820
}
1821

1822
VLFile vl_create_new(int32_t channels, int32_t sample_rate, int32_t tempo, VLError* err)
40✔
1823
{
1824
    auto set = [&](VLError e)
40✔
1825
    {
1826
        if (err)
40✔
1827
            *err = e;
24✔
1828
    };
80✔
1829

1830
    if (channels != 1 && channels != 2)
40✔
1831
    {
1832
        set(VL_ERROR_INVALID_ARG);
4✔
1833
        return nullptr;
4✔
1834
    }
1835

1836
    if (sample_rate < 8000 || sample_rate > 192000)
36✔
1837
    {
1838
        set(VL_ERROR_INVALID_SAMPLE_RATE);
8✔
1839
        return nullptr;
8✔
1840
    }
1841

1842
    if (tempo <= 0)
28✔
1843
    {
1844
        set(VL_ERROR_INVALID_ARG);
4✔
1845
        return nullptr;
4✔
1846
    }
1847

1848
    VLFile_s* h = new (std::nothrow) VLFile_s();
24✔
1849
    if (!h)
24✔
1850
    {
1851
        set(VL_ERROR_OUT_OF_MEMORY);
×
1852
        return nullptr;
×
1853
    }
1854

1855
    h->isNew = true;
24✔
1856
    h->outputSampleRate = sample_rate;
24✔
1857

1858
    h->impl.info.channels = channels;
24✔
1859
    h->impl.info.sample_rate = sample_rate;
24✔
1860
    h->impl.info.slice_count = 0;
24✔
1861
    h->impl.info.tempo = tempo;
24✔
1862
    h->impl.info.original_tempo = tempo;
24✔
1863
    h->impl.info.ppq_length = VLFileImpl::kREXPPQ * 4;
24✔
1864
    h->impl.info.time_sig_num = 4;
24✔
1865
    h->impl.info.time_sig_den = 4;
24✔
1866
    h->impl.info.bit_depth = 16;
24✔
1867
    h->impl.info.total_frames = 0;
24✔
1868
    h->impl.info.loop_start = 0;
24✔
1869
    h->impl.info.loop_end = 0;
24✔
1870
    h->impl.info.processing_gain = 1000;
24✔
1871
    h->impl.info.transient_enabled = 1;
24✔
1872
    h->impl.info.transient_attack = 0;
24✔
1873
    h->impl.info.transient_decay = 1023;
24✔
1874
    h->impl.info.transient_stretch = 0;
24✔
1875
    h->impl.info.silence_selected = 0;
24✔
1876
    h->impl.processingGain = 1000;
24✔
1877
    h->impl.transientEnabled = true;
24✔
1878
    h->impl.transientAttack = 0;
24✔
1879
    h->impl.transientDecay = 1023;
24✔
1880
    h->impl.transientStretch = 0;
24✔
1881
    h->impl.silenceSelected = false;
24✔
1882

1883
    set(VL_OK);
24✔
1884
    return h;
24✔
1885
}
1886

1887
void vl_close(VLFile file)
127✔
1888
{
1889
    delete file;
127✔
1890
}
127✔
1891

1892
/* -----------------------------------------------------------------------
1893
   Read: metadata
1894
   ----------------------------------------------------------------------- */
1895

1896
VLError vl_get_info(VLFile file, VLFileInfo* out)
82✔
1897
{
1898
    if (!file)
82✔
1899
        return VL_ERROR_INVALID_HANDLE;
1✔
1900

1901
    if (!out)
81✔
1902
        return VL_ERROR_INVALID_ARG;
16✔
1903

1904
    *out = file->impl.info;
65✔
1905
    return VL_OK;
65✔
1906
}
1907

1908
VLError vl_get_creator_info(VLFile file, VLCreatorInfo* out)
35✔
1909
{
1910
    if (!file)
35✔
1911
        return VL_ERROR_INVALID_HANDLE;
1✔
1912

1913
    if (!out)
34✔
1914
        return VL_ERROR_INVALID_ARG;
16✔
1915

1916
    const VLCreatorInfo& src = file->impl.creator;
18✔
1917
    if (!src.name[0] && !src.copyright[0] && !src.url[0] && !src.email[0] && !src.free_text[0])
18✔
1918
        return VL_ERROR_NO_CREATOR_INFO;
16✔
1919

1920
    *out = src;
2✔
1921
    return VL_OK;
2✔
1922
}
1923

1924
/* -----------------------------------------------------------------------
1925
   Read: slice enumeration
1926
   ----------------------------------------------------------------------- */
1927

1928
VLError vl_get_slice_info(VLFile file, int32_t index, VLSliceInfo* out)
709✔
1929
{
1930
    if (!file)
709✔
1931
        return VL_ERROR_INVALID_HANDLE;
1✔
1932

1933
    if (!out)
708✔
1934
        return VL_ERROR_INVALID_ARG;
32✔
1935

1936
    if (index < 0 || (size_t)index >= file->impl.slices.size())
676✔
1937
        return VL_ERROR_INVALID_SLICE;
32✔
1938

1939
    const VLSliceEntry& s = file->impl.slices[(size_t)index];
644✔
1940
    out->ppq_pos = (int32_t)s.ppq_pos;
644✔
1941
    out->sample_length = (int32_t)s.sample_length;
644✔
1942
    out->sample_start = (int32_t)s.sample_start;
644✔
1943
    return VL_OK;
644✔
1944
}
1945

1946
/* -----------------------------------------------------------------------
1947
   Read: sample extraction
1948
   ----------------------------------------------------------------------- */
1949

1950
VLError vl_set_output_sample_rate(VLFile file, int32_t rate)
49✔
1951
{
1952
    if (!file)
49✔
1953
        return VL_ERROR_INVALID_HANDLE;
1✔
1954

1955
    if (rate < 8000 || rate > 192000)
48✔
1956
        return VL_ERROR_INVALID_SAMPLE_RATE;
16✔
1957

1958
    if (rate != file->impl.info.sample_rate)
32✔
1959
        return VL_ERROR_NOT_IMPLEMENTED;
16✔
1960

1961
    file->outputSampleRate = rate;
16✔
1962
    return VL_OK;
16✔
1963
}
1964

1965
int32_t vl_get_slice_frame_count(VLFile file, int32_t index)
1,338✔
1966
{
1967
    if (!file)
1,338✔
1968
        return (int32_t)VL_ERROR_INVALID_HANDLE;
1✔
1969

1970
    if (index < 0 || (size_t)index >= file->impl.slices.size())
1,337✔
1971
        return (int32_t)VL_ERROR_INVALID_SLICE;
34✔
1972

1973
    return (int32_t)file->impl.slices[(size_t)index].rendered_length;
1,303✔
1974
}
1975

1976
VLError vl_decode_slice(VLFile file, int32_t index, float* left, float* right, int32_t capacity, int32_t* frames_out)
1,345✔
1977
{
1978
    if (!file)
1,345✔
1979
        return VL_ERROR_INVALID_HANDLE;
1✔
1980

1981
    if (!left)
1,344✔
1982
        return VL_ERROR_INVALID_ARG;
14✔
1983

1984
    if (index < 0 || (size_t)index >= file->impl.slices.size())
1,330✔
1985
        return VL_ERROR_INVALID_SLICE;
28✔
1986

1987
    const VLSliceEntry& s = file->impl.slices[(size_t)index];
1,302✔
1988
    const int32_t needed = (int32_t)s.rendered_length;
1,302✔
1989
    if (capacity < needed)
1,302✔
1990
        return VL_ERROR_BUFFER_TOO_SMALL;
14✔
1991

1992
    const auto& pcm = file->impl.pcm;
1,288✔
1993
    if (pcm.empty())
1,288✔
1994
        return VL_ERROR_FILE_CORRUPT;
×
1995

1996
    const uint32_t requestedFrames = (uint32_t)needed;
1,288✔
1997
    uint32_t sourceStart = s.sample_start;
1,288✔
1998
    if (sourceStart >= file->impl.totalFrames)
1,288✔
1999
    {
2000
        std::fill(left, left + needed, 0.f);
1✔
2001

2002
        if (right)
1✔
2003
            std::fill(right, right + needed, 0.f);
1✔
2004

2005
        if (frames_out)
1✔
2006
            *frames_out = needed;
1✔
2007

2008
        return VL_OK;
1✔
2009
    }
2010

2011
    uint32_t sourceFrames = s.sample_length;
1,287✔
2012
    if (file->impl.loopEnd > file->impl.loopStart && sourceStart < file->impl.loopEnd)
1,287✔
2013
        sourceFrames = std::min<uint32_t>(sourceFrames, file->impl.loopEnd - sourceStart);
1,241✔
2014
    sourceFrames = std::min<uint32_t>(sourceFrames, file->impl.totalFrames - sourceStart);
1,287✔
2015

2016
    const uint32_t sourceEnd = sourceStart + sourceFrames;
1,287✔
2017
    const auto segmentLoop = file->impl.findSegmentLoop(sourceStart, sourceEnd);
1,287✔
2018
    const int32_t ch = std::max(1, file->impl.info.channels);
1,287✔
2019
    const float samplerGain = (float)file->impl.processingGain * 0.000833333354f;
1,287✔
2020

2021
    uint32_t samplePos = std::min(sourceStart + 2u, sourceEnd);
1,287✔
2022
    int loopPhase = 0; // 0: forward source, 1: forward loop, 2: backward loop
1,287✔
2023
    bool stretchPhase = false;
1,287✔
2024
    float stretchEnv = 1.0f;
1,287✔
2025
    const uint32_t stretchFrameCount =
2026
        std::max<uint32_t>(1u, sourceEnd - segmentLoop.end + (requestedFrames > sourceFrames ? requestedFrames - sourceFrames : 0u));
1,287✔
2027
    const float stretchEnvDec = 1.0f / (float)stretchFrameCount;
1,287✔
2028
    float loopLevelComp = 1.0f;
1,287✔
2029
    const float loopLevelCompInc =
1,287✔
2030
        (segmentLoop.end > segmentLoop.start) ? (1.0f - segmentLoop.volumeCompensation) / (float)(segmentLoop.end - segmentLoop.start) : 0.0f;
1,287✔
2031

2032
    for (uint32_t f = 0; f < requestedFrames; ++f)
3,990,542✔
2033
    {
2034
        const size_t src = (size_t)samplePos * (size_t)ch;
3,989,255✔
2035
        float l = 0.f;
3,989,255✔
2036
        float r = 0.f;
3,989,255✔
2037

2038
        if (src < pcm.size())
3,989,255✔
2039
        {
2040
            const float level = samplerGain * stretchEnv * loopLevelComp;
3,989,255✔
2041
            l = ((float)pcm[src] / 32768.f) * level;
3,989,255✔
2042
            r = (ch >= 2 && src + 1 < pcm.size()) ? ((float)pcm[src + 1] / 32768.f) * level : l;
3,989,255✔
2043
        }
2044

2045
        left[f] = l;
3,989,255✔
2046
        if (right)
3,989,255✔
2047
            right[f] = r;
529,540✔
2048

2049
        if (stretchPhase)
3,989,255✔
2050
        {
2051
            stretchEnv = std::max(0.0f, stretchEnv - stretchEnvDec);
922,429✔
2052
            if (loopPhase == 1)
922,429✔
2053
                loopLevelComp += loopLevelCompInc;
175,657✔
2054
            else if (loopPhase == 2)
746,772✔
2055
                loopLevelComp -= loopLevelCompInc;
746,772✔
2056
        }
2057

2058
        if (loopPhase <= 1)
3,989,255✔
2059
        {
2060
            ++samplePos;
3,242,483✔
2061
            if (samplePos >= segmentLoop.end)
3,242,483✔
2062
            {
2063
                stretchPhase = true;
1,354✔
2064
                loopPhase = 2;
1,354✔
2065
                if (samplePos > 0)
1,354✔
2066
                    --samplePos;
1,354✔
2067
                if (samplePos <= segmentLoop.start)
1,354✔
2068
                    loopPhase = 1;
×
2069
            }
2070
        }
2071
        else
2072
        {
2073
            if (samplePos > 0)
746,772✔
2074
                --samplePos;
746,772✔
2075
            if (samplePos <= segmentLoop.start)
746,772✔
2076
                loopPhase = 1;
470✔
2077
        }
2078
    }
2079

2080
    if (frames_out)
1,287✔
2081
        *frames_out = needed;
1,287✔
2082

2083
    return VL_OK;
1,287✔
2084
}
2085

2086
/* -----------------------------------------------------------------------
2087
   Write: assembly from audio slices
2088
   ----------------------------------------------------------------------- */
2089

2090
VLError vl_set_info(VLFile file, const VLFileInfo* info)
28✔
2091
{
2092
    if (!file)
28✔
2093
        return VL_ERROR_INVALID_HANDLE;
1✔
2094

2095
    if (!info)
27✔
2096
        return VL_ERROR_INVALID_ARG;
1✔
2097

2098
    if (!file->impl.slices.empty() || !file->impl.pcm.empty())
26✔
2099
        return VL_ERROR_ALREADY_HAS_DATA;
1✔
2100

2101
    if (info->channels != 1 && info->channels != 2)
25✔
2102
        return VL_ERROR_INVALID_ARG;
×
2103

2104
    if (info->sample_rate < 8000 || info->sample_rate > 192000)
25✔
2105
        return VL_ERROR_INVALID_SAMPLE_RATE;
1✔
2106

2107
    if (info->tempo <= 0)
24✔
2108
        return VL_ERROR_INVALID_ARG;
1✔
2109

2110
    file->impl.info = *info;
23✔
2111
    file->impl.info.channels = info->channels;
23✔
2112
    file->impl.info.sample_rate = info->sample_rate;
23✔
2113
    file->impl.info.bit_depth = 16;
23✔
2114
    file->impl.processingGain = (uint16_t)std::clamp(info->processing_gain > 0 ? info->processing_gain : 1000, 0, 1000);
23✔
2115
    file->impl.transientEnabled = info->transient_enabled != 0;
23✔
2116
    file->impl.transientAttack = (uint16_t)std::clamp(info->transient_attack, 0, 1023);
23✔
2117
    file->impl.transientDecay = (uint16_t)std::clamp(info->transient_decay > 0 ? info->transient_decay : 1023, 0, 1023);
23✔
2118
    file->impl.transientStretch = (uint16_t)std::clamp(info->transient_stretch, 0, 100);
23✔
2119
    file->impl.silenceSelected = info->silence_selected != 0;
23✔
2120
    file->outputSampleRate = info->sample_rate;
23✔
2121
    file->dirty = true;
23✔
2122

2123
    return VL_OK;
23✔
2124
}
2125

2126
VLError vl_set_creator_info(VLFile file, const VLCreatorInfo* info)
6✔
2127
{
2128
    if (!file)
6✔
2129
        return VL_ERROR_INVALID_HANDLE;
1✔
2130

2131
    if (!info)
5✔
2132
        return VL_ERROR_INVALID_ARG;
1✔
2133

2134
    if (!file->impl.slices.empty() || !file->impl.pcm.empty())
4✔
2135
        return VL_ERROR_ALREADY_HAS_DATA;
1✔
2136

2137
    file->impl.creator = *info;
3✔
2138
    file->impl.creator.name[sizeof(file->impl.creator.name) - 1] = '\0';
3✔
2139
    file->impl.creator.copyright[sizeof(file->impl.creator.copyright) - 1] = '\0';
3✔
2140
    file->impl.creator.url[sizeof(file->impl.creator.url) - 1] = '\0';
3✔
2141
    file->impl.creator.email[sizeof(file->impl.creator.email) - 1] = '\0';
3✔
2142
    file->impl.creator.free_text[sizeof(file->impl.creator.free_text) - 1] = '\0';
3✔
2143
    file->dirty = true;
3✔
2144

2145
    return VL_OK;
3✔
2146
}
2147

2148
static int32_t addSliceAtSample(VLFile file, uint32_t sample_start, int32_t ppq_pos, const float* left, const float* right, int32_t frames)
655✔
2149
{
2150
    if (!file)
655✔
2151
        return (int32_t)VL_ERROR_INVALID_HANDLE;
×
2152

2153
    if (!left || frames <= 0 || ppq_pos < 0)
655✔
2154
        return (int32_t)VL_ERROR_INVALID_ARG;
2✔
2155

2156
    const int32_t channels = std::clamp(file->impl.info.channels, 1, 2);
653✔
2157
    if (channels == 2 && !right)
653✔
2158
        return (int32_t)VL_ERROR_INVALID_ARG;
1✔
2159

2160
    const uint32_t end = sample_start + (uint32_t)frames;
652✔
2161
    const uint32_t declaredFrames = file->impl.info.total_frames > 0 ? (uint32_t)file->impl.info.total_frames : 0u;
652✔
2162
    const uint32_t requiredFrames = std::max(end, declaredFrames);
652✔
2163
    const size_t required = (size_t)requiredFrames * (size_t)channels;
652✔
2164

2165
    if (required > file->impl.pcm.max_size())
652✔
2166
        return (int32_t)VL_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE max_size is not reachable in tests.
2167

2168
    if (file->impl.pcm.size() < required)
652✔
2169
    {
2170
        if (!file->impl.pcm.resize(required, 0))
37✔
2171
            return (int32_t)VL_ERROR_OUT_OF_MEMORY;
×
2172
    }
2173

2174
    for (int32_t f = 0; f < frames; ++f)
1,992,532✔
2175
    {
2176
        const size_t dst = ((size_t)sample_start + (size_t)f) * (size_t)channels;
1,991,880✔
2177
        file->impl.pcm[dst] = floatToS16(left[f]);
1,991,880✔
2178
        if (channels == 2)
1,991,880✔
2179
            file->impl.pcm[dst + 1] = floatToS16(right[f]);
261,196✔
2180
    }
2181

2182
    VLSliceEntry s;
652✔
2183
    s.ppq_pos = (uint32_t)ppq_pos;
652✔
2184
    s.sample_start = sample_start;
652✔
2185
    s.sample_length = (uint32_t)frames;
652✔
2186
    s.rendered_length = (uint32_t)frames;
652✔
2187
    s.points = 0x7fff;
652✔
2188
    s.selected_flag = 0;
652✔
2189
    s.state = kSliceNormal;
652✔
2190

2191
    if (file->impl.slices.size() == file->impl.slices.max_size())
652✔
2192
        return (int32_t)VL_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE max_size is not reachable in tests.
2193
    file->impl.slices.push_back(s);
652✔
2194

2195
    file->impl.totalFrames = (uint32_t)(file->impl.pcm.size() / (size_t)channels);
652✔
2196
    file->impl.info.total_frames = (int32_t)file->impl.totalFrames;
652✔
2197
    file->impl.info.slice_count = (int32_t)file->impl.slices.size();
652✔
2198
    if (file->impl.info.loop_end <= file->impl.info.loop_start)
652✔
2199
        file->impl.info.loop_end = file->impl.info.total_frames;
8✔
2200
    file->dirty = true;
652✔
2201

2202
    return (int32_t)file->impl.slices.size() - 1;
652✔
2203
}
2204

2205
int32_t vl_add_slice(VLFile file, int32_t ppq_pos, const float* left, const float* right, int32_t frames)
657✔
2206
{
2207
    if (!file)
657✔
2208
        return (int32_t)VL_ERROR_INVALID_HANDLE;
1✔
2209

2210
    if (ppq_pos < 0)
656✔
2211
        return (int32_t)VL_ERROR_INVALID_ARG;
1✔
2212

2213
    const uint32_t start = calcSampleStartFromPPQ(file->impl, (uint32_t)ppq_pos);
655✔
2214
    return addSliceAtSample(file, start, ppq_pos, left, right, frames);
655✔
2215
}
2216

2217
VLError vl_remove_slice(VLFile file, int32_t index)
4✔
2218
{
2219
    if (!file)
4✔
2220
        return VL_ERROR_INVALID_HANDLE;
1✔
2221

2222
    if (index < 0 || (size_t)index >= file->impl.slices.size())
3✔
2223
        return VL_ERROR_INVALID_SLICE;
2✔
2224

2225
    file->impl.slices.erase(file->impl.slices.begin() + index);
1✔
2226
    file->impl.info.slice_count = (int32_t)file->impl.slices.size();
1✔
2227
    file->dirty = true;
1✔
2228

2229
    return VL_OK;
1✔
2230
}
2231

2232
VLError vl_save(VLFile file, const char* path)
4✔
2233
{
2234
    if (!file)
4✔
2235
        return VL_ERROR_INVALID_HANDLE;
1✔
2236

2237
    if (!path)
3✔
2238
        return VL_ERROR_INVALID_ARG;
1✔
2239

2240
    size_t size = 0;
2✔
2241
    VLError e = vl_save_to_memory(file, nullptr, &size);
2✔
2242
    if (e != VL_OK)
2✔
2243
        return e;
×
2244

2245
    std::vector<uint8_t> buf(size);
2✔
2246
    e = vl_save_to_memory(file, buf.data(), &size);
2✔
2247
    if (e != VL_OK)
2✔
2248
        return e;
×
2249

2250
    std::ofstream out(path, std::ios::binary);
2✔
2251
    if (!out)
2✔
2252
        return VL_ERROR_INVALID_ARG;
1✔
2253

2254
    out.write((const char*)buf.data(), (std::streamsize)size);
1✔
2255
    return out ? VL_OK : VL_ERROR_FILE_CORRUPT;
1✔
2256
}
2✔
2257

2258
VLError vl_save_to_memory(VLFile file, void* buf, size_t* size_out)
130✔
2259
{
2260
    if (!file)
130✔
2261
        return VL_ERROR_INVALID_HANDLE;
1✔
2262

2263
    if (!size_out)
129✔
2264
        return VL_ERROR_INVALID_ARG;
16✔
2265

2266
    if (!file->isNew && !file->dirty && !file->impl.fileData.empty())
113✔
2267
    {
2268
        const std::vector<uint8_t>& encoded = file->impl.fileData;
48✔
2269

2270
        if (!buf)
48✔
2271
        {
2272
            *size_out = encoded.size();
16✔
2273
            return VL_OK;
16✔
2274
        }
2275

2276
        if (*size_out < encoded.size())
32✔
2277
        {
2278
            *size_out = encoded.size();
16✔
2279
            return VL_ERROR_BUFFER_TOO_SMALL;
16✔
2280
        }
2281

2282
        std::memcpy(buf, encoded.data(), encoded.size());
16✔
2283
        *size_out = encoded.size();
16✔
2284
        return VL_OK;
16✔
2285
    }
2286

2287
    if (file->impl.slices.empty() || file->impl.pcm.empty())
65✔
2288
        return VL_ERROR_INVALID_ARG;
1✔
2289

2290
    std::vector<uint8_t> encoded = buildREX2File(file->impl);
64✔
2291

2292
    if (!buf)
64✔
2293
    {
2294
        *size_out = encoded.size();
25✔
2295
        return VL_OK;
25✔
2296
    }
2297

2298
    if (*size_out < encoded.size())
39✔
2299
    {
2300
        *size_out = encoded.size();
14✔
2301
        return VL_ERROR_BUFFER_TOO_SMALL;
14✔
2302
    }
2303

2304
    std::memcpy(buf, encoded.data(), encoded.size());
25✔
2305
    *size_out = encoded.size();
25✔
2306
    return VL_OK;
25✔
2307
}
64✔
2308

2309
/* -----------------------------------------------------------------------
2310
   Utility
2311
   ----------------------------------------------------------------------- */
2312

2313
const char* vl_error_string(VLError err)
13✔
2314
{
2315
    switch (err)
13✔
2316
    {
2317
        case VL_OK:
1✔
2318
            return "OK";
1✔
2319
        case VL_ERROR_INVALID_HANDLE:
1✔
2320
            return "invalid handle";
1✔
2321
        case VL_ERROR_INVALID_ARG:
1✔
2322
            return "invalid argument";
1✔
2323
        case VL_ERROR_FILE_NOT_FOUND:
1✔
2324
            return "file not found";
1✔
2325
        case VL_ERROR_FILE_CORRUPT:
1✔
2326
            return "file corrupt or unsupported format";
1✔
2327
        case VL_ERROR_OUT_OF_MEMORY:
1✔
2328
            return "out of memory";
1✔
2329
        case VL_ERROR_INVALID_SLICE:
1✔
2330
            return "invalid slice index";
1✔
2331
        case VL_ERROR_INVALID_SAMPLE_RATE:
1✔
2332
            return "invalid sample rate";
1✔
2333
        case VL_ERROR_BUFFER_TOO_SMALL:
1✔
2334
            return "buffer too small";
1✔
2335
        case VL_ERROR_NO_CREATOR_INFO:
1✔
2336
            return "no creator info available";
1✔
2337
        case VL_ERROR_NOT_IMPLEMENTED:
1✔
2338
            return "not implemented";
1✔
2339
        case VL_ERROR_ALREADY_HAS_DATA:
1✔
2340
            return "already has data";
1✔
2341
        default:
1✔
2342
            return "unknown error";
1✔
2343
    }
2344
}
2345

2346
const char* vl_version_string(void)
1✔
2347
{
2348
    return "velociloops 0.1.0";
1✔
2349
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc