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

kunitoki / VelociLoops / 25023792810

27 Apr 2026 10:51PM UTC coverage: 93.938% (+0.6%) from 93.382%
25023792810

push

github

kunitoki
More edge case tests

1689 of 1798 relevant lines covered (93.94%)

1408703.16 hits per line

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

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

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

15
/* -----------------------------------------------------------------------
16
   DWOP decompressor
17
   ----------------------------------------------------------------------- */
18

19
namespace
20
{
21

22
inline int32_t clampInt32(int64_t v)
38,533,808✔
23
{
24
    if (v > std::numeric_limits<int32_t>::max())
38,533,808✔
25
        return std::numeric_limits<int32_t>::max();
×
26

27
    if (v < std::numeric_limits<int32_t>::min())
38,533,808✔
28
        return std::numeric_limits<int32_t>::min();
×
29

30
    return static_cast<int32_t>(v);
38,533,808✔
31
}
32

33
inline int32_t addInt32(int32_t a, int32_t b)
15,629,721✔
34
{
35
    return clampInt32(static_cast<int64_t>(a) + static_cast<int64_t>(b));
15,629,721✔
36
}
37

38
inline int32_t subInt32(int32_t a, int32_t b)
22,904,087✔
39
{
40
    return clampInt32(static_cast<int64_t>(a) - static_cast<int64_t>(b));
22,904,087✔
41
}
42

43
struct VLChannelState
44
{
45
    int32_t deltas[5] = {0, 0, 0, 0, 0};
46
    uint32_t averages[5] = {2560, 2560, 2560, 2560, 2560};
47
};
48

49
class VLDWOPDecompressor
50
{
51
public:
52
    VLChannelState ch[2];
53
    uint32_t currentWord = 0;
54
    int32_t bitsLeft = 0;
55
    const uint32_t* inputPtr = nullptr;
56
    const uint32_t* endPtr = nullptr;
57
    std::vector<uint32_t> buf;
58

59
    void init(const uint8_t* data, size_t size)
80✔
60
    {
61
        const size_t words = size / 4;
80✔
62
        buf.resize(words);
80✔
63

64
        for (size_t i = 0; i < words; ++i)
2,371,222✔
65
        {
66
            const size_t b = i * 4;
2,371,142✔
67
            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,371,142✔
68
        }
69

70
        inputPtr = buf.data();
80✔
71
        endPtr = buf.data() + buf.size();
80✔
72
        currentWord = 0;
80✔
73
        bitsLeft = 0;
80✔
74
    }
80✔
75

76
    bool decompressMono(uint32_t frameCount, int32_t* out, int32_t bitDepth)
66✔
77
    {
78
        if (!out || frameCount == 0)
66✔
79
            return false;
×
80

81
        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;
66✔
82

83
        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];
66✔
84

85
        uint32_t j = 2;
66✔
86
        int32_t rbits = 0;
66✔
87

88
        uint32_t cw = currentWord;
66✔
89
        int32_t bl = bitsLeft;
66✔
90
        const uint32_t* inp = inputPtr;
66✔
91
        bool eof = false;
66✔
92

93
        for (uint32_t f = 0; f < frameCount && !eof; ++f)
7,605,292✔
94
        {
95
            uint32_t minAvg = a0;
7,605,226✔
96
            int minIdx = 0;
7,605,226✔
97

98
            if (a1 < minAvg)
7,605,226✔
99
            {
100
                minAvg = a1;
7,104,248✔
101
                minIdx = 1;
7,104,248✔
102
            }
103

104
            if (a2 < minAvg)
7,605,226✔
105
            {
106
                minAvg = a2;
3,176,791✔
107
                minIdx = 2;
3,176,791✔
108
            }
109

110
            if (a3 < minAvg)
7,605,226✔
111
            {
112
                minAvg = a3;
1,903,334✔
113
                minIdx = 3;
1,903,334✔
114
            }
115

116
            if (a4 < minAvg)
7,605,226✔
117
            {
118
                minAvg = a4;
1,209,460✔
119
                minIdx = 4;
1,209,460✔
120
            }
121

122
            uint32_t step = ((minAvg * 3u) + 36u) >> 7;
7,605,226✔
123
            uint32_t prefixSum = 0;
7,605,226✔
124
            int zerosWin = 7;
7,605,226✔
125

126
            while (true)
127
            {
128
                bool bit = readBit(cw, bl, inp, eof);
14,169,065✔
129

130
                if (eof)
14,169,065✔
131
                    break;
×
132

133
                if (bit)
14,169,065✔
134
                    break;
7,605,226✔
135

136
                if (step > 0 && prefixSum > 0xFFFFFFFFu - step)
6,563,839✔
137
                {
138
                    eof = true;
×
139
                    break;
×
140
                }
141

142
                prefixSum += step;
6,563,839✔
143
                if (--zerosWin == 0)
6,563,839✔
144
                {
145
                    step <<= 2;
39,692✔
146
                    zerosWin = 7;
39,692✔
147
                }
148
            }
6,563,839✔
149

150
            if (eof)
7,605,226✔
151
                break;
×
152

153
            adjustJRbits(step, j, rbits);
7,605,226✔
154

155
            uint32_t rem = (rbits > 0) ? readBits(rbits, cw, bl, inp, eof) : 0;
7,605,226✔
156
            if (eof)
7,605,226✔
157
                break;
×
158

159
            const int64_t thresh = static_cast<int64_t>(j) - static_cast<int64_t>(step);
7,605,226✔
160
            if (static_cast<int64_t>(rem) - thresh >= 0)
7,605,226✔
161
            {
162
                const uint32_t extra = readBits(1, cw, bl, inp, eof);
3,723,629✔
163

164
                if (eof)
3,723,629✔
165
                    break;
×
166

167
                rem = rem * 2u - static_cast<uint32_t>(thresh) + extra;
3,723,629✔
168
            }
169

170
            const uint32_t codeVal = rem + prefixSum;
7,605,226✔
171
            const int32_t signed2x = -(int32_t)(codeVal & 1u) ^ (int32_t)codeVal;
7,605,226✔
172
            const int32_t s2x = applyPredictor(minIdx, signed2x, d0, d1, d2, d3, d4);
7,605,226✔
173
            out[f] = clampSample(s2x >> 1, bitDepth);
7,605,226✔
174
            updateAverages(a0, a1, a2, a3, a4, d0, d1, d2, d3, d4);
7,605,226✔
175
        }
176

177
        ch[0].deltas[0] = d0 >> 1;
66✔
178
        ch[0].deltas[1] = d1 >> 1;
66✔
179
        ch[0].deltas[2] = d2 >> 1;
66✔
180
        ch[0].deltas[3] = d3 >> 1;
66✔
181
        ch[0].deltas[4] = d4 >> 1;
66✔
182
        ch[0].averages[0] = a0;
66✔
183
        ch[0].averages[1] = a1;
66✔
184
        ch[0].averages[2] = a2;
66✔
185
        ch[0].averages[3] = a3;
66✔
186
        ch[0].averages[4] = a4;
66✔
187
        currentWord = cw;
66✔
188
        bitsLeft = bl;
66✔
189
        inputPtr = inp;
66✔
190
        return !eof;
66✔
191
    }
192

193
    bool decompressStereo(uint32_t frameCount, int32_t* out, int32_t bitDepth)
14✔
194
    {
195
        if (!out || frameCount == 0)
14✔
196
            return false;
×
197

198
        int32_t d[2][5];
199
        uint32_t a[2][5];
200
        for (int c = 0; c < 2; ++c)
42✔
201
        {
202
            for (int i = 0; i < 5; ++i)
168✔
203
            {
204
                d[c][i] = ch[c].deltas[i] * 2;
140✔
205
                a[c][i] = ch[c].averages[i];
140✔
206
            }
207
        }
208

209
        uint32_t j[2] = {2, 2};
14✔
210
        int32_t rbits[2] = {0, 0};
14✔
211

212
        uint32_t cw = currentWord;
14✔
213
        int32_t bl = bitsLeft;
14✔
214
        const uint32_t* inp = inputPtr;
14✔
215
        bool eof = false;
14✔
216

217
        for (uint32_t f = 0; f < frameCount && !eof; ++f)
1,014,127✔
218
        {
219
            int32_t ch2x[2] = {0, 0};
1,014,113✔
220
            for (int c = 0; c < 2 && !eof; ++c)
3,042,339✔
221
            {
222
                uint32_t minAvg = a[c][0];
2,028,226✔
223
                int minIdx = 0;
2,028,226✔
224
                if (a[c][1] < minAvg)
2,028,226✔
225
                {
226
                    minAvg = a[c][1];
1,532,211✔
227
                    minIdx = 1;
1,532,211✔
228
                }
229

230
                if (a[c][2] < minAvg)
2,028,226✔
231
                {
232
                    minAvg = a[c][2];
502,457✔
233
                    minIdx = 2;
502,457✔
234
                }
235

236
                if (a[c][3] < minAvg)
2,028,226✔
237
                {
238
                    minAvg = a[c][3];
105,499✔
239
                    minIdx = 3;
105,499✔
240
                }
241

242
                if (a[c][4] < minAvg)
2,028,226✔
243
                {
244
                    minAvg = a[c][4];
95,547✔
245
                    minIdx = 4;
95,547✔
246
                }
247

248
                uint32_t step = ((minAvg * 3u) + 36u) >> 7;
2,028,226✔
249
                uint32_t prefixSum = 0;
2,028,226✔
250
                int zerosWin = 7;
2,028,226✔
251

252
                while (true)
253
                {
254
                    bool bit = readBit(cw, bl, inp, eof);
3,488,929✔
255

256
                    if (eof)
3,488,929✔
257
                        break;
×
258

259
                    if (bit)
3,488,929✔
260
                        break;
2,028,226✔
261

262
                    if (step > 0 && prefixSum > 0xFFFFFFFFu - step)
1,460,703✔
263
                    {
264
                        eof = true;
×
265
                        break;
×
266
                    }
267

268
                    prefixSum += step;
1,460,703✔
269
                    if (--zerosWin == 0)
1,460,703✔
270
                    {
271
                        step <<= 2;
1,396✔
272
                        zerosWin = 7;
1,396✔
273
                    }
274
                }
1,460,703✔
275

276
                if (eof)
2,028,226✔
277
                    break;
×
278

279
                adjustJRbits(step, j[c], rbits[c]);
2,028,226✔
280

281
                uint32_t rem = (rbits[c] > 0) ? readBits(rbits[c], cw, bl, inp, eof) : 0;
2,028,226✔
282
                if (eof)
2,028,226✔
283
                    break;
×
284

285
                const int64_t thresh = static_cast<int64_t>(j[c]) - static_cast<int64_t>(step);
2,028,226✔
286
                if (static_cast<int64_t>(rem) - thresh >= 0)
2,028,226✔
287
                {
288
                    const uint32_t extra = readBits(1, cw, bl, inp, eof);
830,485✔
289

290
                    if (eof)
830,485✔
291
                        break;
×
292

293
                    rem = rem * 2u - static_cast<uint32_t>(thresh) + extra;
830,485✔
294
                }
295

296
                const uint32_t codeVal = rem + prefixSum;
2,028,226✔
297
                const int32_t signed2x = -(int32_t)(codeVal & 1u) ^ (int32_t)codeVal;
2,028,226✔
298
                ch2x[c] = applyPredictor(minIdx, signed2x, d[c][0], d[c][1], d[c][2], d[c][3], d[c][4]);
2,028,226✔
299
                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,028,226✔
300
            }
301

302
            if (eof)
1,014,113✔
303
                break;
×
304

305
            out[f * 2 + 0] = clampSample(ch2x[0] >> 1, bitDepth);
1,014,113✔
306
            out[f * 2 + 1] = clampSample(((int64_t)ch2x[0] + (int64_t)ch2x[1]) >> 1, bitDepth);
1,014,113✔
307
        }
308

309
        for (int c = 0; c < 2; ++c)
42✔
310
        {
311
            for (int i = 0; i < 5; ++i)
168✔
312
            {
313
                ch[c].deltas[i] = d[c][i] >> 1;
140✔
314
                ch[c].averages[i] = a[c][i];
140✔
315
            }
316
        }
317
        currentWord = cw;
14✔
318
        bitsLeft = bl;
14✔
319
        inputPtr = inp;
14✔
320
        return !eof;
14✔
321
    }
322

323
private:
324
    static int32_t clampSample(int64_t v, int32_t bitDepth)
9,633,452✔
325
    {
326
        const int32_t maxSample = bitDepth == 24 ? 8388607 : 32767;
9,633,452✔
327
        const int32_t minSample = bitDepth == 24 ? -8388608 : -32768;
9,633,452✔
328

329
        if (v > maxSample)
9,633,452✔
330
            return maxSample;
×
331

332
        if (v < minSample)
9,633,452✔
333
            return minSample;
×
334

335
        return (int32_t)v;
9,633,452✔
336
    }
337

338
    bool readBit(uint32_t& cw, int32_t& bl, const uint32_t*& inp, bool& eof)
17,657,994✔
339
    {
340
        const int32_t blBefore = bl--;
17,657,994✔
341
        if (blBefore - 1 < 0)
17,657,994✔
342
        {
343
            if (inp >= endPtr)
555,386✔
344
            {
345
                eof = true;
×
346
                return false;
×
347
            }
348

349
            cw = *inp++;
555,386✔
350
            bl = 0x1f;
555,386✔
351
        }
352

353
        const bool bit = (int32_t)cw < 0;
17,657,994✔
354
        cw <<= 1;
17,657,994✔
355
        return bit;
17,657,994✔
356
    }
357

358
    uint32_t readBits(int n, uint32_t& cw, int32_t& bl, const uint32_t*& inp, bool& eof)
13,401,875✔
359
    {
360
        if (n <= 0 || n > 31)
13,401,875✔
361
        {
362
            eof = true;
×
363
            return 0;
×
364
        }
365

366
        uint32_t result = cw >> (32 - n);
13,401,875✔
367
        cw <<= n;
13,401,875✔
368

369
        const int32_t blBefore = bl;
13,401,875✔
370
        bl -= n;
13,401,875✔
371

372
        if (blBefore - n < 0)
13,401,875✔
373
        {
374
            if (inp >= endPtr)
1,815,701✔
375
            {
376
                eof = true;
×
377
                return result;
×
378
            }
379

380
            const uint32_t next = *inp++;
1,815,701✔
381
            bl += 32;
1,815,701✔
382
            result |= next >> bl;
1,815,701✔
383
            cw = next << (32 - bl);
1,815,701✔
384
        }
385

386
        return result;
13,401,875✔
387
    }
388

389
    static void adjustJRbits(uint32_t step, uint32_t& j, int32_t& rbits)
9,633,452✔
390
    {
391
        if (step < j)
9,633,452✔
392
        {
393
            for (uint32_t jt = j >> 1; step < jt; jt >>= 1)
9,661,296✔
394
            {
395
                j = jt;
164,431✔
396
                --rbits;
164,431✔
397
            }
398
        }
399
        else
400
        {
401
            while (step >= j)
301,456✔
402
            {
403
                const uint32_t prev = j;
164,869✔
404

405
                j <<= 1;
164,869✔
406
                ++rbits;
164,869✔
407

408
                if (j <= prev)
164,869✔
409
                {
410
                    j = prev;
×
411
                    break;
×
412
                }
413
            }
414
        }
415
    }
9,633,452✔
416

417
    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,633,452✔
418
    {
419
        switch (idx)
9,633,452✔
420
        {
421
            case 0:
996,992✔
422
            {
423
                const int32_t t0 = subInt32(s2x, d0), t1 = subInt32(t0, d1), t2 = subInt32(t1, d2);
996,992✔
424
                d4 = subInt32(t2, d3);
996,992✔
425
                d3 = t2;
996,992✔
426
                d2 = t1;
996,992✔
427
                d1 = t0;
996,992✔
428
                d0 = s2x;
996,992✔
429
                return s2x;
996,992✔
430
            }
431

432
            case 1:
4,957,091✔
433
            {
434
                const int32_t t1 = subInt32(s2x, d1), t2 = subInt32(t1, d2), nd0 = addInt32(d0, s2x);
4,957,091✔
435
                d4 = subInt32(t2, d3);
4,957,091✔
436
                d3 = t2;
4,957,091✔
437
                d2 = t1;
4,957,091✔
438
                d1 = s2x;
4,957,091✔
439
                d0 = nd0;
4,957,091✔
440
                return nd0;
4,957,091✔
441
            }
442

443
            case 2:
1,670,484✔
444
            {
445
                const int32_t nd1 = addInt32(d1, s2x), nd0 = addInt32(d0, nd1), t = subInt32(s2x, d2);
1,670,484✔
446
                d4 = subInt32(t, d3);
1,670,484✔
447
                d3 = t;
1,670,484✔
448
                d2 = s2x;
1,670,484✔
449
                d1 = nd1;
1,670,484✔
450
                d0 = nd0;
1,670,484✔
451
                return nd0;
1,670,484✔
452
            }
453

454
            case 3:
703,878✔
455
            {
456
                const int32_t nd2 = addInt32(d2, s2x), nd1 = addInt32(d1, nd2), nd0 = addInt32(d0, nd1);
703,878✔
457
                d4 = subInt32(s2x, d3);
703,878✔
458
                d3 = s2x;
703,878✔
459
                d2 = nd2;
703,878✔
460
                d1 = nd1;
703,878✔
461
                d0 = nd0;
703,878✔
462
                return nd0;
703,878✔
463
            }
464

465
            case 4:
1,305,007✔
466
            {
467
                const int32_t nd3 = addInt32(d3, s2x), nd2 = addInt32(d2, nd3), nd1 = addInt32(d1, nd2), nd0 = addInt32(d0, nd1);
1,305,007✔
468
                d4 = s2x;
1,305,007✔
469
                d3 = nd3;
1,305,007✔
470
                d2 = nd2;
1,305,007✔
471
                d1 = nd1;
1,305,007✔
472
                d0 = nd0;
1,305,007✔
473
                return nd0;
1,305,007✔
474
            }
475

476
            default: return d0; // LCOV_EXCL_LINE idx comes from a 0..4 minimum search.
477
        }
478
    }
479

480
    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,633,452✔
481
    {
482
        auto mag = [](int32_t v) -> uint32_t
48,167,260✔
483
        {
484
            return (uint32_t)(v ^ (v >> 31));
48,167,260✔
485
        };
486

487
        a0 = a0 + mag(d0) - (a0 >> 5);
9,633,452✔
488
        a1 = a1 + mag(d1) - (a1 >> 5);
9,633,452✔
489
        a2 = a2 + mag(d2) - (a2 >> 5);
9,633,452✔
490
        a3 = a3 + mag(d3) - (a3 >> 5);
9,633,452✔
491
        a4 = a4 + mag(d4) - (a4 >> 5);
9,633,452✔
492
    }
9,633,452✔
493
};
494

495
class VLBitWriter
496
{
497
public:
498
    void writeBit(bool bit)
48,966,512✔
499
    {
500
        if (bit)
48,966,512✔
501
            currentWord |= (uint32_t)1u << (31 - bitCount);
25,595,687✔
502
        if (++bitCount == 32)
48,966,512✔
503
            flushWord();
1,530,173✔
504
    }
48,966,512✔
505

506
    void writeBits(uint32_t value, int count)
5,696,662✔
507
    {
508
        for (int i = count - 1; i >= 0; --i)
39,784,738✔
509
            writeBit(((value >> i) & 1u) != 0);
34,088,076✔
510
    }
5,696,662✔
511

512
    std::vector<uint8_t> finish()
70✔
513
    {
514
        if (bitCount > 0)
70✔
515
            flushWord();
64✔
516

517
        return bytes;
70✔
518
    }
519

520
private:
521
    std::vector<uint8_t> bytes;
522
    uint32_t currentWord = 0;
523
    int bitCount = 0;
524

525
    void flushWord()
1,530,237✔
526
    {
527
        bytes.push_back((uint8_t)(currentWord >> 24));
1,530,237✔
528
        bytes.push_back((uint8_t)(currentWord >> 16));
1,530,237✔
529
        bytes.push_back((uint8_t)(currentWord >> 8));
1,530,237✔
530
        bytes.push_back((uint8_t)currentWord);
1,530,237✔
531
        currentWord = 0;
1,530,237✔
532
        bitCount = 0;
1,530,237✔
533
    }
1,530,237✔
534
};
535

536
class VLDWOPCompressor
537
{
538
public:
539
    VLChannelState ch[2];
540

541
    std::vector<uint8_t> compressMono(const int32_t* in, uint32_t frameCount)
62✔
542
    {
543
        reset();
62✔
544

545
        VLBitWriter bw;
62✔
546
        int32_t d[5] = {};
62✔
547
        uint32_t a[5] = {2560, 2560, 2560, 2560, 2560};
62✔
548
        uint32_t j = 2;
62✔
549
        int32_t rbits = 0;
62✔
550

551
        for (uint32_t f = 0; f < frameCount; ++f)
5,818,040✔
552
            encodeChannel(in[f] * 2, d, a, j, rbits, bw);
5,817,978✔
553

554
        return bw.finish();
124✔
555
    }
62✔
556

557
    std::vector<uint8_t> compressStereo(const int32_t* in, uint32_t frameCount)
8✔
558
    {
559
        reset();
8✔
560

561
        VLBitWriter bw;
8✔
562
        int32_t d[2][5] = {};
8✔
563
        uint32_t a[2][5] = {{2560, 2560, 2560, 2560, 2560}, {2560, 2560, 2560, 2560, 2560}};
8✔
564
        uint32_t j[2] = {2, 2};
8✔
565
        int32_t rbits[2] = {0, 0};
8✔
566

567
        for (uint32_t f = 0; f < frameCount; ++f)
563,302✔
568
        {
569
            const int32_t left2x = in[(size_t)f * 2] * 2;
563,294✔
570
            const int32_t right2x = in[(size_t)f * 2 + 1] * 2;
563,294✔
571
            encodeChannel(left2x, d[0], a[0], j[0], rbits[0], bw);
563,294✔
572
            encodeChannel(right2x - left2x, d[1], a[1], j[1], rbits[1], bw);
563,294✔
573
        }
574

575
        return bw.finish();
16✔
576
    }
8✔
577

578
private:
579
    void reset()
70✔
580
    {
581
        for (auto& c : ch)
210✔
582
        {
583
            std::fill(c.deltas, c.deltas + 5, 0);
140✔
584
            std::fill(c.averages, c.averages + 5, 2560);
140✔
585
        }
586
    }
70✔
587

588
    static uint32_t toCodeValue(int32_t signed2x)
6,944,566✔
589
    {
590
        if (signed2x >= 0)
6,944,566✔
591
            return (uint32_t)signed2x;
4,138,344✔
592

593
        return (uint32_t)(-signed2x - 1);
2,806,222✔
594
    }
595

596
    static int minAverageIndex(const uint32_t a[5])
6,944,566✔
597
    {
598
        int idx = 0;
6,944,566✔
599

600
        for (int i = 1; i < 5; ++i)
34,722,830✔
601
        {
602
            if (a[i] < a[idx])
27,778,264✔
603
                idx = i;
10,210,749✔
604
        }
605

606
        return idx;
6,944,566✔
607
    }
608

609
    static int32_t predictorResidual(int idx, int32_t sample2x, const int32_t d[5])
6,944,566✔
610
    {
611
        switch (idx)
6,944,566✔
612
        {
613
            case 0: return sample2x;
1,376,780✔
614

615
            case 1: return sample2x - d[0];
3,124,212✔
616

617
            case 2: return sample2x - d[0] - d[1];
1,074,646✔
618

619
            case 3: return sample2x - d[0] - d[1] - d[2];
538,371✔
620

621
            case 4: return sample2x - d[0] - d[1] - d[2] - d[3];
830,557✔
622

623
            default: return sample2x; // LCOV_EXCL_LINE idx comes from a 0..4 minimum search.
624
        }
625
    }
626

627
    static int32_t applyPredictor(int idx, int32_t s2x, int32_t d[5])
6,944,566✔
628
    {
629
        switch (idx)
6,944,566✔
630
        {
631
            case 0:
1,376,780✔
632
            {
633
                const int32_t t0 = s2x - d[0], t1 = t0 - d[1], t2 = t1 - d[2];
1,376,780✔
634
                d[4] = t2 - d[3];
1,376,780✔
635
                d[3] = t2;
1,376,780✔
636
                d[2] = t1;
1,376,780✔
637
                d[1] = t0;
1,376,780✔
638
                d[0] = s2x;
1,376,780✔
639
                return s2x;
1,376,780✔
640
            }
641

642
            case 1:
3,124,212✔
643
            {
644
                const int32_t t1 = s2x - d[1], t2 = t1 - d[2], nd0 = d[0] + s2x;
3,124,212✔
645
                d[4] = t2 - d[3];
3,124,212✔
646
                d[3] = t2;
3,124,212✔
647
                d[2] = t1;
3,124,212✔
648
                d[1] = s2x;
3,124,212✔
649
                d[0] = nd0;
3,124,212✔
650
                return nd0;
3,124,212✔
651
            }
652

653
            case 2:
1,074,646✔
654
            {
655
                const int32_t nd1 = d[1] + s2x, nd0 = d[0] + nd1, t = s2x - d[2];
1,074,646✔
656
                d[4] = t - d[3];
1,074,646✔
657
                d[3] = t;
1,074,646✔
658
                d[2] = s2x;
1,074,646✔
659
                d[1] = nd1;
1,074,646✔
660
                d[0] = nd0;
1,074,646✔
661
                return nd0;
1,074,646✔
662
            }
663

664
            case 3:
538,371✔
665
            {
666
                const int32_t nd2 = d[2] + s2x, nd1 = d[1] + nd2, nd0 = d[0] + nd1;
538,371✔
667
                d[4] = s2x - d[3];
538,371✔
668
                d[3] = s2x;
538,371✔
669
                d[2] = nd2;
538,371✔
670
                d[1] = nd1;
538,371✔
671
                d[0] = nd0;
538,371✔
672
                return nd0;
538,371✔
673
            }
674

675
            case 4:
830,557✔
676
            {
677
                const int32_t nd3 = d[3] + s2x, nd2 = d[2] + nd3, nd1 = d[1] + nd2, nd0 = d[0] + nd1;
830,557✔
678
                d[4] = s2x;
830,557✔
679
                d[3] = nd3;
830,557✔
680
                d[2] = nd2;
830,557✔
681
                d[1] = nd1;
830,557✔
682
                d[0] = nd0;
830,557✔
683
                return nd0;
830,557✔
684
            }
685

686
            default: return d[0]; // LCOV_EXCL_LINE idx comes from a 0..4 minimum search.
687
        }
688
    }
689

690
    static void updateAverages(uint32_t a[5], const int32_t d[5])
6,944,566✔
691
    {
692
        auto mag = [](int32_t v) -> uint32_t
34,722,830✔
693
        {
694
            return (uint32_t)(v ^ (v >> 31));
34,722,830✔
695
        };
696

697
        for (int i = 0; i < 5; ++i)
41,667,396✔
698
        {
699
            a[i] = a[i] + mag(d[i]) - (a[i] >> 5);
34,722,830✔
700
        }
701
    }
6,944,566✔
702

703
    static void adjustJRbits(uint32_t step, uint32_t& j, int32_t& rbits)
12,118,751✔
704
    {
705
        if (step < j)
12,118,751✔
706
        {
707
            for (uint32_t jt = j >> 1; step < jt; jt >>= 1)
12,348,227✔
708
            {
709
                j = jt;
419,491✔
710
                --rbits;
419,491✔
711
            }
712
        }
713
        else
714
        {
715
            while (step >= j)
444,882✔
716
            {
717
                const uint32_t prev = j;
254,867✔
718

719
                j <<= 1;
254,867✔
720
                ++rbits;
254,867✔
721

722
                if (j <= prev)
254,867✔
723
                {
724
                    j = prev;
×
725
                    break;
×
726
                }
727
            }
728
        }
729
    }
12,118,751✔
730

731
    static bool encodeRemainder(uint32_t raw, uint32_t step, uint32_t j, int32_t rbits, uint32_t& remBits, bool& hasExtra, bool& extraBit)
12,118,751✔
732
    {
733
        if (rbits < 0 || rbits > 31)
12,118,751✔
734
            return false;
×
735

736
        const uint32_t limit = rbits == 31 ? 0x80000000u : (1u << rbits);
12,118,751✔
737
        const int32_t threshSigned = (int32_t)j - (int32_t)step;
12,118,751✔
738
        if (threshSigned < 0)
12,118,751✔
739
            return false;
×
740

741
        const uint32_t thresh = (uint32_t)threshSigned;
12,118,751✔
742

743
        if (raw < thresh)
12,118,751✔
744
        {
745
            if (raw >= limit)
4,184,881✔
746
                return false;
×
747

748
            remBits = raw;
4,184,881✔
749
            hasExtra = false;
4,184,881✔
750
            extraBit = false;
4,184,881✔
751
            return true;
4,184,881✔
752
        }
753

754
        const uint32_t folded = raw + thresh;
7,933,870✔
755
        remBits = folded >> 1;
7,933,870✔
756
        hasExtra = true;
7,933,870✔
757
        extraBit = (folded & 1u) != 0;
7,933,870✔
758
        return remBits >= thresh && remBits < limit;
7,933,870✔
759
    }
760

761
    static void writeCodeValue(uint32_t codeVal, uint32_t baseStep, uint32_t& j, int32_t& rbits, VLBitWriter& bw)
6,944,566✔
762
    {
763
        uint32_t prefixSum = 0;
6,944,566✔
764
        uint32_t step = baseStep;
6,944,566✔
765
        int zerosWin = 7;
6,944,566✔
766

767
        for (uint32_t zeros = 0; zeros < 0x100000; ++zeros)
12,118,751✔
768
        {
769
            if (codeVal >= prefixSum)
12,118,751✔
770
            {
771
                uint32_t trialJ = j;
12,118,751✔
772
                int32_t trialRbits = rbits;
12,118,751✔
773
                adjustJRbits(step, trialJ, trialRbits);
12,118,751✔
774

775
                uint32_t remBits = 0;
12,118,751✔
776
                bool hasExtra = false;
12,118,751✔
777
                bool extraBit = false;
12,118,751✔
778
                if (encodeRemainder(codeVal - prefixSum, step, trialJ, trialRbits, remBits, hasExtra, extraBit))
12,118,751✔
779
                {
780
                    for (uint32_t i = 0; i < zeros; ++i)
12,118,751✔
781
                        bw.writeBit(false);
5,174,185✔
782

783
                    bw.writeBit(true);
6,944,566✔
784

785
                    if (trialRbits > 0)
6,944,566✔
786
                        bw.writeBits(remBits, trialRbits);
5,696,662✔
787

788
                    if (hasExtra)
6,944,566✔
789
                        bw.writeBit(extraBit);
2,759,685✔
790

791
                    j = trialJ;
6,944,566✔
792
                    rbits = trialRbits;
6,944,566✔
793
                    return;
6,944,566✔
794
                }
795
            }
796

797
            if (baseStep == 0)
5,174,185✔
798
                break;
×
799

800
            if (UINT32_MAX - prefixSum < step)
5,174,185✔
801
                break;
×
802

803
            prefixSum += step;
5,174,185✔
804
            if (prefixSum > codeVal && step != 0)
5,174,185✔
805
                break;
×
806

807
            if (--zerosWin == 0)
5,174,185✔
808
            {
809
                step <<= 2;
31,319✔
810
                zerosWin = 7;
31,319✔
811
            }
812
        }
813

814
        bw.writeBit(true); // LCOV_EXCL_LINE emergency fallback; valid sample residuals encode above.
815
    }
816

817
    static void encodeChannel(int32_t sample2x, int32_t d[5], uint32_t a[5], uint32_t& j, int32_t& rbits, VLBitWriter& bw)
6,944,566✔
818
    {
819
        const int idx = minAverageIndex(a);
6,944,566✔
820
        const uint32_t baseStep = ((a[idx] * 3u) + 36u) >> 7;
6,944,566✔
821
        const int32_t residual = predictorResidual(idx, sample2x, d);
6,944,566✔
822
        writeCodeValue(toCodeValue(residual), baseStep, j, rbits, bw);
6,944,566✔
823
        applyPredictor(idx, residual, d);
6,944,566✔
824
        updateAverages(a, d);
6,944,566✔
825
    }
6,944,566✔
826
};
827

828
class VLIFFWriter
829
{
830
public:
831
    std::vector<uint8_t> data;
832

833
    size_t beginChunk(const char id[4])
2,723✔
834
    {
835
        const size_t start = data.size();
2,723✔
836
        data.insert(data.end(), id, id + 4);
2,723✔
837
        put32(0);
2,723✔
838
        return start;
2,723✔
839
    }
840

841
    size_t beginCat(const char type[4])
210✔
842
    {
843
        const size_t start = beginChunk("CAT ");
210✔
844
        data.insert(data.end(), type, type + 4);
210✔
845
        return start;
210✔
846
    }
847

848
    void endChunk(size_t start)
2,723✔
849
    {
850
        const size_t payloadStart = start + 8;
2,723✔
851
        const uint32_t size = (uint32_t)(data.size() - payloadStart);
2,723✔
852

853
        data[start + 4] = (uint8_t)(size >> 24);
2,723✔
854
        data[start + 5] = (uint8_t)(size >> 16);
2,723✔
855
        data[start + 6] = (uint8_t)(size >> 8);
2,723✔
856
        data[start + 7] = (uint8_t)size;
2,723✔
857

858
        if (data.size() & 1u)
2,723✔
859
            data.push_back(0);
2,295✔
860
    }
2,723✔
861

862
    void put8(uint8_t v)
3,485✔
863
    {
864
        data.push_back(v);
3,485✔
865
    }
3,485✔
866

867
    void put16(uint16_t v)
2,365✔
868
    {
869
        data.push_back((uint8_t)(v >> 8));
2,365✔
870
        data.push_back((uint8_t)v);
2,365✔
871
    }
2,365✔
872

873
    void put32(uint32_t v)
7,143✔
874
    {
875
        data.push_back((uint8_t)(v >> 24));
7,143✔
876
        data.push_back((uint8_t)(v >> 16));
7,143✔
877
        data.push_back((uint8_t)(v >> 8));
7,143✔
878
        data.push_back((uint8_t)v);
7,143✔
879
    }
7,143✔
880

881
    void putBytes(const uint8_t* p, size_t n)
314✔
882
    {
883
        data.insert(data.end(), p, p + n);
314✔
884
    }
314✔
885
};
886

887
/* -----------------------------------------------------------------------
888
   REX2 file parser + DWOP decompressor wrapper
889
   ----------------------------------------------------------------------- */
890

891
enum VLSliceState
892
{
893
    kSliceNormal = 1,
894
    kSliceMuted = 2,
895
    kSliceLocked = 3
896
};
897

898
struct VLSliceEntry
899
{
900
    uint32_t ppq_pos = 0;
901
    uint32_t sample_length = 0;
902
    uint32_t rendered_length = 0;
903
    uint32_t sample_start = 0;
904
    uint32_t render_loop_start = 0;
905
    uint32_t render_loop_end = 0;
906
    float render_loop_volume_compensation = 1.0f;
907
    uint16_t points = 0x7fff;
908
    uint8_t selected_flag = 0;
909
    VLSliceState state = kSliceNormal;
910
    bool synthetic_leading = false;
911
    bool marker = false;
912
};
913

914
int32_t sliceFlags(const VLSliceEntry& s)
690✔
915
{
916
    int32_t flags = 0;
690✔
917
    if (s.state == kSliceMuted)
690✔
918
        flags |= VL_SLICE_FLAG_MUTED;
2✔
919
    else if (s.state == kSliceLocked)
688✔
920
        flags |= VL_SLICE_FLAG_LOCKED;
6✔
921
    if (s.selected_flag)
690✔
922
        flags |= VL_SLICE_FLAG_SELECTED;
30✔
923
    if (s.marker)
690✔
924
        flags |= VL_SLICE_FLAG_MARKER;
×
925
    if (s.synthetic_leading)
690✔
926
        flags |= VL_SLICE_FLAG_SYNTHETIC;
6✔
927
    return flags;
690✔
928
}
929

930
VLError applySliceFlags(VLSliceEntry& s, int32_t flags, int32_t analysisPoints)
6✔
931
{
932
    static constexpr int32_t kKnownFlags = VL_SLICE_FLAG_MUTED | VL_SLICE_FLAG_LOCKED | VL_SLICE_FLAG_SELECTED | VL_SLICE_FLAG_MARKER | VL_SLICE_FLAG_SYNTHETIC;
933

934
    if (flags & ~kKnownFlags)
6✔
935
        return VL_ERROR_INVALID_ARG;
1✔
936

937
    if ((flags & VL_SLICE_FLAG_MUTED) && (flags & VL_SLICE_FLAG_LOCKED))
5✔
938
        return VL_ERROR_INVALID_ARG;
1✔
939

940
    if (flags & VL_SLICE_FLAG_LOCKED)
4✔
941
        s.state = kSliceLocked;
1✔
942
    else if (flags & VL_SLICE_FLAG_MUTED)
3✔
943
        s.state = kSliceMuted;
1✔
944
    else
945
        s.state = kSliceNormal;
2✔
946

947
    s.selected_flag = (flags & VL_SLICE_FLAG_SELECTED) ? 1 : 0;
4✔
948

949
    if (analysisPoints >= 0)
4✔
950
    {
951
        if (analysisPoints > 0x7fff)
3✔
952
            return VL_ERROR_INVALID_ARG;
1✔
953
        s.points = (uint16_t)analysisPoints;
2✔
954
    }
955

956
    return VL_OK;
3✔
957
}
958

959
template <typename T> class VLHeapArray
960
{
961
public:
962
    VLHeapArray() = default;
212✔
963

964
    VLHeapArray(const VLHeapArray&) = delete;
965
    VLHeapArray& operator=(const VLHeapArray&) = delete;
966

967
    VLHeapArray(VLHeapArray&& o) noexcept : ptr_(std::move(o.ptr_)), size_(o.size_)
968
    {
969
        o.size_ = 0;
970
    }
971

972
    VLHeapArray& operator=(VLHeapArray&& o) noexcept
973
    {
974
        ptr_ = std::move(o.ptr_);
975
        size_ = o.size_;
976
        o.size_ = 0;
977
        return *this;
978
    }
979

980
    [[nodiscard]] bool assign(std::size_t n, T val) noexcept
90✔
981
    {
982
        if (n == 0)
90✔
983
        {
984
            ptr_.reset();
×
985
            size_ = 0;
×
986
            return true;
×
987
        }
988

989
        T* raw = new (std::nothrow) T[n];
90✔
990
        if (!raw)
90✔
991
            return false;
×
992

993
        std::fill_n(raw, n, val);
90✔
994
        ptr_.reset(raw);
90✔
995
        size_ = n;
90✔
996

997
        return true;
90✔
998
    }
999

1000
    [[nodiscard]] bool resize(std::size_t n, T val) noexcept
32✔
1001
    {
1002
        if (n == size_)
32✔
1003
            return true;
×
1004

1005
        if (n == 0)
32✔
1006
        {
1007
            ptr_.reset();
×
1008
            size_ = 0;
×
1009
            return true;
×
1010
        }
1011

1012
        T* raw = new (std::nothrow) T[n];
32✔
1013
        if (!raw)
32✔
1014
            return false;
×
1015

1016
        const std::size_t keep = std::min(size_, n);
32✔
1017
        if (keep)
32✔
1018
            std::memcpy(raw, ptr_.get(), keep * sizeof(T));
6✔
1019

1020
        if (n > size_)
32✔
1021
            std::fill_n(raw + size_, n - size_, val);
32✔
1022

1023
        ptr_.reset(raw);
32✔
1024
        size_ = n;
32✔
1025
        return true;
32✔
1026
    }
1027

1028
    T& operator[](std::size_t i) noexcept
3,389,339✔
1029
    {
1030
        assert(i < size_);
3,389,339✔
1031
        return ptr_[i];
3,389,339✔
1032
    }
1033

1034
    const T& operator[](std::size_t i) const noexcept
7,251,546✔
1035
    {
1036
        assert(i < size_);
7,251,546✔
1037
        return ptr_[i];
7,251,546✔
1038
    }
1039

1040
    T* data() noexcept
70✔
1041
    {
1042
        return ptr_.get();
70✔
1043
    }
1044

1045
    const T* data() const noexcept
1046
    {
1047
        return ptr_.get();
1048
    }
1049

1050
    std::size_t size() const noexcept
7,252,918✔
1051
    {
1052
        return size_;
7,252,918✔
1053
    }
1054

1055
    bool empty() const noexcept
1,395✔
1056
    {
1057
        return size_ == 0;
1,395✔
1058
    }
1059

1060
    static constexpr std::size_t max_size() noexcept
651✔
1061
    {
1062
        return std::numeric_limits<std::size_t>::max() / sizeof(T);
651✔
1063
    }
1064

1065
private:
1066
    std::unique_ptr<T[]> ptr_;
1067
    std::size_t size_ = 0;
1068
};
1069

1070
class VLFileImpl
1071
{
1072
public:
1073
    VLFileInfo info = {};
1074
    VLCreatorInfo creator = {};
1075
    std::vector<VLSliceEntry> slices;
1076
    std::vector<uint8_t> fileData;
1077
    VLHeapArray<int32_t> pcm;
1078
    uint32_t totalFrames = 0;
1079
    uint32_t loopStart = 0;
1080
    uint32_t loopEnd = 0;
1081
    bool transientEnabled = true;
1082
    uint16_t transientAttack = 0x15;
1083
    uint16_t transientDecay = 0x3ff;
1084
    uint16_t transientStretch = 0x28;
1085
    uint16_t processingGain = 1000;
1086
    uint8_t analysisSensitivity = 0;
1087
    uint16_t gateSensitivity = 0;
1088
    bool silenceSelected = false;
1089
    bool headerValid = true;
1090
    VLError loadError = VL_OK;
1091

1092
    static constexpr int32_t kREXPPQ = 15360;
1093

1094
    bool loadFromBuffer(const char* buf, size_t size)
185✔
1095
    {
1096
        fileData.assign((const uint8_t*)buf, (const uint8_t*)buf + size);
185✔
1097

1098
        info.channels = 1;
185✔
1099
        info.sample_rate = 44100;
185✔
1100
        info.slice_count = 0;
185✔
1101
        info.tempo = 120000;
185✔
1102
        info.original_tempo = 120000;
185✔
1103
        info.ppq_length = 61440;
185✔
1104
        info.time_sig_num = 4;
185✔
1105
        info.time_sig_den = 4;
185✔
1106
        info.bit_depth = 16;
185✔
1107
        info.total_frames = 0;
185✔
1108
        info.loop_start = 0;
185✔
1109
        info.loop_end = 0;
185✔
1110
        info.processing_gain = processingGain;
185✔
1111
        info.transient_enabled = transientEnabled ? 1 : 0;
185✔
1112
        info.transient_attack = transientAttack;
185✔
1113
        info.transient_decay = transientDecay;
185✔
1114
        info.transient_stretch = transientStretch;
185✔
1115
        info.silence_selected = silenceSelected ? 1 : 0;
185✔
1116
        analysisSensitivity = 0;
185✔
1117
        gateSensitivity = 0;
185✔
1118
        headerValid = true;
185✔
1119
        loadError = VL_OK;
185✔
1120

1121
        std::memset(&creator, 0, sizeof(creator));
185✔
1122

1123
        if (fileData.size() < 12)
185✔
1124
            return fail(VL_ERROR_INVALID_SIZE);
1✔
1125

1126
        if (fileData[0] == 'F' && fileData[1] == 'O' && fileData[2] == 'R' && fileData[3] == 'M' && fileData[8] == 'A' && fileData[9] == 'I' &&
232✔
1127
            fileData[10] == 'F' && fileData[11] == 'F')
232✔
1128
        {
1129
            return loadLegacyAIFF();
48✔
1130
        }
1131

1132
        if (fileData[0] != 'C' || fileData[1] != 'A' || fileData[2] != 'T' || fileData[3] != ' ')
136✔
1133
            return fail(VL_ERROR_FILE_CORRUPT);
18✔
1134

1135
        size_t dwopOffset = 0, dwopSize = 0;
118✔
1136
        bool hasDWOP = false;
118✔
1137

1138
        parseIFF(8 + 4, fileData.size(), dwopOffset, dwopSize, hasDWOP);
118✔
1139
        if (loadError != VL_OK)
118✔
1140
            return false;
26✔
1141

1142
        finalizeSlices();
92✔
1143

1144
        if (!headerValid)
92✔
1145
            return false;
×
1146

1147
        if (!hasDWOP || dwopSize == 0)
92✔
1148
            return fail(VL_ERROR_FILE_CORRUPT);
4✔
1149

1150
        if (dwopOffset + dwopSize > fileData.size())
88✔
1151
            return fail(VL_ERROR_INVALID_SIZE);
×
1152

1153
        if (totalFrames == 0)
88✔
1154
            return fail(VL_ERROR_INVALID_SIZE);
8✔
1155

1156
        // 3600 * 192000 = 691,200,000 — exactly 1 hour at 192 kHz max sample rate.
1157
        static constexpr uint32_t kMaxTotalFrames = 3600u * 192000u;
1158
        if (totalFrames > kMaxTotalFrames)
80✔
1159
            return fail(VL_ERROR_INVALID_SIZE);
×
1160

1161
        const size_t pcmElements = (size_t)totalFrames * (size_t)info.channels;
80✔
1162

1163
        if (!pcm.assign(pcmElements, 0))
80✔
1164
            return fail(VL_ERROR_OUT_OF_MEMORY);
×
1165

1166
        VLDWOPDecompressor dec;
80✔
1167
        dec.init(&fileData[dwopOffset], dwopSize);
80✔
1168

1169
        uint32_t done = 0;
80✔
1170
        bool ok = true;
80✔
1171
        while (done < totalFrames && ok)
160✔
1172
        {
1173
            const uint32_t chunk = std::min<uint32_t>(0x100000, totalFrames - done);
80✔
1174

1175
            if (info.channels == 1)
80✔
1176
                ok = dec.decompressMono(chunk, &pcm[done], info.bit_depth);
66✔
1177
            else
1178
                ok = dec.decompressStereo(chunk, &pcm[(size_t)done * 2], info.bit_depth);
14✔
1179

1180
            done += chunk;
80✔
1181
        }
1182

1183
        if (!ok)
80✔
1184
            return fail(VL_ERROR_FILE_CORRUPT);
×
1185

1186
        finalizeRenderedLengths();
80✔
1187
        return true;
80✔
1188
    }
80✔
1189

1190
private:
1191
    bool fail(VLError error)
97✔
1192
    {
1193
        if (loadError == VL_OK)
97✔
1194
            loadError = error;
95✔
1195
        return false;
97✔
1196
    }
1197

1198
    static uint32_t be32(const uint8_t* p)
58,517✔
1199
    {
1200
        return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | p[3];
58,517✔
1201
    }
1202

1203
    static uint16_t be16(const uint8_t* p)
770,898✔
1204
    {
1205
        return (uint16_t)(((uint16_t)p[0] << 8) | p[1]);
770,898✔
1206
    }
1207

1208
    static int32_t readAIFFExtendedRate(const uint8_t* p)
48✔
1209
    {
1210
        const uint16_t expon = be16(p);
48✔
1211
        uint64_t mant = 0;
48✔
1212
        for (int i = 0; i < 8; ++i)
432✔
1213
            mant = (mant << 8) | p[2 + i];
384✔
1214

1215
        if (expon == 0 || mant == 0)
48✔
1216
            return 0;
4✔
1217

1218
        const int sign = (expon & 0x8000u) ? -1 : 1;
44✔
1219
        const int exp = (int)(expon & 0x7fffu) - 16383;
44✔
1220
        const long double value = (long double)sign * (long double)mant * std::ldexp((long double)1.0, exp - 63);
44✔
1221
        if (value <= 0.0L || value > (long double)std::numeric_limits<int32_t>::max())
44✔
1222
            return 0;
×
1223

1224
        return (int32_t)std::lround((double)value);
44✔
1225
    }
1226

1227
    static int32_t readSignedSampleBE(const uint8_t* p, uint16_t bits)
964,919✔
1228
    {
1229
        switch (bits)
964,919✔
1230
        {
1231
            case 8: return (int8_t)p[0] << 8;
91,528✔
1232
            case 16: return (int16_t)be16(p);
766,609✔
1233
            case 24:
61,018✔
1234
            {
1235
                int32_t v = ((int32_t)p[0] << 16) | ((int32_t)p[1] << 8) | (int32_t)p[2];
61,018✔
1236
                if (v & 0x800000)
61,018✔
1237
                    v |= ~0xffffff;
32,554✔
1238
                return v;
61,018✔
1239
            }
1240
            case 32:
45,764✔
1241
            {
1242
                int32_t v = (int32_t)be32(p);
45,764✔
1243
                return v >> 16;
45,764✔
1244
            }
1245
            default: return 0;
×
1246
        }
1247
    }
1248

1249
    bool parseLegacyTempo(const uint8_t* d, uint32_t sz, bool appIsReCy)
44✔
1250
    {
1251
        const uint32_t tempoOffset = appIsReCy ? 14u : 16u;
44✔
1252
        if (sz < tempoOffset + 4u)
44✔
1253
            return false;
4✔
1254

1255
        const uint32_t v = be32(d + tempoOffset);
40✔
1256
        if (v < 20000u || v > 450000u)
40✔
1257
            return false;
4✔
1258

1259
        info.tempo = (int32_t)v;
36✔
1260
        info.original_tempo = (int32_t)v;
36✔
1261
        return true;
36✔
1262
    }
1263

1264
    static uint16_t legacyReCycleFilterPoints(uint16_t sensitivity)
9✔
1265
    {
1266
        const uint32_t sens = std::min<uint32_t>(sensitivity, 1000u);
9✔
1267
        const uint32_t visibleRange = (sens * 0x7fffu + 999u) / 1000u;
9✔
1268
        return (uint16_t)(0x7fffu - visibleRange);
9✔
1269
    }
1270

1271
    bool parseLegacyReCycleSlices(const uint8_t* d, uint32_t sz, std::vector<VLSliceEntry>& out)
41✔
1272
    {
1273
        if (sz < 4u + 0xa0u)
41✔
1274
            return false;
32✔
1275

1276
        const uint8_t* binary = d + 4;
9✔
1277
        const uint32_t binarySize = sz - 4u;
9✔
1278
        if (be32(binary) != 0xd1daded0u)
9✔
1279
            return false;
×
1280

1281
        const uint16_t sensitivity = be16(binary + 0x14);
9✔
1282
        const uint16_t filterPoints = legacyReCycleFilterPoints(sensitivity);
9✔
1283
        const uint16_t storedCount = be16(binary + 0x9e);
9✔
1284
        if (storedCount == 0 || storedCount > 1000)
9✔
1285
            return false;
×
1286

1287
        if (binarySize < 0xa0u + (uint32_t)storedCount * 8u)
9✔
1288
            return false;
×
1289

1290
        std::vector<VLSliceEntry> parsed;
9✔
1291
        parsed.reserve(storedCount);
9✔
1292
        for (uint16_t i = 0; i < storedCount; ++i)
186✔
1293
        {
1294
            const uint8_t* rec = binary + 0xa0u + (uint32_t)i * 8u;
177✔
1295
            const uint8_t state = rec[0] & 0x7f;
177✔
1296
            const bool selected = (rec[0] & 0x80) != 0;
177✔
1297
            const uint32_t start = ((uint32_t)rec[1] << 24) | ((uint32_t)rec[2] << 16) | ((uint32_t)rec[3] << 8) | rec[4];
177✔
1298
            const uint16_t points = be16(rec + 6);
177✔
1299

1300
            if (!selected && (state != 0 || points <= filterPoints))
177✔
1301
                continue;
107✔
1302

1303
            VLSliceEntry s;
70✔
1304
            s.sample_start = start;
70✔
1305
            s.sample_length = 1;
70✔
1306
            s.points = points;
70✔
1307
            s.selected_flag = selected ? 1 : 0;
70✔
1308
            if (state == 1)
70✔
1309
                s.state = kSliceLocked;
7✔
1310
            else if (state == 2)
63✔
1311
                s.state = kSliceMuted;
×
1312
            else
1313
                s.state = kSliceNormal;
63✔
1314
            parsed.push_back(s);
70✔
1315
        }
1316

1317
        if (parsed.empty())
9✔
1318
            return false;
2✔
1319

1320
        out = std::move(parsed);
7✔
1321
        return true;
7✔
1322
    }
9✔
1323

1324
    bool parseLegacyREXSlices(const uint8_t* d, uint32_t sz, std::vector<VLSliceEntry>& out, uint32_t& ppqLength,
3✔
1325
                              uint32_t& exportedFrameCount)
1326
    {
1327
        if (sz < 4u + 0x3f8u)
3✔
1328
            return false;
×
1329

1330
        const uint8_t* binary = d + 4;
3✔
1331
        const uint32_t binarySize = sz - 4u;
3✔
1332
        if (be32(binary) != 0xd1d1d1dau)
3✔
1333
            return false;
×
1334

1335
        const uint32_t storedPpqLength = be32(binary + 6);
3✔
1336
        if (storedPpqLength == 0 || storedPpqLength > std::numeric_limits<uint32_t>::max() / 16u)
3✔
1337
            return false;
×
1338

1339
        const uint16_t storedCount = be16(binary + 0x0a);
3✔
1340
        if (storedCount == 0 || storedCount > 1000)
3✔
1341
            return false;
×
1342

1343
        if (binarySize < 0x3f8u + (uint32_t)storedCount * 12u)
3✔
1344
            return false;
×
1345

1346
        std::vector<VLSliceEntry> parsed;
3✔
1347
        std::vector<uint32_t> ppqPositions;
3✔
1348
        std::vector<uint32_t> sourceLengths;
3✔
1349
        parsed.reserve(storedCount);
3✔
1350
        ppqPositions.reserve(storedCount);
3✔
1351
        sourceLengths.reserve(storedCount);
3✔
1352
        for (uint16_t i = 0; i < storedCount; ++i)
33✔
1353
        {
1354
            const uint8_t* rec = binary + 0x3f8u + (uint32_t)i * 12u;
30✔
1355
            const uint32_t start = be32(rec);
30✔
1356
            const uint32_t length = be32(rec + 4);
30✔
1357
            const uint32_t ppq16 = be32(rec + 8);
30✔
1358
            if (ppq16 > storedPpqLength || ppq16 > std::numeric_limits<uint32_t>::max() / 16u)
30✔
1359
                return false;
×
1360
            if (length == 0)
30✔
1361
                continue;
×
1362

1363
            VLSliceEntry s;
30✔
1364
            s.ppq_pos = ppq16 * 16u;
30✔
1365
            s.sample_start = start;
30✔
1366
            s.sample_length = length;
30✔
1367
            s.points = 0x7fff;
30✔
1368
            s.selected_flag = i == 0 ? 1 : 0;
30✔
1369
            s.state = kSliceNormal;
30✔
1370
            parsed.push_back(s);
30✔
1371
            ppqPositions.push_back(ppq16);
30✔
1372
            sourceLengths.push_back(length);
30✔
1373
        }
1374

1375
        if (parsed.empty())
3✔
1376
            return false;
×
1377

1378
        double samplesPerPpq = 0.0;
3✔
1379
        for (size_t i = 0; i < ppqPositions.size(); ++i)
33✔
1380
        {
1381
            uint32_t nextPpq = storedPpqLength;
30✔
1382
            for (size_t j = i + 1; j < ppqPositions.size(); ++j)
30✔
1383
            {
1384
                if (ppqPositions[j] != ppqPositions[i])
27✔
1385
                {
1386
                    nextPpq = ppqPositions[j];
27✔
1387
                    break;
27✔
1388
                }
1389
            }
1390

1391
            if (nextPpq <= ppqPositions[i])
30✔
1392
                continue;
×
1393

1394
            const uint32_t delta = nextPpq - ppqPositions[i];
30✔
1395
            samplesPerPpq = std::max(samplesPerPpq, (double)sourceLengths[i] / (double)delta);
30✔
1396
        }
1397

1398
        if (samplesPerPpq <= 0.0)
3✔
1399
            return false;
×
1400

1401
        ppqLength = storedPpqLength * 16u;
3✔
1402
        exportedFrameCount = (uint32_t)(samplesPerPpq * (double)storedPpqLength);
3✔
1403
        out = std::move(parsed);
3✔
1404
        return true;
3✔
1405
    }
3✔
1406

1407
    bool loadLegacyAIFF()
48✔
1408
    {
1409
        uint16_t channels = 0;
48✔
1410
        uint32_t frameCount = 0;
48✔
1411
        uint16_t bits = 0;
48✔
1412
        int32_t sampleRate = 0;
48✔
1413
        bool haveCOMM = false;
48✔
1414
        bool haveSSND = false;
48✔
1415
        size_t ssndOffset = 0;
48✔
1416
        size_t ssndSize = 0;
48✔
1417
        uint32_t markerLoopStart = 0;
48✔
1418
        uint32_t markerLoopEnd = 0;
48✔
1419
        bool haveLoopStart = false;
48✔
1420
        bool haveLoopEnd = false;
48✔
1421
        bool sawLegacyApp = false;
48✔
1422
        bool sawLegacyTempo = false;
48✔
1423
        bool legacyLoopLengthSet = true;
48✔
1424
        std::vector<VLSliceEntry> legacySlices;
48✔
1425
        bool legacySlicesHaveExplicitPpq = false;
48✔
1426
        uint32_t legacyRexPpqLength = 0;
48✔
1427
        uint32_t legacyRexExportedFrameCount = 0;
48✔
1428

1429
        const size_t formEnd = std::min(fileData.size(), (size_t)be32(&fileData[4]) + 8u);
48✔
1430
        size_t off = 12;
48✔
1431
        while (off + 8 <= formEnd && off + 8 <= fileData.size())
212✔
1432
        {
1433
            char id[5] = {};
164✔
1434
            std::memcpy(id, &fileData[off], 4);
164✔
1435
            const uint32_t sz = be32(&fileData[off + 4]);
164✔
1436
            const size_t payload = off + 8;
164✔
1437
            if (payload + sz > fileData.size())
164✔
1438
                break;
×
1439

1440
            if (std::strcmp(id, "COMM") == 0 && sz >= 18)
164✔
1441
            {
1442
                channels = be16(&fileData[payload]);
48✔
1443
                frameCount = be32(&fileData[payload + 2]);
48✔
1444
                bits = be16(&fileData[payload + 6]);
48✔
1445
                sampleRate = readAIFFExtendedRate(&fileData[payload + 8]);
48✔
1446
                haveCOMM = true;
48✔
1447
            }
1448
            else if (std::strcmp(id, "SSND") == 0 && sz >= 8)
116✔
1449
            {
1450
                const uint32_t dataOffset = be32(&fileData[payload]);
48✔
1451
                const size_t dataStart = payload + 8u + (size_t)dataOffset;
48✔
1452
                if (dataStart <= payload + sz && dataStart <= fileData.size())
48✔
1453
                {
1454
                    ssndOffset = dataStart;
48✔
1455
                    ssndSize = (payload + sz) - dataStart;
48✔
1456
                    haveSSND = true;
48✔
1457
                }
1458
            }
48✔
1459
            else if (std::strcmp(id, "MARK") == 0 && sz >= 2)
68✔
1460
            {
1461
                uint32_t pos = 2;
12✔
1462
                const uint16_t count = be16(&fileData[payload]);
12✔
1463
                for (uint16_t i = 0; i < count && pos + 7 <= sz; ++i)
36✔
1464
                {
1465
                    const uint32_t markerPos = be32(&fileData[payload + pos + 2]);
24✔
1466
                    const uint8_t nameLen = fileData[payload + pos + 6];
24✔
1467
                    pos += 7;
24✔
1468
                    if (pos + nameLen > sz)
24✔
1469
                        break;
×
1470

1471
                    const char* name = (const char*)&fileData[payload + pos];
24✔
1472
                    const bool isLoopStart = nameLen == 10 && std::memcmp(name, "Loop start", 10) == 0;
24✔
1473
                    const bool isLoopEnd = nameLen == 8 && std::memcmp(name, "Loop end", 8) == 0;
24✔
1474
                    if (isLoopStart)
24✔
1475
                    {
1476
                        markerLoopStart = markerPos;
12✔
1477
                        haveLoopStart = true;
12✔
1478
                    }
1479
                    else if (isLoopEnd)
12✔
1480
                    {
1481
                        markerLoopEnd = markerPos;
12✔
1482
                        haveLoopEnd = true;
12✔
1483
                    }
1484

1485
                    pos += nameLen + (((nameLen + 1u) & 1u) ? 1u : 0u);
24✔
1486
                }
1487
            }
12✔
1488
            else if (std::strcmp(id, "APPL") == 0 && sz >= 8)
56✔
1489
            {
1490
                const bool appIsREX = std::memcmp(&fileData[payload], "REX ", 4) == 0;
44✔
1491
                const bool appIsReCy = std::memcmp(&fileData[payload], "ReCy", 4) == 0;
44✔
1492
                if (appIsREX || appIsReCy)
44✔
1493
                {
1494
                    sawLegacyApp = true;
44✔
1495
                    if (appIsReCy)
44✔
1496
                    {
1497
                        legacyLoopLengthSet = sz > 12 && fileData[payload + 12] != 0;
41✔
1498
                        parseLegacyReCycleSlices(&fileData[payload], sz, legacySlices);
41✔
1499
                    }
1500
                    else if (appIsREX)
3✔
1501
                    {
1502
                        legacySlicesHaveExplicitPpq =
1503
                            parseLegacyREXSlices(&fileData[payload], sz, legacySlices, legacyRexPpqLength, legacyRexExportedFrameCount);
3✔
1504
                    }
1505
                    sawLegacyTempo = parseLegacyTempo(&fileData[payload], sz, appIsReCy) || sawLegacyTempo;
44✔
1506
                }
1507
            }
1508

1509
            off = payload + sz;
164✔
1510
            if (off & 1)
164✔
1511
                ++off;
×
1512
        }
1513

1514
        if (!sawLegacyApp)
48✔
1515
            return fail(VL_ERROR_FILE_CORRUPT);
4✔
1516

1517
        if (!sawLegacyTempo)
44✔
1518
            return fail(VL_ERROR_INVALID_TEMPO);
8✔
1519

1520
        if (!legacyLoopLengthSet)
36✔
1521
            return fail(VL_ERROR_ZERO_LOOP_LENGTH);
6✔
1522

1523
        if (!haveCOMM || !haveSSND || channels < 1 || channels > 2 || frameCount == 0 || sampleRate <= 0)
30✔
1524
        {
1525
            if (frameCount == 0)
12✔
1526
                return fail(VL_ERROR_INVALID_SIZE);
4✔
1527
            if (sampleRate <= 0)
8✔
1528
                return fail(VL_ERROR_INVALID_SAMPLE_RATE);
4✔
1529
            return fail(VL_ERROR_FILE_CORRUPT);
4✔
1530
        }
1531

1532
        if (bits != 8 && bits != 16 && bits != 24 && bits != 32)
18✔
1533
            return fail(VL_ERROR_FILE_CORRUPT);
4✔
1534

1535
        const size_t bytesPerSample = (size_t)((bits + 7u) / 8u);
14✔
1536
        const size_t frameBytes = bytesPerSample * (size_t)channels;
14✔
1537
        if (frameBytes == 0)
14✔
1538
            return fail(VL_ERROR_INVALID_SIZE);
×
1539

1540
        const uint32_t availableFrames = (uint32_t)std::min<size_t>(frameCount, ssndSize / frameBytes);
14✔
1541
        if (availableFrames == 0)
14✔
1542
            return fail(VL_ERROR_INVALID_SIZE);
4✔
1543

1544
        if (!pcm.assign((size_t)availableFrames * channels, 0))
10✔
1545
            return fail(VL_ERROR_OUT_OF_MEMORY);
×
1546

1547
        const uint8_t* src = &fileData[ssndOffset];
10✔
1548
        for (uint32_t frame = 0; frame < availableFrames; ++frame)
964,929✔
1549
        {
1550
            for (uint16_t ch = 0; ch < channels; ++ch)
1,929,838✔
1551
            {
1552
                const size_t sample = ((size_t)frame * channels + ch) * bytesPerSample;
964,919✔
1553
                pcm[(size_t)frame * channels + ch] = readSignedSampleBE(src + sample, bits);
964,919✔
1554
            }
1555
        }
1556

1557
        totalFrames = availableFrames;
10✔
1558
        loopStart = haveLoopStart && markerLoopStart < availableFrames ? markerLoopStart : 0;
10✔
1559
        loopEnd = haveLoopEnd && markerLoopEnd > loopStart && markerLoopEnd <= availableFrames ? markerLoopEnd : availableFrames;
10✔
1560

1561
        info.channels = channels;
10✔
1562
        info.sample_rate = sampleRate;
10✔
1563
        info.slice_count = 1;
10✔
1564
        info.ppq_length = legacyRexPpqLength > 0 ? (int32_t)legacyRexPpqLength : kREXPPQ * 4;
10✔
1565
        info.time_sig_num = 4;
10✔
1566
        info.time_sig_den = 4;
10✔
1567
        info.bit_depth = bits;
10✔
1568
        info.total_frames = (int32_t)totalFrames;
10✔
1569
        info.loop_start = (int32_t)loopStart;
10✔
1570
        info.loop_end = (int32_t)loopEnd;
10✔
1571
        info.processing_gain = processingGain;
10✔
1572
        info.transient_enabled = 0;
10✔
1573
        info.transient_attack = 0;
10✔
1574
        info.transient_decay = 1023;
10✔
1575
        info.transient_stretch = 0;
10✔
1576
        info.silence_selected = 0;
10✔
1577
        transientEnabled = false;
10✔
1578
        transientAttack = 0;
10✔
1579
        transientDecay = 1023;
10✔
1580
        transientStretch = 0;
10✔
1581

1582
        if (legacyRexExportedFrameCount > 0 && info.ppq_length > 0)
10✔
1583
        {
1584
            const double beats = (double)info.ppq_length / (double)kREXPPQ;
3✔
1585
            const double bpm = beats * 60.0 * (double)info.sample_rate / (double)legacyRexExportedFrameCount;
3✔
1586
            const int32_t derivedOriginalTempo = (int32_t)std::lround(bpm * 1000.0);
3✔
1587
            if (derivedOriginalTempo > 0)
3✔
1588
                info.original_tempo = derivedOriginalTempo;
3✔
1589
        }
1590

1591
        if (!legacySlices.empty())
10✔
1592
        {
1593
            std::sort(legacySlices.begin(), legacySlices.end(),
10✔
1594
                      [](const VLSliceEntry& a, const VLSliceEntry& b)
180✔
1595
                      {
1596
                          return a.sample_start < b.sample_start;
180✔
1597
                      });
1598
            legacySlices.erase(std::unique(legacySlices.begin(), legacySlices.end(),
10✔
1599
                                           [](const VLSliceEntry& a, const VLSliceEntry& b)
90✔
1600
                                           {
1601
                                               return a.sample_start == b.sample_start;
90✔
1602
                                           }),
1603
                               legacySlices.end());
10✔
1604

1605
            const uint32_t sliceEnd = loopEnd > loopStart ? loopEnd : totalFrames;
10✔
1606
            for (size_t i = 0; i < legacySlices.size(); ++i)
110✔
1607
            {
1608
                const uint32_t next = i + 1 < legacySlices.size() ? legacySlices[i + 1].sample_start : sliceEnd;
100✔
1609
                if (!legacySlicesHaveExplicitPpq)
100✔
1610
                    legacySlices[i].sample_length = next > legacySlices[i].sample_start ? next - legacySlices[i].sample_start : 1u;
70✔
1611
            }
1612

1613
            slices = std::move(legacySlices);
10✔
1614
            if (!legacySlicesHaveExplicitPpq)
10✔
1615
                finalizeSlices();
7✔
1616
            else
1617
                info.slice_count = (int32_t)slices.size();
3✔
1618
            finalizeRenderedLengths();
10✔
1619
        }
1620
        else
1621
        {
1622
            VLSliceEntry s;
×
1623
            s.ppq_pos = 0;
×
1624
            s.sample_start = loopStart;
×
1625
            s.sample_length = std::max<uint32_t>(1u, loopEnd > loopStart ? loopEnd - loopStart : totalFrames - loopStart);
×
1626
            s.rendered_length = s.sample_length;
×
1627
            s.points = 0x7fff;
×
1628
            s.selected_flag = 1;
×
1629
            s.state = kSliceNormal;
×
1630
            slices.push_back(s);
×
1631
            info.slice_count = (int32_t)slices.size();
×
1632
            finalizeRenderedLengths();
×
1633
        }
1634
        return true;
10✔
1635
    }
48✔
1636

1637
    void parseIFF(size_t start, size_t end, size_t& dwopOffset, size_t& dwopSize, bool& hasDWOP)
286✔
1638
    {
1639
        size_t off = start;
286✔
1640
        while (off + 8 < end && off + 8 < fileData.size())
4,726✔
1641
        {
1642
            char id[5] = {};
4,448✔
1643
            std::memcpy(id, &fileData[off], 4);
4,448✔
1644
            off += 4;
4,448✔
1645

1646
            const uint32_t sz = be32(&fileData[off]);
4,448✔
1647
            off += 4;
4,448✔
1648

1649
            if (off + sz > fileData.size())
4,448✔
1650
            {
1651
                fail(VL_ERROR_INVALID_SIZE);
8✔
1652
                break;
8✔
1653
            }
1654

1655
            if (std::strcmp(id, "HEAD") == 0)
4,440✔
1656
                parseHEAD(&fileData[off], sz);
118✔
1657

1658
            else if (std::strcmp(id, "CREI") == 0)
4,322✔
1659
                parseCREI(&fileData[off], sz);
8✔
1660

1661
            else if (std::strcmp(id, "SINF") == 0)
4,314✔
1662
                parseSINF(&fileData[off], sz);
116✔
1663

1664
            else if (std::strcmp(id, "GLOB") == 0)
4,198✔
1665
                parseGLOB(&fileData[off], sz);
118✔
1666

1667
            else if (std::strcmp(id, "TRSH") == 0)
4,080✔
1668
                parseTRSH(&fileData[off], sz);
84✔
1669

1670
            else if (std::strcmp(id, "RECY") == 0)
3,996✔
1671
                parseRECY(&fileData[off], sz);
84✔
1672

1673
            else if (std::strcmp(id, "SLCE") == 0)
3,912✔
1674
                parseSLCE(&fileData[off], sz);
3,459✔
1675

1676
            else if ((std::strcmp(id, "SDAT") == 0 || std::strcmp(id, "DWOP") == 0) && !hasDWOP)
453✔
1677
            {
1678
                dwopOffset = off;
108✔
1679
                dwopSize = sz;
108✔
1680
                hasDWOP = true;
108✔
1681
            }
1682

1683
            else if (std::strcmp(id, "CAT ") == 0 && sz >= 4)
345✔
1684
            {
1685
                parseIFF(off + 4, off + sz, dwopOffset, dwopSize, hasDWOP);
168✔
1686
            }
1687

1688
            off += sz;
4,440✔
1689
            if (off & 1)
4,440✔
1690
                ++off;
3,853✔
1691
        }
1692
    }
286✔
1693

1694
    void parseSINF(const uint8_t* d, uint32_t sz)
116✔
1695
    {
1696
        if (sz < 18)
116✔
1697
            return;
4✔
1698

1699
        info.channels = d[0];
112✔
1700
        const uint8_t bd = d[1];
112✔
1701
        info.sample_rate = (int32_t)be32(d + 2);
112✔
1702
        totalFrames = be32(d + 6);
112✔
1703
        loopStart = be32(d + 10);
112✔
1704
        loopEnd = be32(d + 14);
112✔
1705
        info.total_frames = (int32_t)totalFrames;
112✔
1706
        info.loop_start = (int32_t)loopStart;
112✔
1707
        info.loop_end = (int32_t)loopEnd;
112✔
1708

1709
        switch (bd)
112✔
1710
        {
1711
            case 1: info.bit_depth = 8; break;
1✔
1712

1713
            case 3: info.bit_depth = 16; break;
103✔
1714

1715
            case 5: info.bit_depth = 24; break;
6✔
1716

1717
            case 7: info.bit_depth = 32; break;
1✔
1718

1719
            default: info.bit_depth = 16; break;
1✔
1720
        }
1721

1722
        const uint32_t frames = (loopEnd > loopStart) ? (loopEnd - loopStart) : totalFrames;
112✔
1723
        if (frames > 0 && info.sample_rate > 0 && info.ppq_length > 0)
112✔
1724
        {
1725
            const double beats = (double)info.ppq_length / (double)kREXPPQ;
108✔
1726
            const double bpm = beats * 60.0 * (double)info.sample_rate / (double)frames;
108✔
1727

1728
            const int32_t t = (int32_t)std::lround(bpm * 1000.0);
108✔
1729
            if (t > 0)
108✔
1730
                info.original_tempo = t;
108✔
1731
        }
1732

1733
        if (info.original_tempo == 0)
112✔
1734
            info.original_tempo = info.tempo;
×
1735

1736
        if (info.channels != 1 && info.channels != 2)
112✔
1737
            info.channels = 1;
×
1738
    }
1739

1740
    void parseHEAD(const uint8_t* d, uint32_t sz)
118✔
1741
    {
1742
        if (sz < 6 || be32(d) != 0x490cf18du)
118✔
1743
        {
1744
            headerValid = false;
4✔
1745
            fail(VL_ERROR_FILE_CORRUPT);
4✔
1746
            return;
4✔
1747
        }
1748

1749
        if (d[4] != 0xbc)
114✔
1750
        {
1751
            headerValid = false;
×
1752
            fail(VL_ERROR_FILE_CORRUPT);
×
1753
            return;
×
1754
        }
1755

1756
        if (d[5] > 0x03)
114✔
1757
        {
1758
            headerValid = false;
6✔
1759
            fail(VL_ERROR_FILE_TOO_NEW);
6✔
1760
        }
1761
    }
1762

1763
    void parseGLOB(const uint8_t* d, uint32_t sz)
118✔
1764
    {
1765
        if (sz < 22)
118✔
1766
        {
1767
            fail(VL_ERROR_INVALID_SIZE);
6✔
1768
            return;
6✔
1769
        }
1770

1771
        info.slice_count = (int32_t)be32(d);
112✔
1772
        info.time_sig_num = d[7];
112✔
1773
        info.time_sig_den = d[8];
112✔
1774
        analysisSensitivity = d[9];
112✔
1775
        gateSensitivity = be16(d + 10);
112✔
1776
        const uint32_t tempo = be32(d + 16);
112✔
1777
        if (tempo < 20000u || tempo > 450000u)
112✔
1778
            fail(VL_ERROR_INVALID_TEMPO);
4✔
1779
        info.tempo = (int32_t)tempo;
112✔
1780
        info.ppq_length = 61440;
112✔
1781
        processingGain = be16(d + 12);
112✔
1782
        silenceSelected = d[21] != 0;
112✔
1783
        info.processing_gain = processingGain;
112✔
1784
        info.silence_selected = silenceSelected ? 1 : 0;
112✔
1785
    }
1786

1787
    void parseCREI(const uint8_t* d, uint32_t sz)
8✔
1788
    {
1789
        uint32_t off = 0;
8✔
1790
        auto readString = [&](char* dst, size_t dstSize)
40✔
1791
        {
1792
            if (!dst || dstSize == 0)
40✔
1793
                return;
×
1794

1795
            dst[0] = '\0';
40✔
1796
            if (off + 4 > sz)
40✔
1797
                return;
4✔
1798

1799
            const uint32_t n = be32(d + off);
36✔
1800
            off += 4;
36✔
1801
            if (off + n > sz)
36✔
1802
            {
1803
                off = sz;
1✔
1804
                return;
1✔
1805
            }
1806

1807
            const size_t copy = std::min<size_t>(n, dstSize - 1);
35✔
1808
            if (copy)
35✔
1809
                std::memcpy(dst, d + off, copy);
29✔
1810

1811
            dst[copy] = '\0';
35✔
1812
            off += n;
35✔
1813
        };
8✔
1814

1815
        readString(creator.name, sizeof(creator.name));
8✔
1816
        readString(creator.copyright, sizeof(creator.copyright));
8✔
1817
        readString(creator.url, sizeof(creator.url));
8✔
1818
        readString(creator.email, sizeof(creator.email));
8✔
1819
        readString(creator.free_text, sizeof(creator.free_text));
8✔
1820
    }
8✔
1821

1822
    void parseTRSH(const uint8_t* d, uint32_t sz)
84✔
1823
    {
1824
        if (sz < 7)
84✔
1825
            return;
×
1826

1827
        transientEnabled = d[0] != 0;
84✔
1828
        transientAttack = be16(d + 1);
84✔
1829
        transientDecay = be16(d + 3);
84✔
1830
        transientStretch = be16(d + 5);
84✔
1831
        info.transient_enabled = transientEnabled ? 1 : 0;
84✔
1832
        info.transient_attack = transientAttack;
84✔
1833
        info.transient_decay = transientDecay;
84✔
1834
        info.transient_stretch = transientStretch;
84✔
1835
    }
1836

1837
    void parseRECY(const uint8_t* d, uint32_t sz)
84✔
1838
    {
1839
        if (sz < 12)
84✔
1840
            return;
×
1841

1842
        const int32_t t = (int32_t)be32(d + 8);
84✔
1843
        if (t > 0)
84✔
1844
            info.original_tempo = t;
81✔
1845
    }
1846

1847
    void parseSLCE(const uint8_t* d, uint32_t sz)
3,459✔
1848
    {
1849
        static constexpr size_t kMaxSlices = 1024;
1850

1851
        if (slices.size() >= kMaxSlices)
3,459✔
1852
            return;
×
1853

1854
        if (sz < 10)
3,459✔
1855
            return;
×
1856

1857
        VLSliceEntry s;
3,459✔
1858
        s.sample_start = be32(d);
3,459✔
1859
        s.sample_length = be32(d + 4);
3,459✔
1860
        s.points = be16(d + 8);
3,459✔
1861
        s.marker = s.sample_length <= 1;
3,459✔
1862

1863
        const uint8_t flags = sz > 10 ? d[10] : 0;
3,459✔
1864
        s.selected_flag = (flags & 0x04) ? 1 : 0;
3,459✔
1865

1866
        if (flags & 0x02)
3,459✔
1867
            s.state = kSliceLocked;
7✔
1868
        else if (flags & 0x01)
3,452✔
1869
            s.state = kSliceMuted;
1✔
1870
        else
1871
            s.state = kSliceNormal;
3,451✔
1872

1873
        slices.push_back(s);
3,459✔
1874
        info.slice_count = (int32_t)slices.size();
3,459✔
1875
    }
1876

1877
    static uint16_t rex2FilterPoints(uint8_t sensitivity)
770✔
1878
    {
1879
        const uint32_t sens = std::min<uint32_t>(sensitivity, 100u);
770✔
1880
        const uint32_t visibleRange = (sens * 0x7fffu + 99u) / 100u;
770✔
1881
        return (uint16_t)(0x7fffu - visibleRange);
770✔
1882
    }
1883

1884
    bool isVisibleSliceBoundary(const VLSliceEntry& s) const
3,394✔
1885
    {
1886
        if (s.sample_length > 1)
3,394✔
1887
            return true;
2,624✔
1888

1889
        if (s.selected_flag || s.state != kSliceNormal)
770✔
1890
            return true;
×
1891

1892
        return s.points > rex2FilterPoints(analysisSensitivity);
770✔
1893
    }
1894

1895
    uint32_t defaultSliceEnd(uint32_t start) const
2,643✔
1896
    {
1897
        if (loopEnd > loopStart && start < loopEnd)
2,643✔
1898
            return loopEnd;
2,639✔
1899

1900
        return totalFrames;
4✔
1901
    }
1902

1903
    uint32_t gateLengthFrames() const
99✔
1904
    {
1905
        if (gateSensitivity == 0)
99✔
1906
            return 0;
89✔
1907

1908
        const uint32_t sr = info.sample_rate > 0 ? (uint32_t)info.sample_rate : 44100u;
10✔
1909
        uint32_t frames = (uint32_t)(((uint64_t)gateSensitivity * sr + 4500u) / 9000u);
10✔
1910
        frames = std::max(1u, frames);
10✔
1911

1912
        return std::max(1u, ((frames + 64u) / 128u) * 128u);
10✔
1913
    }
1914

1915
    void finalizeSlices()
99✔
1916
    {
1917
        const uint32_t denom = (loopEnd > loopStart) ? (loopEnd - loopStart) : (totalFrames ? totalFrames : 1u);
99✔
1918
        const uint32_t gatedFrames = gateLengthFrames();
99✔
1919

1920
        std::sort(slices.begin(), slices.end(),
99✔
1921
                  [](const VLSliceEntry& a, const VLSliceEntry& b)
22,662✔
1922
                  {
1923
                      return a.sample_start < b.sample_start;
22,662✔
1924
                  });
1925

1926
        std::vector<VLSliceEntry> out;
99✔
1927
        for (auto s : slices)
3,604✔
1928
        {
1929
            if (loopEnd > loopStart && s.sample_start < totalFrames && s.sample_start >= loopEnd)
3,505✔
1930
                continue;
111✔
1931

1932
            if (!isVisibleSliceBoundary(s))
3,394✔
1933
                continue;
751✔
1934

1935
            const uint32_t rel = (s.sample_start > loopStart) ? (s.sample_start - loopStart) : 0;
2,643✔
1936

1937
            s.ppq_pos = (uint32_t)(((uint64_t)rel * info.ppq_length + denom / 2) / denom);
2,643✔
1938
            s.marker = false;
2,643✔
1939

1940
            out.push_back(s);
2,643✔
1941
        }
1942

1943
        for (size_t i = 0; i < out.size(); ++i)
2,742✔
1944
        {
1945
            const uint32_t start = out[i].sample_start;
2,643✔
1946
            uint32_t next = defaultSliceEnd(start);
2,643✔
1947
            for (size_t j = i + 1; j < out.size(); ++j)
2,644✔
1948
            {
1949
                if (out[j].sample_start > start)
2,563✔
1950
                {
1951
                    next = out[j].sample_start;
2,562✔
1952
                    break;
2,562✔
1953
                }
1954
            }
1955

1956
            const uint32_t derived = next > start ? next - start : 1u;
2,643✔
1957
            if (gateSensitivity == 0 || out[i].sample_length <= 1)
2,643✔
1958
            {
1959
                out[i].sample_length = derived;
2,543✔
1960
                if (gateSensitivity != 0 && gatedFrames > 0)
2,543✔
1961
                    out[i].sample_length = std::min(out[i].sample_length, gatedFrames);
×
1962
            }
1963
            else if (derived > 1)
100✔
1964
            {
1965
                out[i].sample_length = std::min(out[i].sample_length, derived);
100✔
1966
            }
1967

1968
            out[i].sample_length = std::max<uint32_t>(1u, out[i].sample_length);
2,643✔
1969
        }
1970

1971
        if (!out.empty() && loopEnd > loopStart && out.front().sample_start > loopStart && out.front().sample_start <= totalFrames)
99✔
1972
        {
1973
            VLSliceEntry leading;
12✔
1974
            leading.ppq_pos = 0;
12✔
1975
            leading.sample_start = loopStart;
12✔
1976
            leading.sample_length = out.front().sample_start - loopStart;
12✔
1977
            leading.points = 0x7fff;
12✔
1978
            leading.selected_flag = 1;
12✔
1979
            leading.state = kSliceNormal;
12✔
1980
            leading.synthetic_leading = true;
12✔
1981

1982
            out.insert(out.begin(), leading);
12✔
1983
        }
1984

1985
        slices.swap(out);
99✔
1986
        info.slice_count = (int32_t)slices.size();
99✔
1987
    }
99✔
1988

1989
    uint32_t sourceEndForSlice(const VLSliceEntry& s) const
9,911✔
1990
    {
1991
        const uint32_t start = s.sample_start;
9,911✔
1992
        if (start >= totalFrames)
9,911✔
1993
            return start;
8✔
1994

1995
        uint32_t frames = std::min(s.sample_length, totalFrames - start);
9,903✔
1996
        if (loopEnd > loopStart && start < loopEnd)
9,903✔
1997
            frames = std::min(frames, loopEnd - start);
9,252✔
1998

1999
        return start + frames;
9,903✔
2000
    }
2001

2002
public:
2003
    struct SegmentLoop
2004
    {
2005
        uint32_t start = 0;
2006
        uint32_t end = 0;
2007
        float volumeCompensation = 1.0f;
2008
    };
2009

2010
    SegmentLoop findSegmentLoop(uint32_t start, uint32_t end) const
5,281✔
2011
    {
2012
        SegmentLoop r{start, end};
5,281✔
2013

2014
        const uint32_t sr = info.sample_rate ? (uint32_t)info.sample_rate : 44100u;
5,281✔
2015
        const uint32_t srch = std::max(1u, (400u * sr) / 44100u);
5,281✔
2016
        const uint32_t mhl = std::max(1u, (20000u * sr) / 44100u);
5,281✔
2017

2018
        if (end <= start || end - start < srch * 3u)
5,281✔
2019
            return r;
2,064✔
2020

2021
        const uint32_t ch = std::max(1, info.channels);
3,217✔
2022
        auto leftAbs = [&](uint32_t f) -> int
2,345,965✔
2023
        {
2024
            const size_t i = (size_t)f * ch;
2,345,965✔
2025
            return i < pcm.size() ? std::abs((int)pcm[i]) : 0;
2,345,965✔
2026
        };
3,217✔
2027

2028
        uint32_t loopEnd = end - srch;
3,217✔
2029
        int peak = -1;
3,217✔
2030
        for (uint32_t i = 0, f = end - srch; i < srch && f > start; ++i, --f)
1,284,617✔
2031
        {
2032
            const int p = leftAbs(f);
1,281,400✔
2033
            if (p > peak)
1,281,400✔
2034
            {
2035
                peak = p;
64,103✔
2036
                loopEnd = f;
64,103✔
2037
            }
2038
        }
2039

2040
        uint32_t hl = std::min((loopEnd - start) / 2u, mhl);
3,217✔
2041
        uint32_t ls = loopEnd - hl;
3,217✔
2042
        uint32_t loopStart = ls;
3,217✔
2043
        int lspeak = -1;
3,217✔
2044
        for (uint32_t i = 0, f = ls; i < srch && f >= start; ++i)
1,067,782✔
2045
        {
2046
            const int p = leftAbs(f);
1,064,565✔
2047
            if (p > lspeak)
1,064,565✔
2048
            {
2049
                lspeak = p;
47,301✔
2050
                loopStart = f;
47,301✔
2051
            }
2052

2053
            if (f == 0)
1,064,565✔
2054
                break;
×
2055

2056
            --f;
1,064,565✔
2057
        }
2058

2059
        r.start = std::clamp(loopStart, start, end - 1u);
3,217✔
2060
        r.end = std::clamp(loopEnd, r.start + 1u, end);
3,217✔
2061

2062
        if (lspeak > 0 && peak > 0)
3,217✔
2063
            r.volumeCompensation = std::min(10.0f, (float)peak / (float)lspeak);
3,091✔
2064

2065
        return r;
3,217✔
2066
    }
2067

2068
private:
2069
    void cacheRenderLoop(VLSliceEntry& s) const
5,281✔
2070
    {
2071
        const uint32_t start = s.sample_start;
5,281✔
2072
        const uint32_t end = sourceEndForSlice(s);
5,281✔
2073
        const SegmentLoop loop = findSegmentLoop(start, end);
5,281✔
2074
        s.render_loop_start = loop.start;
5,281✔
2075
        s.render_loop_end = loop.end;
5,281✔
2076
        s.render_loop_volume_compensation = loop.volumeCompensation;
5,281✔
2077
    }
5,281✔
2078

2079
    uint32_t calcRenderedLength(const VLSliceEntry& s) const
4,630✔
2080
    {
2081
        const uint32_t start = s.sample_start;
4,630✔
2082
        const uint32_t end = sourceEndForSlice(s);
4,630✔
2083

2084
        if (end <= start)
4,630✔
2085
            return 1u;
4✔
2086

2087
        const uint32_t segLen = end - start;
4,626✔
2088
        if (!transientEnabled || transientStretch == 0)
4,626✔
2089
            return segLen;
3,008✔
2090

2091
        const uint32_t loopE = (s.render_loop_end > start && s.render_loop_end <= end) ? s.render_loop_end : end;
1,618✔
2092
        const uint32_t stretchN = (uint32_t)transientStretch + 1u;
1,618✔
2093
        const uint32_t stretchT = (uint32_t)(((uint64_t)(loopE - start) * stretchN) / 100u);
1,618✔
2094

2095
        return std::max(1u, segLen + stretchT);
1,618✔
2096
    }
2097

2098
public:
2099
    void cacheSliceRender(VLSliceEntry& s) const
4,630✔
2100
    {
2101
        cacheRenderLoop(s);
4,630✔
2102
        s.rendered_length = calcRenderedLength(s);
4,630✔
2103
    }
4,630✔
2104

2105
    void cacheSliceLoop(VLSliceEntry& s) const
651✔
2106
    {
2107
        cacheRenderLoop(s);
651✔
2108
    }
651✔
2109

2110
    void finalizeRenderedLengths()
160✔
2111
    {
2112
        for (auto& s : slices)
4,790✔
2113
            cacheSliceRender(s);
4,630✔
2114
    }
160✔
2115
};
2116

2117
int32_t storageBitDepth(int32_t bitDepth)
7,330,016✔
2118
{
2119
    return bitDepth == 24 ? 24 : 16;
7,330,016✔
2120
}
2121

2122
int32_t floatToPCM(float s, int32_t bitDepth)
2,424,340✔
2123
{
2124
    if (s > 1.0f)
2,424,340✔
2125
        s = 1.0f;
64✔
2126

2127
    if (s < -1.0f)
2,424,340✔
2128
        s = -1.0f;
64✔
2129

2130
    if (storageBitDepth(bitDepth) == 24)
2,424,340✔
2131
    {
2132
        const float scaled = s >= 0.0f ? s * 8388607.0f : s * 8388608.0f;
113,479✔
2133
        return (int32_t)std::lround(scaled);
113,479✔
2134
    }
2135

2136
    const float scaled = s >= 0.0f ? s * 32767.0f : s * 32768.0f;
2,310,861✔
2137
    return (int32_t)std::lround(scaled);
2,310,861✔
2138
}
2139

2140
float pcmToFloat(int32_t sample, int32_t bitDepth)
4,905,581✔
2141
{
2142
    return storageBitDepth(bitDepth) == 24 ? (float)sample / 8388608.0f : (float)sample / 32768.0f;
4,905,581✔
2143
}
2144

2145
uint8_t bitDepthCode(int32_t bitDepth)
70✔
2146
{
2147
    switch (bitDepth)
70✔
2148
    {
2149
        case 8: return 1; // LCOV_EXCL_LINE unsupported authored depth is normalized before writing.
2150

2151
        case 16: return 3;
67✔
2152

2153
        case 24: return 5;
3✔
2154

2155
        case 32: return 7; // LCOV_EXCL_LINE unsupported authored depth is normalized before writing.
2156

2157
        default: return 3; // LCOV_EXCL_LINE unsupported authored depth is normalized before writing.
2158
    }
2159
}
2160

2161
bool hasCreatorInfo(const VLCreatorInfo& c)
70✔
2162
{
2163
    return c.name[0] || c.copyright[0] || c.url[0] || c.email[0] || c.free_text[0];
70✔
2164
}
2165

2166
void writeCStringChunkString(VLIFFWriter& w, const char* s)
40✔
2167
{
2168
    const size_t n = s ? std::min<size_t>(std::strlen(s), 255) : 0;
40✔
2169

2170
    w.put32((uint32_t)n);
40✔
2171

2172
    if (n)
40✔
2173
        w.putBytes((const uint8_t*)s, n);
34✔
2174
}
40✔
2175

2176
void writeSimpleChunk(VLIFFWriter& w, const char id[4], const std::vector<uint8_t>& payload)
70✔
2177
{
2178
    const size_t c = w.beginChunk(id);
70✔
2179

2180
    if (!payload.empty())
70✔
2181
        w.putBytes(payload.data(), payload.size());
70✔
2182

2183
    w.endChunk(c);
70✔
2184
}
70✔
2185

2186
uint32_t calcSampleStartFromPPQ(const VLFileImpl& impl, uint32_t ppq)
654✔
2187
{
2188
    const VLFileInfo& info = impl.info;
654✔
2189
    if (info.ppq_length > 0 && info.loop_end > info.loop_start)
654✔
2190
    {
2191
        const uint32_t denom = (uint32_t)(info.loop_end - info.loop_start);
641✔
2192
        return (uint32_t)info.loop_start + (uint32_t)(((uint64_t)ppq * denom + (uint32_t)info.ppq_length / 2u) / (uint32_t)info.ppq_length);
641✔
2193
    }
2194

2195
    const uint32_t tempo = info.tempo > 0 ? (uint32_t)info.tempo : 120000u;
13✔
2196
    const uint32_t sr = info.sample_rate > 0 ? (uint32_t)info.sample_rate : 44100u;
13✔
2197
    return (uint32_t)(((uint64_t)ppq * sr * 60000u + (uint64_t)tempo * VLFileImpl::kREXPPQ / 2u) / ((uint64_t)tempo * VLFileImpl::kREXPPQ));
13✔
2198
}
2199

2200
void normaliseInfoForSave(VLFileImpl& impl)
70✔
2201
{
2202
    impl.info.channels = std::clamp(impl.info.channels, 1, 2);
70✔
2203

2204
    if (impl.info.sample_rate <= 0)
70✔
2205
        impl.info.sample_rate = 44100;
×
2206

2207
    if (impl.info.tempo <= 0)
70✔
2208
        impl.info.tempo = 120000;
×
2209

2210
    if (impl.info.original_tempo <= 0)
70✔
2211
        impl.info.original_tempo = impl.info.tempo;
2✔
2212

2213
    if (impl.info.time_sig_num <= 0)
70✔
2214
        impl.info.time_sig_num = 4;
2✔
2215

2216
    if (impl.info.time_sig_den <= 0)
70✔
2217
        impl.info.time_sig_den = 4;
2✔
2218

2219
    impl.info.bit_depth = storageBitDepth(impl.info.bit_depth);
70✔
2220
    impl.info.slice_count = (int32_t)impl.slices.size();
70✔
2221

2222
    impl.totalFrames = (uint32_t)(impl.pcm.size() / (size_t)impl.info.channels);
70✔
2223
    impl.info.total_frames = (int32_t)impl.totalFrames;
70✔
2224

2225
    if (impl.info.loop_start < 0 || (uint32_t)impl.info.loop_start >= impl.totalFrames)
70✔
2226
        impl.info.loop_start = 0;
1✔
2227

2228
    if (impl.info.loop_end <= impl.info.loop_start || (uint32_t)impl.info.loop_end > impl.totalFrames)
70✔
2229
        impl.info.loop_end = (int32_t)impl.totalFrames;
1✔
2230

2231
    impl.loopStart = (uint32_t)std::max(0, impl.info.loop_start);
70✔
2232
    impl.loopEnd = (uint32_t)std::max(impl.info.loop_start, impl.info.loop_end);
70✔
2233

2234
    if (impl.info.ppq_length <= 0)
70✔
2235
    {
2236
        const uint32_t frames = impl.loopEnd > impl.loopStart ? impl.loopEnd - impl.loopStart : impl.totalFrames;
2✔
2237

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

2240
        impl.info.ppq_length = std::max(1, (int32_t)std::lround(beats * VLFileImpl::kREXPPQ));
2✔
2241
    }
2242

2243
    impl.processingGain = (uint16_t)std::clamp(impl.info.processing_gain > 0 ? impl.info.processing_gain : 1000, 0, 1000);
70✔
2244
    impl.transientEnabled = impl.info.transient_enabled != 0;
70✔
2245
    impl.transientAttack = (uint16_t)std::clamp(impl.info.transient_attack, 0, 1023);
70✔
2246
    impl.transientDecay = (uint16_t)std::clamp(impl.info.transient_decay > 0 ? impl.info.transient_decay : 1023, 0, 1023);
70✔
2247
    impl.transientStretch = (uint16_t)std::clamp(impl.info.transient_stretch, 0, 100);
70✔
2248
    impl.silenceSelected = impl.info.silence_selected != 0;
70✔
2249
    impl.finalizeRenderedLengths();
70✔
2250
}
70✔
2251

2252
std::vector<uint8_t> buildREX2File(VLFileImpl& impl)
70✔
2253
{
2254
    normaliseInfoForSave(impl);
70✔
2255

2256
    VLDWOPCompressor comp;
70✔
2257
    const std::vector<uint8_t> sdat =
2258
        impl.info.channels == 2 ? comp.compressStereo(impl.pcm.data(), impl.totalFrames) : comp.compressMono(impl.pcm.data(), impl.totalFrames);
70✔
2259

2260
    VLIFFWriter w;
70✔
2261
    const size_t root = w.beginCat("REX2");
70✔
2262

2263
    {
2264
        const size_t c = w.beginChunk("HEAD");
70✔
2265
        const uint8_t head[] = {0x49, 0x0c, 0xf1, 0x8d, 0xbc, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
70✔
2266
                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
2267
        w.putBytes(head, sizeof(head));
70✔
2268
        w.endChunk(c);
70✔
2269
    }
2270

2271
    if (hasCreatorInfo(impl.creator))
70✔
2272
    {
2273
        const size_t c = w.beginChunk("CREI");
8✔
2274
        writeCStringChunkString(w, impl.creator.name);
8✔
2275
        writeCStringChunkString(w, impl.creator.copyright);
8✔
2276
        writeCStringChunkString(w, impl.creator.url);
8✔
2277
        writeCStringChunkString(w, impl.creator.email);
8✔
2278
        writeCStringChunkString(w, impl.creator.free_text);
8✔
2279
        w.endChunk(c);
8✔
2280
    }
2281

2282
    {
2283
        const size_t c = w.beginChunk("GLOB");
70✔
2284
        w.put32((uint32_t)impl.slices.size());
70✔
2285
        w.put8(0);
70✔
2286
        w.put8(1);
70✔
2287
        w.put8(0);
70✔
2288
        w.put8((uint8_t)impl.info.time_sig_num);
70✔
2289
        w.put8((uint8_t)impl.info.time_sig_den);
70✔
2290
        w.put8(0x4e);
70✔
2291
        w.put8(0);
70✔
2292
        w.put8(0);
70✔
2293
        w.put16(impl.processingGain);
70✔
2294
        w.put16(1);
70✔
2295
        w.put32((uint32_t)impl.info.tempo);
70✔
2296
        w.put8(1);
70✔
2297
        w.put8(impl.silenceSelected ? 1 : 0);
70✔
2298
        w.endChunk(c);
70✔
2299
    }
2300

2301
    {
2302
        const size_t c = w.beginChunk("RECY");
70✔
2303
        w.put8(0xbc);
70✔
2304
        w.put8(0x02);
70✔
2305
        w.put8(0);
70✔
2306
        w.put8(0);
70✔
2307
        w.put8(0);
70✔
2308
        w.put8(1);
70✔
2309
        w.put8(0);
70✔
2310
        w.put8(0);
70✔
2311
        w.put32((uint32_t)impl.info.original_tempo);
70✔
2312
        w.put16(0);
70✔
2313
        w.put8(8);
70✔
2314
        w.endChunk(c);
70✔
2315
    }
2316

2317
    {
2318
        const size_t devl = w.beginCat("DEVL");
70✔
2319
        const size_t trsh = w.beginChunk("TRSH");
70✔
2320
        w.put8(impl.transientEnabled ? 1 : 0);
70✔
2321
        w.put16(impl.transientAttack);
70✔
2322
        w.put16(impl.transientDecay);
70✔
2323
        w.put16(impl.transientStretch);
70✔
2324
        w.endChunk(trsh);
70✔
2325

2326
        const size_t eq = w.beginChunk("EQ  ");
70✔
2327
        const uint8_t eqPayload[] = {0x00, 0x00, 0x0f, 0x00, 0x64, 0x00, 0x00, 0x03, 0xe8, 0x09, 0xc4, 0x00, 0x00, 0x03, 0xe8, 0x4e, 0x20};
70✔
2328
        w.putBytes(eqPayload, sizeof(eqPayload));
70✔
2329
        w.endChunk(eq);
70✔
2330

2331
        const size_t comp = w.beginChunk("COMP");
70✔
2332
        const uint8_t compPayload[] = {0x00, 0x00, 0x4d, 0x00, 0x27, 0x00, 0x42, 0x00, 0x38};
70✔
2333
        w.putBytes(compPayload, sizeof(compPayload));
70✔
2334
        w.endChunk(comp);
70✔
2335

2336
        w.endChunk(devl);
70✔
2337
    }
2338

2339
    {
2340
        const size_t slcl = w.beginCat("SLCL");
70✔
2341
        std::vector<VLSliceEntry> sorted = impl.slices;
70✔
2342
        std::sort(sorted.begin(), sorted.end(),
70✔
2343
                  [](const VLSliceEntry& a, const VLSliceEntry& b)
14,469✔
2344
                  {
2345
                      return a.sample_start < b.sample_start;
14,469✔
2346
                  });
2347
        for (const auto& s : sorted)
2,015✔
2348
        {
2349
            const size_t c = w.beginChunk("SLCE");
1,945✔
2350
            w.put32(s.sample_start);
1,945✔
2351
            w.put32(std::max<uint32_t>(1, s.sample_length));
1,945✔
2352
            w.put16(s.points);
1,945✔
2353
            uint8_t flags = 0;
1,945✔
2354
            if (s.state == kSliceMuted)
1,945✔
2355
                flags |= 0x01;
6✔
2356
            else if (s.state == kSliceLocked)
1,939✔
2357
                flags |= 0x02;
6✔
2358
            if (s.selected_flag)
1,945✔
2359
                flags |= 0x04;
12✔
2360
            w.put8(flags);
1,945✔
2361
            w.endChunk(c);
1,945✔
2362
        }
2363
        w.endChunk(slcl);
70✔
2364
    }
70✔
2365

2366
    {
2367
        const size_t c = w.beginChunk("SINF");
70✔
2368
        w.put8((uint8_t)impl.info.channels);
70✔
2369
        w.put8(bitDepthCode(impl.info.bit_depth));
70✔
2370
        w.put32((uint32_t)impl.info.sample_rate);
70✔
2371
        w.put32(impl.totalFrames);
70✔
2372
        w.put32(impl.loopStart);
70✔
2373
        w.put32(impl.loopEnd);
70✔
2374
        w.endChunk(c);
70✔
2375
    }
2376

2377
    writeSimpleChunk(w, "SDAT", sdat);
70✔
2378
    w.endChunk(root);
70✔
2379
    return w.data;
140✔
2380
}
70✔
2381

2382
} // anonymous namespace
2383

2384
/* -----------------------------------------------------------------------
2385
   VLFile_s  —  the opaque handle
2386
   ----------------------------------------------------------------------- */
2387

2388
struct VLFile_s
2389
{
2390
    VLFileImpl impl;
2391
    int32_t outputSampleRate = 44100;
2392
    bool isNew = false;
2393
    bool dirty = false;
2394
};
2395

2396
/* -----------------------------------------------------------------------
2397
   Utilities
2398
   ----------------------------------------------------------------------- */
2399

2400
namespace
2401
{
2402

2403
int32_t addSliceAtSample(VLFile file, uint32_t sample_start, int32_t ppq_pos, const float* left, const float* right, int32_t frames)
654✔
2404
{
2405
    if (!file)
654✔
2406
        return (int32_t)VL_ERROR_INVALID_HANDLE;
×
2407

2408
    if (!left || frames <= 0 || ppq_pos < 0)
654✔
2409
        return (int32_t)VL_ERROR_INVALID_ARG;
2✔
2410

2411
    const int32_t channels = std::clamp(file->impl.info.channels, 1, 2);
652✔
2412
    if (channels == 2 && !right)
652✔
2413
        return (int32_t)VL_ERROR_INVALID_ARG;
1✔
2414

2415
    const uint32_t end = sample_start + (uint32_t)frames;
651✔
2416
    const uint32_t declaredFrames = file->impl.info.total_frames > 0 ? (uint32_t)file->impl.info.total_frames : 0u;
651✔
2417
    const uint32_t requiredFrames = std::max(end, declaredFrames);
651✔
2418
    const size_t required = (size_t)requiredFrames * (size_t)channels;
651✔
2419

2420
    if (required > file->impl.pcm.max_size())
651✔
2421
        return (int32_t)VL_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE max_size is not reachable in tests.
2422

2423
    if (file->impl.pcm.size() < required)
651✔
2424
    {
2425
        if (!file->impl.pcm.resize(required, 0))
32✔
2426
            return (int32_t)VL_ERROR_OUT_OF_MEMORY;
×
2427
    }
2428

2429
    for (int32_t f = 0; f < frames; ++f)
2,163,795✔
2430
    {
2431
        const size_t dst = ((size_t)sample_start + (size_t)f) * (size_t)channels;
2,163,144✔
2432
        file->impl.pcm[dst] = floatToPCM(left[f], file->impl.info.bit_depth);
2,163,144✔
2433
        if (channels == 2)
2,163,144✔
2434
            file->impl.pcm[dst + 1] = floatToPCM(right[f], file->impl.info.bit_depth);
261,196✔
2435
    }
2436

2437
    VLSliceEntry s;
651✔
2438
    s.ppq_pos = (uint32_t)ppq_pos;
651✔
2439
    s.sample_start = sample_start;
651✔
2440
    s.sample_length = (uint32_t)frames;
651✔
2441
    s.rendered_length = (uint32_t)frames;
651✔
2442
    s.points = 0x7fff;
651✔
2443
    s.selected_flag = 0;
651✔
2444
    s.state = kSliceNormal;
651✔
2445

2446
    file->impl.totalFrames = (uint32_t)(file->impl.pcm.size() / (size_t)channels);
651✔
2447
    file->impl.info.total_frames = (int32_t)file->impl.totalFrames;
651✔
2448
    file->impl.cacheSliceLoop(s);
651✔
2449

2450
    if (file->impl.slices.size() == file->impl.slices.max_size())
651✔
2451
        return (int32_t)VL_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE max_size is not reachable in tests.
2452
    file->impl.slices.push_back(s);
651✔
2453

2454
    file->impl.info.slice_count = (int32_t)file->impl.slices.size();
651✔
2455
    if (file->impl.info.loop_end <= file->impl.info.loop_start)
651✔
2456
        file->impl.info.loop_end = file->impl.info.total_frames;
9✔
2457
    file->dirty = true;
651✔
2458

2459
    return (int32_t)file->impl.slices.size() - 1;
651✔
2460
}
2461

2462
} // anonymous namespace
2463

2464
/* -----------------------------------------------------------------------
2465
   Open / close
2466
   ----------------------------------------------------------------------- */
2467

2468
VLFile vl_open(const char* path, VLError* err)
101✔
2469
{
2470
    auto set = [&](VLError e)
2✔
2471
    {
2472
        if (err)
2✔
2473
            *err = e;
2✔
2474
    };
103✔
2475

2476
    if (!path)
101✔
2477
    {
2478
        set(VL_ERROR_INVALID_ARG);
1✔
2479
        return nullptr;
1✔
2480
    }
2481

2482
    std::ifstream f(path, std::ios::binary | std::ios::ate);
100✔
2483
    if (!f)
100✔
2484
    {
2485
        set(VL_ERROR_FILE_NOT_FOUND);
1✔
2486
        return nullptr;
1✔
2487
    }
2488

2489
    const std::streamsize sz = f.tellg();
99✔
2490
    f.seekg(0);
99✔
2491

2492
    std::vector<char> buf((size_t)sz);
99✔
2493
    if (!f.read(buf.data(), sz))
99✔
2494
    {
2495
        set(VL_ERROR_FILE_CORRUPT);
×
2496
        return nullptr;
×
2497
    }
2498

2499
    return vl_open_from_memory(buf.data(), (size_t)sz, err);
99✔
2500
}
100✔
2501

2502
VLFile vl_open_from_memory(const void* data, size_t size, VLError* err)
186✔
2503
{
2504
    auto set = [&](VLError e)
186✔
2505
    {
2506
        if (err)
186✔
2507
            *err = e;
186✔
2508
    };
372✔
2509

2510
    if (!data || size == 0)
186✔
2511
    {
2512
        set(VL_ERROR_INVALID_ARG);
1✔
2513
        return nullptr;
1✔
2514
    }
2515

2516
    VLFile_s* h = new (std::nothrow) VLFile_s();
185✔
2517
    if (!h)
185✔
2518
    {
2519
        set(VL_ERROR_OUT_OF_MEMORY);
×
2520
        return nullptr;
×
2521
    }
2522

2523
    if (!h->impl.loadFromBuffer((const char*)data, size))
185✔
2524
    {
2525
        const VLError loadError = h->impl.loadError != VL_OK ? h->impl.loadError : VL_ERROR_FILE_CORRUPT;
95✔
2526
        delete h;
95✔
2527
        set(loadError);
95✔
2528
        return nullptr;
95✔
2529
    }
2530

2531
    h->outputSampleRate = h->impl.info.sample_rate ? h->impl.info.sample_rate : 44100;
90✔
2532
    set(VL_OK);
90✔
2533
    return h;
90✔
2534
}
2535

2536
VLFile vl_create_new(int32_t channels, int32_t sample_rate, int32_t tempo, VLError* err)
43✔
2537
{
2538
    auto set = [&](VLError e)
43✔
2539
    {
2540
        if (err)
43✔
2541
            *err = e;
27✔
2542
    };
86✔
2543

2544
    if (channels != 1 && channels != 2)
43✔
2545
    {
2546
        set(VL_ERROR_INVALID_ARG);
4✔
2547
        return nullptr;
4✔
2548
    }
2549

2550
    if (sample_rate < 8000 || sample_rate > 192000)
39✔
2551
    {
2552
        set(VL_ERROR_INVALID_SAMPLE_RATE);
8✔
2553
        return nullptr;
8✔
2554
    }
2555

2556
    if (tempo <= 0)
31✔
2557
    {
2558
        set(VL_ERROR_INVALID_TEMPO);
4✔
2559
        return nullptr;
4✔
2560
    }
2561

2562
    VLFile_s* h = new (std::nothrow) VLFile_s();
27✔
2563
    if (!h)
27✔
2564
    {
2565
        set(VL_ERROR_OUT_OF_MEMORY);
×
2566
        return nullptr;
×
2567
    }
2568

2569
    h->isNew = true;
27✔
2570
    h->outputSampleRate = sample_rate;
27✔
2571

2572
    h->impl.info.channels = channels;
27✔
2573
    h->impl.info.sample_rate = sample_rate;
27✔
2574
    h->impl.info.slice_count = 0;
27✔
2575
    h->impl.info.tempo = tempo;
27✔
2576
    h->impl.info.original_tempo = tempo;
27✔
2577
    h->impl.info.ppq_length = VLFileImpl::kREXPPQ * 4;
27✔
2578
    h->impl.info.time_sig_num = 4;
27✔
2579
    h->impl.info.time_sig_den = 4;
27✔
2580
    h->impl.info.bit_depth = 16;
27✔
2581
    h->impl.info.total_frames = 0;
27✔
2582
    h->impl.info.loop_start = 0;
27✔
2583
    h->impl.info.loop_end = 0;
27✔
2584
    h->impl.info.processing_gain = 1000;
27✔
2585
    h->impl.info.transient_enabled = 1;
27✔
2586
    h->impl.info.transient_attack = 0;
27✔
2587
    h->impl.info.transient_decay = 1023;
27✔
2588
    h->impl.info.transient_stretch = 0;
27✔
2589
    h->impl.info.silence_selected = 0;
27✔
2590
    h->impl.processingGain = 1000;
27✔
2591
    h->impl.transientEnabled = true;
27✔
2592
    h->impl.transientAttack = 0;
27✔
2593
    h->impl.transientDecay = 1023;
27✔
2594
    h->impl.transientStretch = 0;
27✔
2595
    h->impl.silenceSelected = false;
27✔
2596

2597
    set(VL_OK);
27✔
2598
    return h;
27✔
2599
}
2600

2601
void vl_close(VLFile file)
177✔
2602
{
2603
    delete file;
177✔
2604
}
177✔
2605

2606
/* -----------------------------------------------------------------------
2607
   Read: metadata
2608
   ----------------------------------------------------------------------- */
2609

2610
VLError vl_get_info(VLFile file, VLFileInfo* out)
100✔
2611
{
2612
    if (!file)
100✔
2613
        return VL_ERROR_INVALID_HANDLE;
1✔
2614

2615
    if (!out)
99✔
2616
        return VL_ERROR_INVALID_ARG;
18✔
2617

2618
    *out = file->impl.info;
81✔
2619
    return VL_OK;
81✔
2620
}
2621

2622
VLError vl_get_creator_info(VLFile file, VLCreatorInfo* out)
39✔
2623
{
2624
    if (!file)
39✔
2625
        return VL_ERROR_INVALID_HANDLE;
1✔
2626

2627
    if (!out)
38✔
2628
        return VL_ERROR_INVALID_ARG;
18✔
2629

2630
    const VLCreatorInfo& src = file->impl.creator;
20✔
2631
    if (!src.name[0] && !src.copyright[0] && !src.url[0] && !src.email[0] && !src.free_text[0])
20✔
2632
        return VL_ERROR_NO_CREATOR_INFO;
18✔
2633

2634
    *out = src;
2✔
2635
    return VL_OK;
2✔
2636
}
2637

2638
/* -----------------------------------------------------------------------
2639
   Read: slice enumeration
2640
   ----------------------------------------------------------------------- */
2641

2642
VLError vl_get_slice_info(VLFile file, int32_t index, VLSliceInfo* out)
763✔
2643
{
2644
    if (!file)
763✔
2645
        return VL_ERROR_INVALID_HANDLE;
1✔
2646

2647
    if (!out)
762✔
2648
        return VL_ERROR_INVALID_ARG;
36✔
2649

2650
    if (index < 0 || (size_t)index >= file->impl.slices.size())
726✔
2651
        return VL_ERROR_INVALID_SLICE;
36✔
2652

2653
    const VLSliceEntry& s = file->impl.slices[(size_t)index];
690✔
2654
    out->ppq_pos = (int32_t)s.ppq_pos;
690✔
2655
    out->sample_length = (int32_t)s.sample_length;
690✔
2656
    out->sample_start = (int32_t)s.sample_start;
690✔
2657
    out->analysis_points = (int32_t)s.points;
690✔
2658
    out->flags = sliceFlags(s);
690✔
2659
    return VL_OK;
690✔
2660
}
2661

2662
VLError vl_set_slice_info(VLFile file, int32_t index, int32_t flags, int32_t analysis_points)
43✔
2663
{
2664
    if (!file)
43✔
2665
        return VL_ERROR_INVALID_HANDLE;
1✔
2666

2667
    if (index < 0 || (size_t)index >= file->impl.slices.size())
42✔
2668
        return VL_ERROR_INVALID_SLICE;
36✔
2669

2670
    VLError err = applySliceFlags(file->impl.slices[(size_t)index], flags, analysis_points);
6✔
2671
    if (err != VL_OK)
6✔
2672
        return err;
3✔
2673

2674
    file->dirty = true;
3✔
2675
    return VL_OK;
3✔
2676
}
2677

2678
/* -----------------------------------------------------------------------
2679
   Read: sample extraction
2680
   ----------------------------------------------------------------------- */
2681

2682
VLError vl_set_output_sample_rate(VLFile file, int32_t rate)
55✔
2683
{
2684
    if (!file)
55✔
2685
        return VL_ERROR_INVALID_HANDLE;
1✔
2686

2687
    if (rate < 8000 || rate > 192000)
54✔
2688
        return VL_ERROR_INVALID_SAMPLE_RATE;
18✔
2689

2690
    if (rate != file->impl.info.sample_rate)
36✔
2691
        return VL_ERROR_NOT_IMPLEMENTED;
18✔
2692

2693
    file->outputSampleRate = rate;
18✔
2694
    return VL_OK;
18✔
2695
}
2696

2697
int32_t vl_get_slice_frame_count(VLFile file, int32_t index)
1,374✔
2698
{
2699
    if (!file)
1,374✔
2700
        return (int32_t)VL_ERROR_INVALID_HANDLE;
1✔
2701

2702
    if (index < 0 || (size_t)index >= file->impl.slices.size())
1,373✔
2703
        return (int32_t)VL_ERROR_INVALID_SLICE;
38✔
2704

2705
    return (int32_t)file->impl.slices[(size_t)index].rendered_length;
1,335✔
2706
}
2707

2708
VLError vl_decode_slice(VLFile file, int32_t index, float* left, float* right, int32_t capacity, int32_t* frames_out)
1,360✔
2709
{
2710
    if (!file)
1,360✔
2711
        return VL_ERROR_INVALID_HANDLE;
1✔
2712

2713
    if (!left)
1,359✔
2714
        return VL_ERROR_INVALID_ARG;
16✔
2715

2716
    if (index < 0 || (size_t)index >= file->impl.slices.size())
1,343✔
2717
        return VL_ERROR_INVALID_SLICE;
32✔
2718

2719
    const VLSliceEntry& s = file->impl.slices[(size_t)index];
1,311✔
2720
    const int32_t needed = (int32_t)s.rendered_length;
1,311✔
2721
    if (capacity < needed)
1,311✔
2722
        return VL_ERROR_BUFFER_TOO_SMALL;
16✔
2723

2724
    const auto& pcm = file->impl.pcm;
1,295✔
2725
    if (pcm.empty())
1,295✔
2726
        return VL_ERROR_FILE_CORRUPT;
×
2727

2728
    const uint32_t requestedFrames = (uint32_t)needed;
1,295✔
2729
    uint32_t sourceStart = s.sample_start;
1,295✔
2730
    if (sourceStart >= file->impl.totalFrames)
1,295✔
2731
    {
2732
        std::fill(left, left + needed, 0.f);
1✔
2733

2734
        if (right)
1✔
2735
            std::fill(right, right + needed, 0.f);
1✔
2736

2737
        if (frames_out)
1✔
2738
            *frames_out = needed;
1✔
2739

2740
        return VL_OK;
1✔
2741
    }
2742

2743
    uint32_t sourceFrames = s.sample_length;
1,294✔
2744
    if (file->impl.loopEnd > file->impl.loopStart && sourceStart < file->impl.loopEnd)
1,294✔
2745
        sourceFrames = std::min<uint32_t>(sourceFrames, file->impl.loopEnd - sourceStart);
1,293✔
2746
    sourceFrames = std::min<uint32_t>(sourceFrames, file->impl.totalFrames - sourceStart);
1,294✔
2747

2748
    const uint32_t sourceEnd = sourceStart + sourceFrames;
1,294✔
2749
    const VLFileImpl::SegmentLoop segmentLoop{
2750
        s.render_loop_start,
1,294✔
2751
        s.render_loop_end,
1,294✔
2752
        s.render_loop_volume_compensation,
1,294✔
2753
    };
1,294✔
2754
    const int32_t ch = std::max(1, file->impl.info.channels);
1,294✔
2755
    const float samplerGain = (float)file->impl.processingGain * 0.000833333354f;
1,294✔
2756

2757
    uint32_t samplePos = std::min(sourceStart + 2u, sourceEnd);
1,294✔
2758
    int loopPhase = 0; // 0: forward source, 1: forward loop, 2: backward loop
1,294✔
2759
    bool stretchPhase = false;
1,294✔
2760
    float stretchEnv = 1.0f;
1,294✔
2761
    const uint32_t stretchFrameCount =
2762
        std::max<uint32_t>(1u, sourceEnd - segmentLoop.end + (requestedFrames > sourceFrames ? requestedFrames - sourceFrames : 0u));
1,294✔
2763
    const float stretchEnvDec = 1.0f / (float)stretchFrameCount;
1,294✔
2764
    float loopLevelComp = 1.0f;
1,294✔
2765
    const float loopLevelCompInc =
1,294✔
2766
        (segmentLoop.end > segmentLoop.start) ? (1.0f - segmentLoop.volumeCompensation) / (float)(segmentLoop.end - segmentLoop.start) : 0.0f;
1,294✔
2767

2768
    for (uint32_t f = 0; f < requestedFrames; ++f)
4,322,692✔
2769
    {
2770
        const size_t src = (size_t)samplePos * (size_t)ch;
4,321,398✔
2771
        float l = 0.f;
4,321,398✔
2772
        float r = 0.f;
4,321,398✔
2773

2774
        if (src < pcm.size())
4,321,398✔
2775
        {
2776
            const float level = samplerGain * stretchEnv * loopLevelComp;
4,321,398✔
2777
            l = pcmToFloat(pcm[src], file->impl.info.bit_depth) * level;
4,321,398✔
2778
            r = (ch >= 2 && src + 1 < pcm.size()) ? pcmToFloat(pcm[src + 1], file->impl.info.bit_depth) * level : l;
4,321,398✔
2779
        }
2780

2781
        left[f] = l;
4,321,398✔
2782
        if (right)
4,321,398✔
2783
            right[f] = r;
584,183✔
2784

2785
        if (stretchPhase)
4,321,398✔
2786
        {
2787
            stretchEnv = std::max(0.0f, stretchEnv - stretchEnvDec);
609,223✔
2788
            if (loopPhase == 1)
609,223✔
2789
                loopLevelComp += loopLevelCompInc;
71,880✔
2790
            else if (loopPhase == 2)
537,343✔
2791
                loopLevelComp -= loopLevelCompInc;
537,343✔
2792
        }
2793

2794
        if (loopPhase <= 1)
4,321,398✔
2795
        {
2796
            ++samplePos;
3,784,055✔
2797
            if (samplePos >= segmentLoop.end)
3,784,055✔
2798
            {
2799
                stretchPhase = true;
1,296✔
2800
                loopPhase = 2;
1,296✔
2801
                if (samplePos > 0)
1,296✔
2802
                    --samplePos;
1,296✔
2803
                if (samplePos <= segmentLoop.start)
1,296✔
2804
                    loopPhase = 1;
×
2805
            }
2806
        }
2807
        else
2808
        {
2809
            if (samplePos > 0)
537,343✔
2810
                --samplePos;
537,343✔
2811
            if (samplePos <= segmentLoop.start)
537,343✔
2812
                loopPhase = 1;
43✔
2813
        }
2814
    }
2815

2816
    if (frames_out)
1,294✔
2817
        *frames_out = needed;
1,294✔
2818

2819
    return VL_OK;
1,294✔
2820
}
2821

2822
/* -----------------------------------------------------------------------
2823
   Write: assembly from audio slices
2824
   ----------------------------------------------------------------------- */
2825

2826
VLError vl_set_info(VLFile file, const VLFileInfo* info)
30✔
2827
{
2828
    if (!file)
30✔
2829
        return VL_ERROR_INVALID_HANDLE;
1✔
2830

2831
    if (!info)
29✔
2832
        return VL_ERROR_INVALID_ARG;
1✔
2833

2834
    if (!file->impl.slices.empty() || !file->impl.pcm.empty())
28✔
2835
        return VL_ERROR_ALREADY_HAS_DATA;
1✔
2836

2837
    if (info->channels != 1 && info->channels != 2)
27✔
2838
        return VL_ERROR_INVALID_ARG;
×
2839

2840
    if (info->sample_rate < 8000 || info->sample_rate > 192000)
27✔
2841
        return VL_ERROR_INVALID_SAMPLE_RATE;
1✔
2842

2843
    if (info->tempo <= 0)
26✔
2844
        return VL_ERROR_INVALID_TEMPO;
1✔
2845

2846
    file->impl.info = *info;
25✔
2847
    file->impl.info.channels = info->channels;
25✔
2848
    file->impl.info.sample_rate = info->sample_rate;
25✔
2849
    file->impl.info.bit_depth = storageBitDepth(info->bit_depth);
25✔
2850
    file->impl.processingGain = (uint16_t)std::clamp(info->processing_gain > 0 ? info->processing_gain : 1000, 0, 1000);
25✔
2851
    file->impl.transientEnabled = info->transient_enabled != 0;
25✔
2852
    file->impl.transientAttack = (uint16_t)std::clamp(info->transient_attack, 0, 1023);
25✔
2853
    file->impl.transientDecay = (uint16_t)std::clamp(info->transient_decay > 0 ? info->transient_decay : 1023, 0, 1023);
25✔
2854
    file->impl.transientStretch = (uint16_t)std::clamp(info->transient_stretch, 0, 100);
25✔
2855
    file->impl.silenceSelected = info->silence_selected != 0;
25✔
2856
    file->outputSampleRate = info->sample_rate;
25✔
2857
    file->dirty = true;
25✔
2858

2859
    return VL_OK;
25✔
2860
}
2861

2862
VLError vl_set_creator_info(VLFile file, const VLCreatorInfo* info)
6✔
2863
{
2864
    if (!file)
6✔
2865
        return VL_ERROR_INVALID_HANDLE;
1✔
2866

2867
    if (!info)
5✔
2868
        return VL_ERROR_INVALID_ARG;
1✔
2869

2870
    if (!file->impl.slices.empty() || !file->impl.pcm.empty())
4✔
2871
        return VL_ERROR_ALREADY_HAS_DATA;
1✔
2872

2873
    file->impl.creator = *info;
3✔
2874
    file->impl.creator.name[sizeof(file->impl.creator.name) - 1] = '\0';
3✔
2875
    file->impl.creator.copyright[sizeof(file->impl.creator.copyright) - 1] = '\0';
3✔
2876
    file->impl.creator.url[sizeof(file->impl.creator.url) - 1] = '\0';
3✔
2877
    file->impl.creator.email[sizeof(file->impl.creator.email) - 1] = '\0';
3✔
2878
    file->impl.creator.free_text[sizeof(file->impl.creator.free_text) - 1] = '\0';
3✔
2879
    file->dirty = true;
3✔
2880

2881
    return VL_OK;
3✔
2882
}
2883

2884
int32_t vl_add_slice(VLFile file, int32_t ppq_pos, const float* left, const float* right, int32_t frames)
656✔
2885
{
2886
    if (!file)
656✔
2887
        return (int32_t)VL_ERROR_INVALID_HANDLE;
1✔
2888

2889
    if (ppq_pos < 0)
655✔
2890
        return (int32_t)VL_ERROR_INVALID_ARG;
1✔
2891

2892
    const uint32_t start = calcSampleStartFromPPQ(file->impl, (uint32_t)ppq_pos);
654✔
2893
    return addSliceAtSample(file, start, ppq_pos, left, right, frames);
654✔
2894
}
2895

2896
VLError vl_remove_slice(VLFile file, int32_t index)
4✔
2897
{
2898
    if (!file)
4✔
2899
        return VL_ERROR_INVALID_HANDLE;
1✔
2900

2901
    if (index < 0 || (size_t)index >= file->impl.slices.size())
3✔
2902
        return VL_ERROR_INVALID_SLICE;
2✔
2903

2904
    file->impl.slices.erase(file->impl.slices.begin() + index);
1✔
2905
    file->impl.info.slice_count = (int32_t)file->impl.slices.size();
1✔
2906
    file->dirty = true;
1✔
2907

2908
    return VL_OK;
1✔
2909
}
2910

2911
VLError vl_save(VLFile file, const char* path)
5✔
2912
{
2913
    if (!file)
5✔
2914
        return VL_ERROR_INVALID_HANDLE;
1✔
2915

2916
    if (!path)
4✔
2917
        return VL_ERROR_INVALID_ARG;
1✔
2918

2919
    size_t size = 0;
3✔
2920
    VLError e = vl_save_to_memory(file, nullptr, &size);
3✔
2921
    if (e != VL_OK)
3✔
2922
        return e;
1✔
2923

2924
    std::vector<uint8_t> buf(size);
2✔
2925
    e = vl_save_to_memory(file, buf.data(), &size);
2✔
2926
    if (e != VL_OK)
2✔
2927
        return e;
×
2928

2929
    std::ofstream out(path, std::ios::binary);
2✔
2930
    if (!out)
2✔
2931
        return VL_ERROR_INVALID_ARG;
1✔
2932

2933
    out.write((const char*)buf.data(), (std::streamsize)size);
1✔
2934
    return out ? VL_OK : VL_ERROR_FILE_CORRUPT;
1✔
2935
}
2✔
2936

2937
VLError vl_save_to_memory(VLFile file, void* buf, size_t* size_out)
145✔
2938
{
2939
    if (!file)
145✔
2940
        return VL_ERROR_INVALID_HANDLE;
1✔
2941

2942
    if (!size_out)
144✔
2943
        return VL_ERROR_INVALID_ARG;
18✔
2944

2945
    if (!file->isNew && !file->dirty && !file->impl.fileData.empty())
126✔
2946
    {
2947
        const std::vector<uint8_t>& encoded = file->impl.fileData;
54✔
2948

2949
        if (!buf)
54✔
2950
        {
2951
            *size_out = encoded.size();
18✔
2952
            return VL_OK;
18✔
2953
        }
2954

2955
        if (*size_out < encoded.size())
36✔
2956
        {
2957
            *size_out = encoded.size();
18✔
2958
            return VL_ERROR_BUFFER_TOO_SMALL;
18✔
2959
        }
2960

2961
        std::memcpy(buf, encoded.data(), encoded.size());
18✔
2962
        *size_out = encoded.size();
18✔
2963
        return VL_OK;
18✔
2964
    }
2965

2966
    if (file->impl.slices.empty() || file->impl.pcm.empty())
72✔
2967
        return VL_ERROR_INVALID_ARG;
2✔
2968

2969
    std::vector<uint8_t> encoded = buildREX2File(file->impl);
70✔
2970

2971
    if (!buf)
70✔
2972
    {
2973
        *size_out = encoded.size();
27✔
2974
        return VL_OK;
27✔
2975
    }
2976

2977
    if (*size_out < encoded.size())
43✔
2978
    {
2979
        *size_out = encoded.size();
16✔
2980
        return VL_ERROR_BUFFER_TOO_SMALL;
16✔
2981
    }
2982

2983
    std::memcpy(buf, encoded.data(), encoded.size());
27✔
2984
    *size_out = encoded.size();
27✔
2985
    return VL_OK;
27✔
2986
}
70✔
2987

2988
/* -----------------------------------------------------------------------
2989
   Utility
2990
   ----------------------------------------------------------------------- */
2991

2992
const char* vl_error_string(VLError err)
17✔
2993
{
2994
    switch (static_cast<int32_t>(err))
17✔
2995
    {
2996
        case static_cast<int32_t>(VL_OK): return "OK";
1✔
2997
        case static_cast<int32_t>(VL_ERROR_INVALID_HANDLE): return "invalid handle";
1✔
2998
        case static_cast<int32_t>(VL_ERROR_INVALID_ARG): return "invalid argument";
1✔
2999
        case static_cast<int32_t>(VL_ERROR_FILE_NOT_FOUND): return "file not found";
1✔
3000
        case static_cast<int32_t>(VL_ERROR_FILE_CORRUPT): return "file corrupt or unsupported format";
1✔
3001
        case static_cast<int32_t>(VL_ERROR_OUT_OF_MEMORY): return "out of memory";
1✔
3002
        case static_cast<int32_t>(VL_ERROR_INVALID_SLICE): return "invalid slice index";
1✔
3003
        case static_cast<int32_t>(VL_ERROR_INVALID_SAMPLE_RATE): return "invalid sample rate";
1✔
3004
        case static_cast<int32_t>(VL_ERROR_BUFFER_TOO_SMALL): return "buffer too small";
1✔
3005
        case static_cast<int32_t>(VL_ERROR_NO_CREATOR_INFO): return "no creator info available";
1✔
3006
        case static_cast<int32_t>(VL_ERROR_NOT_IMPLEMENTED): return "not implemented";
1✔
3007
        case static_cast<int32_t>(VL_ERROR_ALREADY_HAS_DATA): return "already has data";
1✔
3008
        case static_cast<int32_t>(VL_ERROR_FILE_TOO_NEW): return "file too new";
1✔
3009
        case static_cast<int32_t>(VL_ERROR_ZERO_LOOP_LENGTH): return "zero loop length";
1✔
3010
        case static_cast<int32_t>(VL_ERROR_INVALID_SIZE): return "invalid size";
1✔
3011
        case static_cast<int32_t>(VL_ERROR_INVALID_TEMPO): return "invalid tempo";
1✔
3012
        default: return "unknown error";
1✔
3013
    }
3014
}
3015

3016
const char* vl_version_string(void)
1✔
3017
{
3018
    return "velociloops 0.1.0";
1✔
3019
}
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