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

kunitoki / VelociLoops / 25340660593

04 May 2026 08:03PM UTC coverage: 74.729% (-19.2%) from 93.961%
25340660593

Pull #2

github

kunitoki
Fixed UB
Pull Request #2: Onset detection and save

292 of 337 new or added lines in 1 file covered. (86.65%)

4483 of 5999 relevant lines covered (74.73%)

2566394.54 hits per line

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

92.66
/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
/* -----------------------------------------------------------------------
17
   DWOP decompressor
18
   ----------------------------------------------------------------------- */
19

20
namespace
21
{
22

23
#include "fft8g.h"
24

25
inline int32_t clampInt32(int64_t v)
43,070,488✔
26
{
27
    if (v > std::numeric_limits<int32_t>::max())
43,070,488✔
28
        return std::numeric_limits<int32_t>::max();
×
29

30
    if (v < std::numeric_limits<int32_t>::min())
43,070,488✔
31
        return std::numeric_limits<int32_t>::min();
×
32

33
    return static_cast<int32_t>(v);
43,070,488✔
34
}
35

36
inline int32_t addInt32(int32_t a, int32_t b)
18,171,940✔
37
{
38
    return clampInt32(static_cast<int64_t>(a) + static_cast<int64_t>(b));
18,171,940✔
39
}
40

41
inline int32_t subInt32(int32_t a, int32_t b)
24,898,548✔
42
{
43
    return clampInt32(static_cast<int64_t>(a) - static_cast<int64_t>(b));
24,898,548✔
44
}
45

46
struct VLChannelState
47
{
48
    int32_t deltas[5] = {0, 0, 0, 0, 0};
49
    uint32_t averages[5] = {2560, 2560, 2560, 2560, 2560};
50
};
51

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

62
    void init(const uint8_t* data, size_t size)
90✔
63
    {
64
        const size_t words = size / 4;
90✔
65
        buf.resize(words);
90✔
66

67
        for (size_t i = 0; i < words; ++i)
2,678,472✔
68
        {
69
            const size_t b = i * 4;
2,678,382✔
70
            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,678,382✔
71
        }
72

73
        inputPtr = buf.data();
90✔
74
        endPtr = buf.data() + buf.size();
90✔
75
        currentWord = 0;
90✔
76
        bitsLeft = 0;
90✔
77
    }
90✔
78

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

84
        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;
77✔
85

86
        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];
77✔
87

88
        uint32_t j = 2;
77✔
89
        int32_t rbits = 0;
77✔
90

91
        uint32_t cw = currentWord;
77✔
92
        int32_t bl = bitsLeft;
77✔
93
        const uint32_t* inp = inputPtr;
77✔
94
        bool eof = false;
77✔
95

96
        for (uint32_t f = 0; f < frameCount && !eof; ++f)
9,042,993✔
97
        {
98
            uint32_t minAvg = a0;
9,042,916✔
99
            int minIdx = 0;
9,042,916✔
100

101
            if (a1 < minAvg)
9,042,916✔
102
            {
103
                minAvg = a1;
8,435,265✔
104
                minIdx = 1;
8,435,265✔
105
            }
106

107
            if (a2 < minAvg)
9,042,916✔
108
            {
109
                minAvg = a2;
3,873,454✔
110
                minIdx = 2;
3,873,454✔
111
            }
112

113
            if (a3 < minAvg)
9,042,916✔
114
            {
115
                minAvg = a3;
2,367,280✔
116
                minIdx = 3;
2,367,280✔
117
            }
118

119
            if (a4 < minAvg)
9,042,916✔
120
            {
121
                minAvg = a4;
1,505,454✔
122
                minIdx = 4;
1,505,454✔
123
            }
124

125
            uint32_t step = ((minAvg * 3u) + 36u) >> 7;
9,042,916✔
126
            uint32_t prefixSum = 0;
9,042,916✔
127
            int zerosWin = 7;
9,042,916✔
128

129
            while (true)
130
            {
131
                bool bit = readBit(cw, bl, inp, eof);
16,827,779✔
132

133
                if (eof)
16,827,779✔
134
                    break;
×
135

136
                if (bit)
16,827,779✔
137
                    break;
9,042,916✔
138

139
                if (step > 0 && prefixSum > 0xFFFFFFFFu - step)
7,784,863✔
140
                {
141
                    eof = true;
×
142
                    break;
×
143
                }
144

145
                prefixSum += step;
7,784,863✔
146
                if (--zerosWin == 0)
7,784,863✔
147
                {
148
                    step <<= 2;
49,191✔
149
                    zerosWin = 7;
49,191✔
150
                }
151
            }
7,784,863✔
152

153
            if (eof)
9,042,916✔
154
                break;
×
155

156
            adjustJRbits(step, j, rbits);
9,042,916✔
157

158
            uint32_t rem = (rbits > 0) ? readBits(rbits, cw, bl, inp, eof) : 0;
9,042,916✔
159
            if (eof)
9,042,916✔
160
                break;
×
161

162
            const int64_t thresh = static_cast<int64_t>(j) - static_cast<int64_t>(step);
9,042,916✔
163
            if (static_cast<int64_t>(rem) - thresh >= 0)
9,042,916✔
164
            {
165
                const uint32_t extra = readBits(1, cw, bl, inp, eof);
4,408,233✔
166

167
                if (eof)
4,408,233✔
168
                    break;
×
169

170
                rem = rem * 2u - static_cast<uint32_t>(thresh) + extra;
4,408,233✔
171
            }
172

173
            const uint32_t codeVal = rem + prefixSum;
9,042,916✔
174
            const int32_t signed2x = -(int32_t)(codeVal & 1u) ^ (int32_t)codeVal;
9,042,916✔
175
            const int32_t s2x = applyPredictor(minIdx, signed2x, d0, d1, d2, d3, d4);
9,042,916✔
176
            out[f] = clampSample(s2x >> 1, bitDepth);
9,042,916✔
177
            updateAverages(a0, a1, a2, a3, a4, d0, d1, d2, d3, d4);
9,042,916✔
178
        }
179

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

196
    bool decompressStereo(uint32_t frameCount, int32_t* out, int32_t bitDepth)
13✔
197
    {
198
        if (!out || frameCount == 0)
13✔
199
            return false;
×
200

201
        int32_t d[2][5];
202
        uint32_t a[2][5];
203
        for (int c = 0; c < 2; ++c)
39✔
204
        {
205
            for (int i = 0; i < 5; ++i)
156✔
206
            {
207
                d[c][i] = ch[c].deltas[i] * 2;
130✔
208
                a[c][i] = ch[c].averages[i];
130✔
209
            }
210
        }
211

212
        uint32_t j[2] = {2, 2};
13✔
213
        int32_t rbits[2] = {0, 0};
13✔
214

215
        uint32_t cw = currentWord;
13✔
216
        int32_t bl = bitsLeft;
13✔
217
        const uint32_t* inp = inputPtr;
13✔
218
        bool eof = false;
13✔
219

220
        for (uint32_t f = 0; f < frameCount && !eof; ++f)
862,366✔
221
        {
222
            int32_t ch2x[2] = {0, 0};
862,353✔
223
            for (int c = 0; c < 2 && !eof; ++c)
2,587,059✔
224
            {
225
                uint32_t minAvg = a[c][0];
1,724,706✔
226
                int minIdx = 0;
1,724,706✔
227
                if (a[c][1] < minAvg)
1,724,706✔
228
                {
229
                    minAvg = a[c][1];
1,277,134✔
230
                    minIdx = 1;
1,277,134✔
231
                }
232

233
                if (a[c][2] < minAvg)
1,724,706✔
234
                {
235
                    minAvg = a[c][2];
452,099✔
236
                    minIdx = 2;
452,099✔
237
                }
238

239
                if (a[c][3] < minAvg)
1,724,706✔
240
                {
241
                    minAvg = a[c][3];
134,524✔
242
                    minIdx = 3;
134,524✔
243
                }
244

245
                if (a[c][4] < minAvg)
1,724,706✔
246
                {
247
                    minAvg = a[c][4];
126,522✔
248
                    minIdx = 4;
126,522✔
249
                }
250

251
                uint32_t step = ((minAvg * 3u) + 36u) >> 7;
1,724,706✔
252
                uint32_t prefixSum = 0;
1,724,706✔
253
                int zerosWin = 7;
1,724,706✔
254

255
                while (true)
256
                {
257
                    bool bit = readBit(cw, bl, inp, eof);
2,937,765✔
258

259
                    if (eof)
2,937,765✔
260
                        break;
×
261

262
                    if (bit)
2,937,765✔
263
                        break;
1,724,706✔
264

265
                    if (step > 0 && prefixSum > 0xFFFFFFFFu - step)
1,213,059✔
266
                    {
267
                        eof = true;
×
268
                        break;
×
269
                    }
270

271
                    prefixSum += step;
1,213,059✔
272
                    if (--zerosWin == 0)
1,213,059✔
273
                    {
274
                        step <<= 2;
1,125✔
275
                        zerosWin = 7;
1,125✔
276
                    }
277
                }
1,213,059✔
278

279
                if (eof)
1,724,706✔
280
                    break;
×
281

282
                adjustJRbits(step, j[c], rbits[c]);
1,724,706✔
283

284
                uint32_t rem = (rbits[c] > 0) ? readBits(rbits[c], cw, bl, inp, eof) : 0;
1,724,706✔
285
                if (eof)
1,724,706✔
286
                    break;
×
287

288
                const int64_t thresh = static_cast<int64_t>(j[c]) - static_cast<int64_t>(step);
1,724,706✔
289
                if (static_cast<int64_t>(rem) - thresh >= 0)
1,724,706✔
290
                {
291
                    const uint32_t extra = readBits(1, cw, bl, inp, eof);
700,489✔
292

293
                    if (eof)
700,489✔
294
                        break;
×
295

296
                    rem = rem * 2u - static_cast<uint32_t>(thresh) + extra;
700,489✔
297
                }
298

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

305
            if (eof)
862,353✔
306
                break;
×
307

308
            out[f * 2 + 0] = clampSample(ch2x[0] >> 1, bitDepth);
862,353✔
309
            out[f * 2 + 1] = clampSample(((int64_t)ch2x[0] + (int64_t)ch2x[1]) >> 1, bitDepth);
862,353✔
310
        }
311

312
        for (int c = 0; c < 2; ++c)
39✔
313
        {
314
            for (int i = 0; i < 5; ++i)
156✔
315
            {
316
                ch[c].deltas[i] = d[c][i] >> 1;
130✔
317
                ch[c].averages[i] = a[c][i];
130✔
318
            }
319
        }
320
        currentWord = cw;
13✔
321
        bitsLeft = bl;
13✔
322
        inputPtr = inp;
13✔
323
        return !eof;
13✔
324
    }
325

326
private:
327
    static int32_t clampSample(int64_t v, int32_t bitDepth)
10,767,622✔
328
    {
329
        const int32_t maxSample = bitDepth == 24 ? 8388607 : 32767;
10,767,622✔
330
        const int32_t minSample = bitDepth == 24 ? -8388608 : -32768;
10,767,622✔
331

332
        if (v > maxSample)
10,767,622✔
333
            return maxSample;
×
334

335
        if (v < minSample)
10,767,622✔
336
            return minSample;
×
337

338
        return (int32_t)v;
10,767,622✔
339
    }
340

341
    bool readBit(uint32_t& cw, int32_t& bl, const uint32_t*& inp, bool& eof)
19,765,544✔
342
    {
343
        const int32_t blBefore = bl--;
19,765,544✔
344
        if (blBefore - 1 < 0)
19,765,544✔
345
        {
346
            if (inp >= endPtr)
621,351✔
347
            {
348
                eof = true;
×
349
                return false;
×
350
            }
351

352
            cw = *inp++;
621,351✔
353
            bl = 0x1f;
621,351✔
354
        }
355

356
        const bool bit = (int32_t)cw < 0;
19,765,544✔
357
        cw <<= 1;
19,765,544✔
358
        return bit;
19,765,544✔
359
    }
360

361
    uint32_t readBits(int n, uint32_t& cw, int32_t& bl, const uint32_t*& inp, bool& eof)
15,039,191✔
362
    {
363
        if (n <= 0 || n > 31)
15,039,191✔
364
        {
365
            eof = true;
×
366
            return 0;
×
367
        }
368

369
        uint32_t result = cw >> (32 - n);
15,039,191✔
370
        cw <<= n;
15,039,191✔
371

372
        const int32_t blBefore = bl;
15,039,191✔
373
        bl -= n;
15,039,191✔
374

375
        if (blBefore - n < 0)
15,039,191✔
376
        {
377
            if (inp >= endPtr)
2,056,982✔
378
            {
379
                eof = true;
×
380
                return result;
×
381
            }
382

383
            const uint32_t next = *inp++;
2,056,982✔
384
            bl += 32;
2,056,982✔
385
            result |= next >> bl;
2,056,982✔
386
            cw = next << (32 - bl);
2,056,982✔
387
        }
388

389
        return result;
15,039,191✔
390
    }
391

392
    static void adjustJRbits(uint32_t step, uint32_t& j, int32_t& rbits)
10,767,622✔
393
    {
394
        if (step < j)
10,767,622✔
395
        {
396
            for (uint32_t jt = j >> 1; step < jt; jt >>= 1)
10,801,553✔
397
            {
398
                j = jt;
191,635✔
399
                --rbits;
191,635✔
400
            }
401
        }
402
        else
403
        {
404
            while (step >= j)
349,842✔
405
            {
406
                const uint32_t prev = j;
192,138✔
407

408
                j <<= 1;
192,138✔
409
                ++rbits;
192,138✔
410

411
                if (j <= prev)
192,138✔
412
                {
413
                    j = prev;
×
414
                    break;
×
415
                }
416
            }
417
        }
418
    }
10,767,622✔
419

420
    static int32_t applyPredictor(int idx, int32_t s2x, int32_t& d0, int32_t& d1, int32_t& d2, int32_t& d3, int32_t& d4)
10,767,622✔
421
    {
422
        switch (idx)
10,767,622✔
423
        {
424
            case 0:
1,055,222✔
425
            {
426
                const int32_t t0 = subInt32(s2x, d0), t1 = subInt32(t0, d1), t2 = subInt32(t1, d2);
1,055,222✔
427
                d4 = subInt32(t2, d3);
1,055,222✔
428
                d3 = t2;
1,055,222✔
429
                d2 = t1;
1,055,222✔
430
                d1 = t0;
1,055,222✔
431
                d0 = s2x;
1,055,222✔
432
                return s2x;
1,055,222✔
433
            }
434

435
            case 1:
5,386,705✔
436
            {
437
                const int32_t t1 = subInt32(s2x, d1), t2 = subInt32(t1, d2), nd0 = addInt32(d0, s2x);
5,386,705✔
438
                d4 = subInt32(t2, d3);
5,386,705✔
439
                d3 = t2;
5,386,705✔
440
                d2 = t1;
5,386,705✔
441
                d1 = s2x;
5,386,705✔
442
                d0 = nd0;
5,386,705✔
443
                return nd0;
5,386,705✔
444
            }
445

446
            case 2:
1,823,826✔
447
            {
448
                const int32_t nd1 = addInt32(d1, s2x), nd0 = addInt32(d0, nd1), t = subInt32(s2x, d2);
1,823,826✔
449
                d4 = subInt32(t, d3);
1,823,826✔
450
                d3 = t;
1,823,826✔
451
                d2 = s2x;
1,823,826✔
452
                d1 = nd1;
1,823,826✔
453
                d0 = nd0;
1,823,826✔
454
                return nd0;
1,823,826✔
455
            }
456

457
            case 3:
869,893✔
458
            {
459
                const int32_t nd2 = addInt32(d2, s2x), nd1 = addInt32(d1, nd2), nd0 = addInt32(d0, nd1);
869,893✔
460
                d4 = subInt32(s2x, d3);
869,893✔
461
                d3 = s2x;
869,893✔
462
                d2 = nd2;
869,893✔
463
                d1 = nd1;
869,893✔
464
                d0 = nd0;
869,893✔
465
                return nd0;
869,893✔
466
            }
467

468
            case 4:
1,631,976✔
469
            {
470
                const int32_t nd3 = addInt32(d3, s2x), nd2 = addInt32(d2, nd3), nd1 = addInt32(d1, nd2), nd0 = addInt32(d0, nd1);
1,631,976✔
471
                d4 = s2x;
1,631,976✔
472
                d3 = nd3;
1,631,976✔
473
                d2 = nd2;
1,631,976✔
474
                d1 = nd1;
1,631,976✔
475
                d0 = nd0;
1,631,976✔
476
                return nd0;
1,631,976✔
477
            }
478

479
            default: return d0; // LCOV_EXCL_LINE idx comes from a 0..4 minimum search.
480
        }
481
    }
482

483
    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)
10,767,622✔
484
    {
485
        auto mag = [](int32_t v) -> uint32_t
53,838,110✔
486
        {
487
            return (uint32_t)(v ^ (v >> 31));
53,838,110✔
488
        };
489

490
        a0 = a0 + mag(d0) - (a0 >> 5);
10,767,622✔
491
        a1 = a1 + mag(d1) - (a1 >> 5);
10,767,622✔
492
        a2 = a2 + mag(d2) - (a2 >> 5);
10,767,622✔
493
        a3 = a3 + mag(d3) - (a3 >> 5);
10,767,622✔
494
        a4 = a4 + mag(d4) - (a4 >> 5);
10,767,622✔
495
    }
10,767,622✔
496
};
497

498
class VLBitWriter
499
{
500
public:
501
    void writeBit(bool bit)
96,812,128✔
502
    {
503
        if (bit)
96,812,128✔
504
            currentWord |= (uint32_t)1u << (31 - bitCount);
50,124,810✔
505
        if (++bitCount == 32)
96,812,128✔
506
            flushWord();
3,025,316✔
507
    }
96,812,128✔
508

509
    void writeBits(uint32_t value, int count)
11,193,023✔
510
    {
511
        for (int i = count - 1; i >= 0; --i)
79,438,526✔
512
            writeBit(((value >> i) & 1u) != 0);
68,245,503✔
513
    }
11,193,023✔
514

515
    std::vector<uint8_t> finish()
116✔
516
    {
517
        if (bitCount > 0)
116✔
518
            flushWord();
110✔
519

520
        return bytes;
116✔
521
    }
522

523
private:
524
    std::vector<uint8_t> bytes;
525
    uint32_t currentWord = 0;
526
    int bitCount = 0;
527

528
    void flushWord()
3,025,426✔
529
    {
530
        bytes.push_back((uint8_t)(currentWord >> 24));
3,025,426✔
531
        bytes.push_back((uint8_t)(currentWord >> 16));
3,025,426✔
532
        bytes.push_back((uint8_t)(currentWord >> 8));
3,025,426✔
533
        bytes.push_back((uint8_t)currentWord);
3,025,426✔
534
        currentWord = 0;
3,025,426✔
535
        bitCount = 0;
3,025,426✔
536
    }
3,025,426✔
537
};
538

539
class VLDWOPCompressor
540
{
541
public:
542
    VLChannelState ch[2];
543

544
    std::vector<uint8_t> compressMono(const int32_t* in, uint32_t frameCount)
103✔
545
    {
546
        reset();
103✔
547

548
        VLBitWriter bw;
103✔
549
        int32_t d[5] = {};
103✔
550
        uint32_t a[5] = {2560, 2560, 2560, 2560, 2560};
103✔
551
        uint32_t j = 2;
103✔
552
        int32_t rbits = 0;
103✔
553

554
        for (uint32_t f = 0; f < frameCount; ++f)
11,102,791✔
555
            encodeChannel(in[f] * 2, d, a, j, rbits, bw);
11,102,688✔
556

557
        return bw.finish();
206✔
558
    }
103✔
559

560
    std::vector<uint8_t> compressStereo(const int32_t* in, uint32_t frameCount)
13✔
561
    {
562
        reset();
13✔
563

564
        VLBitWriter bw;
13✔
565
        int32_t d[2][5] = {};
13✔
566
        uint32_t a[2][5] = {{2560, 2560, 2560, 2560, 2560}, {2560, 2560, 2560, 2560, 2560}};
13✔
567
        uint32_t j[2] = {2, 2};
13✔
568
        int32_t rbits[2] = {0, 0};
13✔
569

570
        for (uint32_t f = 0; f < frameCount; ++f)
838,011✔
571
        {
572
            const int32_t left2x = in[(size_t)f * 2] * 2;
837,998✔
573
            const int32_t right2x = in[(size_t)f * 2 + 1] * 2;
837,998✔
574
            encodeChannel(left2x, d[0], a[0], j[0], rbits[0], bw);
837,998✔
575
            encodeChannel(right2x - left2x, d[1], a[1], j[1], rbits[1], bw);
837,998✔
576
        }
577

578
        return bw.finish();
26✔
579
    }
13✔
580

581
private:
582
    void reset()
116✔
583
    {
584
        for (auto& c : ch)
348✔
585
        {
586
            std::fill(c.deltas, c.deltas + 5, 0);
232✔
587
            std::fill(c.averages, c.averages + 5, 2560);
232✔
588
        }
589
    }
116✔
590

591
    static uint32_t toCodeValue(int32_t signed2x)
12,778,684✔
592
    {
593
        if (signed2x >= 0)
12,778,684✔
594
            return (uint32_t)signed2x;
7,247,474✔
595

596
        return (uint32_t)(-signed2x - 1);
5,531,210✔
597
    }
598

599
    static int minAverageIndex(const uint32_t a[5])
12,778,684✔
600
    {
601
        int idx = 0;
12,778,684✔
602

603
        for (int i = 1; i < 5; ++i)
63,893,420✔
604
        {
605
            if (a[i] < a[idx])
51,114,736✔
606
                idx = i;
20,550,229✔
607
        }
608

609
        return idx;
12,778,684✔
610
    }
611

612
    static int32_t predictorResidual(int idx, int32_t sample2x, const int32_t d[5])
12,778,684✔
613
    {
614
        switch (idx)
12,778,684✔
615
        {
616
            case 0: return sample2x;
1,816,958✔
617

618
            case 1: return sample2x - d[0];
6,045,332✔
619

620
            case 2: return sample2x - d[0] - d[1];
2,053,928✔
621

622
            case 3: return sample2x - d[0] - d[1] - d[2];
1,052,599✔
623

624
            case 4: return sample2x - d[0] - d[1] - d[2] - d[3];
1,809,867✔
625

626
            default: return sample2x; // LCOV_EXCL_LINE idx comes from a 0..4 minimum search.
627
        }
628
    }
629

630
    static int32_t applyPredictor(int idx, int32_t s2x, int32_t d[5])
12,778,684✔
631
    {
632
        switch (idx)
12,778,684✔
633
        {
634
            case 0:
1,816,958✔
635
            {
636
                const int32_t t0 = s2x - d[0], t1 = t0 - d[1], t2 = t1 - d[2];
1,816,958✔
637
                d[4] = t2 - d[3];
1,816,958✔
638
                d[3] = t2;
1,816,958✔
639
                d[2] = t1;
1,816,958✔
640
                d[1] = t0;
1,816,958✔
641
                d[0] = s2x;
1,816,958✔
642
                return s2x;
1,816,958✔
643
            }
644

645
            case 1:
6,045,332✔
646
            {
647
                const int32_t t1 = s2x - d[1], t2 = t1 - d[2], nd0 = d[0] + s2x;
6,045,332✔
648
                d[4] = t2 - d[3];
6,045,332✔
649
                d[3] = t2;
6,045,332✔
650
                d[2] = t1;
6,045,332✔
651
                d[1] = s2x;
6,045,332✔
652
                d[0] = nd0;
6,045,332✔
653
                return nd0;
6,045,332✔
654
            }
655

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

667
            case 3:
1,052,599✔
668
            {
669
                const int32_t nd2 = d[2] + s2x, nd1 = d[1] + nd2, nd0 = d[0] + nd1;
1,052,599✔
670
                d[4] = s2x - d[3];
1,052,599✔
671
                d[3] = s2x;
1,052,599✔
672
                d[2] = nd2;
1,052,599✔
673
                d[1] = nd1;
1,052,599✔
674
                d[0] = nd0;
1,052,599✔
675
                return nd0;
1,052,599✔
676
            }
677

678
            case 4:
1,809,867✔
679
            {
680
                const int32_t nd3 = d[3] + s2x, nd2 = d[2] + nd3, nd1 = d[1] + nd2, nd0 = d[0] + nd1;
1,809,867✔
681
                d[4] = s2x;
1,809,867✔
682
                d[3] = nd3;
1,809,867✔
683
                d[2] = nd2;
1,809,867✔
684
                d[1] = nd1;
1,809,867✔
685
                d[0] = nd0;
1,809,867✔
686
                return nd0;
1,809,867✔
687
            }
688

689
            default: return d[0]; // LCOV_EXCL_LINE idx comes from a 0..4 minimum search.
690
        }
691
    }
692

693
    static void updateAverages(uint32_t a[5], const int32_t d[5])
12,778,684✔
694
    {
695
        auto mag = [](int32_t v) -> uint32_t
63,893,420✔
696
        {
697
            return (uint32_t)(v ^ (v >> 31));
63,893,420✔
698
        };
699

700
        for (int i = 0; i < 5; ++i)
76,672,104✔
701
        {
702
            a[i] = a[i] + mag(d[i]) - (a[i] >> 5);
63,893,420✔
703
        }
704
    }
12,778,684✔
705

706
    static void adjustJRbits(uint32_t step, uint32_t& j, int32_t& rbits)
22,935,373✔
707
    {
708
        if (step < j)
22,935,373✔
709
        {
710
            for (uint32_t jt = j >> 1; step < jt; jt >>= 1)
23,371,772✔
711
            {
712
                j = jt;
819,278✔
713
                --rbits;
819,278✔
714
            }
715
        }
716
        else
717
        {
718
            while (step >= j)
878,476✔
719
            {
720
                const uint32_t prev = j;
495,597✔
721

722
                j <<= 1;
495,597✔
723
                ++rbits;
495,597✔
724

725
                if (j <= prev)
495,597✔
726
                {
727
                    j = prev;
×
728
                    break;
×
729
                }
730
            }
731
        }
732
    }
22,935,373✔
733

734
    static bool encodeRemainder(uint32_t raw, uint32_t step, uint32_t j, int32_t rbits, uint32_t& remBits, bool& hasExtra, bool& extraBit)
22,935,373✔
735
    {
736
        if (rbits < 0 || rbits > 31)
22,935,373✔
737
            return false;
×
738

739
        const uint32_t limit = rbits == 31 ? 0x80000000u : (1u << rbits);
22,935,373✔
740
        const int32_t threshSigned = (int32_t)j - (int32_t)step;
22,935,373✔
741
        if (threshSigned < 0)
22,935,373✔
742
            return false;
×
743

744
        const uint32_t thresh = (uint32_t)threshSigned;
22,935,373✔
745

746
        if (raw < thresh)
22,935,373✔
747
        {
748
            if (raw >= limit)
7,147,432✔
749
                return false;
×
750

751
            remBits = raw;
7,147,432✔
752
            hasExtra = false;
7,147,432✔
753
            extraBit = false;
7,147,432✔
754
            return true;
7,147,432✔
755
        }
756

757
        const uint32_t folded = raw + thresh;
15,787,941✔
758
        remBits = folded >> 1;
15,787,941✔
759
        hasExtra = true;
15,787,941✔
760
        extraBit = (folded & 1u) != 0;
15,787,941✔
761
        return remBits >= thresh && remBits < limit;
15,787,941✔
762
    }
763

764
    static void writeCodeValue(uint32_t codeVal, uint32_t baseStep, uint32_t& j, int32_t& rbits, VLBitWriter& bw)
12,778,684✔
765
    {
766
        uint32_t prefixSum = 0;
12,778,684✔
767
        uint32_t step = baseStep;
12,778,684✔
768
        int zerosWin = 7;
12,778,684✔
769

770
        for (uint32_t zeros = 0; zeros < 0x100000; ++zeros)
22,935,373✔
771
        {
772
            if (codeVal >= prefixSum)
22,935,373✔
773
            {
774
                uint32_t trialJ = j;
22,935,373✔
775
                int32_t trialRbits = rbits;
22,935,373✔
776
                adjustJRbits(step, trialJ, trialRbits);
22,935,373✔
777

778
                uint32_t remBits = 0;
22,935,373✔
779
                bool hasExtra = false;
22,935,373✔
780
                bool extraBit = false;
22,935,373✔
781
                if (encodeRemainder(codeVal - prefixSum, step, trialJ, trialRbits, remBits, hasExtra, extraBit))
22,935,373✔
782
                {
783
                    for (uint32_t i = 0; i < zeros; ++i)
22,935,373✔
784
                        bw.writeBit(false);
10,156,689✔
785

786
                    bw.writeBit(true);
12,778,684✔
787

788
                    if (trialRbits > 0)
12,778,684✔
789
                        bw.writeBits(remBits, trialRbits);
11,193,023✔
790

791
                    if (hasExtra)
12,778,684✔
792
                        bw.writeBit(extraBit);
5,631,252✔
793

794
                    j = trialJ;
12,778,684✔
795
                    rbits = trialRbits;
12,778,684✔
796
                    return;
12,778,684✔
797
                }
798
            }
799

800
            if (baseStep == 0)
10,156,689✔
801
                break;
×
802

803
            if (UINT32_MAX - prefixSum < step)
10,156,689✔
804
                break;
×
805

806
            prefixSum += step;
10,156,689✔
807
            if (prefixSum > codeVal && step != 0)
10,156,689✔
808
                break;
×
809

810
            if (--zerosWin == 0)
10,156,689✔
811
            {
812
                step <<= 2;
60,852✔
813
                zerosWin = 7;
60,852✔
814
            }
815
        }
816

817
        bw.writeBit(true); // LCOV_EXCL_LINE emergency fallback; valid sample residuals encode above.
818
    }
819

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

831
class VLIFFWriter
832
{
833
public:
834
    std::vector<uint8_t> data;
835

836
    size_t beginChunk(const char id[4])
5,057✔
837
    {
838
        const size_t start = data.size();
5,057✔
839
        data.insert(data.end(), id, id + 4);
5,057✔
840
        put32(0);
5,057✔
841
        return start;
5,057✔
842
    }
843

844
    size_t beginCat(const char type[4])
348✔
845
    {
846
        const size_t start = beginChunk("CAT ");
348✔
847
        data.insert(data.end(), type, type + 4);
348✔
848
        return start;
348✔
849
    }
850

851
    void endChunk(size_t start)
5,057✔
852
    {
853
        const size_t payloadStart = start + 8;
5,057✔
854
        const uint32_t size = (uint32_t)(data.size() - payloadStart);
5,057✔
855

856
        data[start + 4] = (uint8_t)(size >> 24);
5,057✔
857
        data[start + 5] = (uint8_t)(size >> 16);
5,057✔
858
        data[start + 6] = (uint8_t)(size >> 8);
5,057✔
859
        data[start + 7] = (uint8_t)size;
5,057✔
860

861
        if (data.size() & 1u)
5,057✔
862
            data.push_back(0);
4,350✔
863
    }
5,057✔
864

865
    void put8(uint8_t v)
5,626✔
866
    {
867
        data.push_back(v);
5,626✔
868
    }
5,626✔
869

870
    void put16(uint16_t v)
4,582✔
871
    {
872
        data.push_back((uint8_t)(v >> 8));
4,582✔
873
        data.push_back((uint8_t)v);
4,582✔
874
    }
4,582✔
875

876
    void put32(uint32_t v)
13,580✔
877
    {
878
        data.push_back((uint8_t)(v >> 24));
13,580✔
879
        data.push_back((uint8_t)(v >> 16));
13,580✔
880
        data.push_back((uint8_t)(v >> 8));
13,580✔
881
        data.push_back((uint8_t)v);
13,580✔
882
    }
13,580✔
883

884
    void putBytes(const uint8_t* p, size_t n)
510✔
885
    {
886
        data.insert(data.end(), p, p + n);
510✔
887
    }
510✔
888
};
889

890
/* -----------------------------------------------------------------------
891
   REX2 file parser + DWOP decompressor wrapper
892
   ----------------------------------------------------------------------- */
893

894
enum VLSliceState
895
{
896
    kSliceNormal = 1,
897
    kSliceMuted = 2,
898
    kSliceLocked = 3
899
};
900

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

917
int32_t sliceFlags(const VLSliceEntry& s)
2,177✔
918
{
919
    int32_t flags = 0;
2,177✔
920
    if (s.state == kSliceMuted)
2,177✔
921
        flags |= VL_SLICE_FLAG_MUTED;
2✔
922
    else if (s.state == kSliceLocked)
2,175✔
923
        flags |= VL_SLICE_FLAG_LOCKED;
10✔
924
    if (s.selected_flag)
2,177✔
925
        flags |= VL_SLICE_FLAG_SELECTED;
67✔
926
    if (s.marker)
2,177✔
927
        flags |= VL_SLICE_FLAG_MARKER;
×
928
    if (s.synthetic_leading)
2,177✔
929
        flags |= VL_SLICE_FLAG_SYNTHETIC;
7✔
930
    return flags;
2,177✔
931
}
932

933
VLError applySliceFlags(VLSliceEntry& s, int32_t flags, int32_t analysisPoints)
6✔
934
{
935
    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;
936

937
    if (flags & ~kKnownFlags)
6✔
938
        return VL_ERROR_INVALID_ARG;
1✔
939

940
    if ((flags & VL_SLICE_FLAG_MUTED) && (flags & VL_SLICE_FLAG_LOCKED))
5✔
941
        return VL_ERROR_INVALID_ARG;
1✔
942

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

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

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

959
    return VL_OK;
3✔
960
}
961

962
template <typename T> class VLHeapArray
963
{
964
public:
965
    VLHeapArray() = default;
244✔
966

967
    VLHeapArray(const VLHeapArray&) = delete;
968
    VLHeapArray& operator=(const VLHeapArray&) = delete;
969

970
    VLHeapArray(VLHeapArray&& o) noexcept : ptr_(std::move(o.ptr_)), size_(o.size_)
971
    {
972
        o.size_ = 0;
973
    }
974

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

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

992
        T* raw = new (std::nothrow) T[n];
100✔
993
        if (!raw)
100✔
994
            return false;
×
995

996
        std::fill_n(raw, n, val);
100✔
997
        ptr_.reset(raw);
100✔
998
        size_ = n;
100✔
999

1000
        return true;
100✔
1001
    }
1002

1003
    [[nodiscard]] bool resize(std::size_t n, T val) noexcept
55✔
1004
    {
1005
        if (n == size_)
55✔
1006
            return true;
×
1007

1008
        if (n == 0)
55✔
1009
        {
1010
            ptr_.reset();
×
1011
            size_ = 0;
×
1012
            return true;
×
1013
        }
1014

1015
        T* raw = new (std::nothrow) T[n];
55✔
1016
        if (!raw)
55✔
1017
            return false;
×
1018

1019
        const std::size_t keep = std::min(size_, n);
55✔
1020
        if (keep)
55✔
1021
            std::memcpy(raw, ptr_.get(), keep * sizeof(T));
5✔
1022

1023
        if (n > size_)
55✔
1024
            std::fill_n(raw + size_, n - size_, val);
55✔
1025

1026
        ptr_.reset(raw);
55✔
1027
        size_ = n;
55✔
1028
        return true;
55✔
1029
    }
1030

1031
    T& operator[](std::size_t i) noexcept
7,372,441✔
1032
    {
1033
        assert(i < size_);
7,372,441✔
1034
        return ptr_[i];
7,372,441✔
1035
    }
1036

1037
    const T& operator[](std::size_t i) const noexcept
9,661,894✔
1038
    {
1039
        assert(i < size_);
9,661,894✔
1040
        return ptr_[i];
9,661,894✔
1041
    }
1042

1043
    T* data() noexcept
116✔
1044
    {
1045
        return ptr_.get();
116✔
1046
    }
1047

1048
    const T* data() const noexcept
1049
    {
1050
        return ptr_.get();
1051
    }
1052

1053
    std::size_t size() const noexcept
9,664,234✔
1054
    {
1055
        return size_;
9,664,234✔
1056
    }
1057

1058
    bool empty() const noexcept
1,674✔
1059
    {
1060
        return size_ == 0;
1,674✔
1061
    }
1062

1063
    static constexpr std::size_t max_size() noexcept
1,112✔
1064
    {
1065
        return std::numeric_limits<std::size_t>::max() / sizeof(T);
1,112✔
1066
    }
1067

1068
private:
1069
    std::unique_ptr<T[]> ptr_;
1070
    std::size_t size_ = 0;
1071
};
1072

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

1098
    static constexpr int32_t kREXPPQ = 15360;
1099

1100
    bool loadFromBuffer(const char* buf, size_t size)
193✔
1101
    {
1102
        fileData.assign((const uint8_t*)buf, (const uint8_t*)buf + size);
193✔
1103

1104
        info.channels = 1;
193✔
1105
        info.sample_rate = 44100;
193✔
1106
        info.slice_count = 0;
193✔
1107
        info.tempo = 120000;
193✔
1108
        info.original_tempo = 120000;
193✔
1109
        info.ppq_length = 61440;
193✔
1110
        info.time_sig_num = 4;
193✔
1111
        info.time_sig_den = 4;
193✔
1112
        info.bit_depth = 16;
193✔
1113
        info.total_frames = 0;
193✔
1114
        info.loop_start = 0;
193✔
1115
        info.loop_end = 0;
193✔
1116
        info.processing_gain = processingGain;
193✔
1117
        info.transient_enabled = transientEnabled ? 1 : 0;
193✔
1118
        info.transient_attack = transientAttack;
193✔
1119
        info.transient_decay = transientDecay;
193✔
1120
        info.transient_stretch = transientStretch;
193✔
1121
        info.silence_selected = silenceSelected ? 1 : 0;
193✔
1122
        analysisSensitivity = 0;
193✔
1123
        gateSensitivity = 0;
193✔
1124
        headerValid = true;
193✔
1125
        loadError = VL_OK;
193✔
1126
        loadedFromFile = false;
193✔
1127
        globBars = 1;
193✔
1128
        globBeats = 0;
193✔
1129

1130
        std::memset(&creator, 0, sizeof(creator));
193✔
1131

1132
        if (fileData.size() < 12)
193✔
1133
            return fail(VL_ERROR_INVALID_SIZE);
1✔
1134

1135
        if (fileData[0] == 'F' && fileData[1] == 'O' && fileData[2] == 'R' && fileData[3] == 'M' && fileData[8] == 'A' && fileData[9] == 'I' &&
240✔
1136
            fileData[10] == 'F' && fileData[11] == 'F')
240✔
1137
        {
1138
            const bool ok = loadLegacyAIFF();
48✔
1139
            if (ok)
48✔
1140
                loadedFromFile = true;
10✔
1141
            return ok;
48✔
1142
        }
1143

1144
        if (fileData[0] != 'C' || fileData[1] != 'A' || fileData[2] != 'T' || fileData[3] != ' ')
144✔
1145
            return fail(VL_ERROR_FILE_CORRUPT);
16✔
1146

1147
        size_t dwopOffset = 0, dwopSize = 0;
128✔
1148
        bool hasDWOP = false;
128✔
1149

1150
        parseIFF(8 + 4, fileData.size(), dwopOffset, dwopSize, hasDWOP);
128✔
1151
        if (loadError != VL_OK)
128✔
1152
            return false;
26✔
1153

1154
        finalizeSlices();
102✔
1155

1156
        if (!headerValid)
102✔
1157
            return false;
×
1158

1159
        if (!hasDWOP || dwopSize == 0)
102✔
1160
            return fail(VL_ERROR_FILE_CORRUPT);
4✔
1161

1162
        if (dwopOffset + dwopSize > fileData.size())
98✔
1163
            return fail(VL_ERROR_INVALID_SIZE);
×
1164

1165
        if (totalFrames == 0)
98✔
1166
            return fail(VL_ERROR_INVALID_SIZE);
8✔
1167

1168
        // 3600 * 192000 = 691,200,000 — exactly 1 hour at 192 kHz max sample rate.
1169
        static constexpr uint32_t kMaxTotalFrames = 3600u * 192000u;
1170
        if (totalFrames > kMaxTotalFrames)
90✔
1171
            return fail(VL_ERROR_INVALID_SIZE);
×
1172

1173
        const size_t pcmElements = (size_t)totalFrames * (size_t)info.channels;
90✔
1174

1175
        if (!pcm.assign(pcmElements, 0))
90✔
1176
            return fail(VL_ERROR_OUT_OF_MEMORY);
×
1177

1178
        VLDWOPDecompressor dec;
90✔
1179
        dec.init(&fileData[dwopOffset], dwopSize);
90✔
1180

1181
        uint32_t done = 0;
90✔
1182
        bool ok = true;
90✔
1183
        while (done < totalFrames && ok)
180✔
1184
        {
1185
            const uint32_t chunk = std::min<uint32_t>(0x100000, totalFrames - done);
90✔
1186

1187
            if (info.channels == 1)
90✔
1188
                ok = dec.decompressMono(chunk, &pcm[done], info.bit_depth);
77✔
1189
            else
1190
                ok = dec.decompressStereo(chunk, &pcm[(size_t)done * 2], info.bit_depth);
13✔
1191

1192
            done += chunk;
90✔
1193
        }
1194

1195
        if (!ok)
90✔
1196
            return fail(VL_ERROR_FILE_CORRUPT);
×
1197

1198
        finalizeRenderedLengths();
90✔
1199
        loadedFromFile = true;
90✔
1200
        return true;
90✔
1201
    }
90✔
1202

1203
private:
1204
    bool fail(VLError error)
95✔
1205
    {
1206
        if (loadError == VL_OK)
95✔
1207
            loadError = error;
93✔
1208
        return false;
95✔
1209
    }
1210

1211
    static uint32_t be32(const uint8_t* p)
60,047✔
1212
    {
1213
        return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | p[3];
60,047✔
1214
    }
1215

1216
    static uint16_t be16(const uint8_t* p)
771,518✔
1217
    {
1218
        return (uint16_t)(((uint16_t)p[0] << 8) | p[1]);
771,518✔
1219
    }
1220

1221
    static int32_t readAIFFExtendedRate(const uint8_t* p)
48✔
1222
    {
1223
        const uint16_t expon = be16(p);
48✔
1224
        uint64_t mant = 0;
48✔
1225
        for (int i = 0; i < 8; ++i)
432✔
1226
            mant = (mant << 8) | p[2 + i];
384✔
1227

1228
        if (expon == 0 || mant == 0)
48✔
1229
            return 0;
4✔
1230

1231
        const int sign = (expon & 0x8000u) ? -1 : 1;
44✔
1232
        const int exp = (int)(expon & 0x7fffu) - 16383;
44✔
1233
        const long double value = (long double)sign * (long double)mant * std::ldexp((long double)1.0, exp - 63);
44✔
1234
        if (value <= 0.0L || value > (long double)std::numeric_limits<int32_t>::max())
44✔
1235
            return 0;
×
1236

1237
        return (int32_t)std::lround((double)value);
44✔
1238
    }
1239

1240
    static int32_t readSignedSampleBE(const uint8_t* p, uint16_t bits)
964,919✔
1241
    {
1242
        switch (bits)
964,919✔
1243
        {
1244
            case 8: return (int32_t)(int8_t)p[0] * 256;
91,528✔
1245
            case 16: return (int16_t)be16(p);
766,609✔
1246
            case 24:
61,018✔
1247
            {
1248
                int32_t v = ((int32_t)p[0] << 16) | ((int32_t)p[1] << 8) | (int32_t)p[2];
61,018✔
1249
                if (v & 0x800000)
61,018✔
1250
                    v |= ~0xffffff;
32,554✔
1251
                return v;
61,018✔
1252
            }
1253
            case 32:
45,764✔
1254
            {
1255
                int32_t v = (int32_t)be32(p);
45,764✔
1256
                return v >> 16;
45,764✔
1257
            }
1258
            default: return 0;
×
1259
        }
1260
    }
1261

1262
    bool parseLegacyTempo(const uint8_t* d, uint32_t sz, bool appIsReCy)
44✔
1263
    {
1264
        const uint32_t tempoOffset = appIsReCy ? 14u : 16u;
44✔
1265
        if (sz < tempoOffset + 4u)
44✔
1266
            return false;
4✔
1267

1268
        const uint32_t v = be32(d + tempoOffset);
40✔
1269
        if (v < 20000u || v > 450000u)
40✔
1270
            return false;
4✔
1271

1272
        info.tempo = (int32_t)v;
36✔
1273
        info.original_tempo = (int32_t)v;
36✔
1274
        return true;
36✔
1275
    }
1276

1277
    static uint16_t legacyReCycleFilterPoints(uint16_t sensitivity)
9✔
1278
    {
1279
        const uint32_t sens = std::min<uint32_t>(sensitivity, 1000u);
9✔
1280
        const uint32_t visibleRange = (sens * 0x7fffu + 999u) / 1000u;
9✔
1281
        return (uint16_t)(0x7fffu - visibleRange);
9✔
1282
    }
1283

1284
    bool parseLegacyReCycleSlices(const uint8_t* d, uint32_t sz, std::vector<VLSliceEntry>& out)
41✔
1285
    {
1286
        if (sz < 4u + 0xa0u)
41✔
1287
            return false;
32✔
1288

1289
        const uint8_t* binary = d + 4;
9✔
1290
        const uint32_t binarySize = sz - 4u;
9✔
1291
        if (be32(binary) != 0xd1daded0u)
9✔
1292
            return false;
×
1293

1294
        const uint16_t sensitivity = be16(binary + 0x14);
9✔
1295
        const uint16_t filterPoints = legacyReCycleFilterPoints(sensitivity);
9✔
1296
        const uint16_t storedCount = be16(binary + 0x9e);
9✔
1297
        if (storedCount == 0 || storedCount > 1000)
9✔
1298
            return false;
×
1299

1300
        if (binarySize < 0xa0u + (uint32_t)storedCount * 8u)
9✔
1301
            return false;
×
1302

1303
        std::vector<VLSliceEntry> parsed;
9✔
1304
        parsed.reserve(storedCount);
9✔
1305
        for (uint16_t i = 0; i < storedCount; ++i)
186✔
1306
        {
1307
            const uint8_t* rec = binary + 0xa0u + (uint32_t)i * 8u;
177✔
1308
            const uint8_t state = rec[0] & 0x7f;
177✔
1309
            const bool selected = (rec[0] & 0x80) != 0;
177✔
1310
            const uint32_t start = ((uint32_t)rec[1] << 24) | ((uint32_t)rec[2] << 16) | ((uint32_t)rec[3] << 8) | rec[4];
177✔
1311
            const uint16_t points = be16(rec + 6);
177✔
1312

1313
            if (!selected && (state != 0 || points <= filterPoints))
177✔
1314
                continue;
107✔
1315

1316
            VLSliceEntry s;
70✔
1317
            s.sample_start = start;
70✔
1318
            s.sample_length = 1;
70✔
1319
            s.points = points;
70✔
1320
            s.selected_flag = selected ? 1 : 0;
70✔
1321
            if (state == 1)
70✔
1322
                s.state = kSliceLocked;
7✔
1323
            else if (state == 2)
63✔
1324
                s.state = kSliceMuted;
×
1325
            else
1326
                s.state = kSliceNormal;
63✔
1327
            parsed.push_back(s);
70✔
1328
        }
1329

1330
        if (parsed.empty())
9✔
1331
            return false;
2✔
1332

1333
        out = std::move(parsed);
7✔
1334
        return true;
7✔
1335
    }
9✔
1336

1337
    bool parseLegacyREXSlices(const uint8_t* d, uint32_t sz, std::vector<VLSliceEntry>& out, uint32_t& ppqLength,
3✔
1338
                              uint32_t& exportedFrameCount)
1339
    {
1340
        if (sz < 4u + 0x3f8u)
3✔
1341
            return false;
×
1342

1343
        const uint8_t* binary = d + 4;
3✔
1344
        const uint32_t binarySize = sz - 4u;
3✔
1345
        if (be32(binary) != 0xd1d1d1dau)
3✔
1346
            return false;
×
1347

1348
        const uint32_t storedPpqLength = be32(binary + 6);
3✔
1349
        if (storedPpqLength == 0 || storedPpqLength > std::numeric_limits<uint32_t>::max() / 16u)
3✔
1350
            return false;
×
1351

1352
        const uint16_t storedCount = be16(binary + 0x0a);
3✔
1353
        if (storedCount == 0 || storedCount > 1000)
3✔
1354
            return false;
×
1355

1356
        if (binarySize < 0x3f8u + (uint32_t)storedCount * 12u)
3✔
1357
            return false;
×
1358

1359
        std::vector<VLSliceEntry> parsed;
3✔
1360
        std::vector<uint32_t> ppqPositions;
3✔
1361
        std::vector<uint32_t> sourceLengths;
3✔
1362
        parsed.reserve(storedCount);
3✔
1363
        ppqPositions.reserve(storedCount);
3✔
1364
        sourceLengths.reserve(storedCount);
3✔
1365
        for (uint16_t i = 0; i < storedCount; ++i)
33✔
1366
        {
1367
            const uint8_t* rec = binary + 0x3f8u + (uint32_t)i * 12u;
30✔
1368
            const uint32_t start = be32(rec);
30✔
1369
            const uint32_t length = be32(rec + 4);
30✔
1370
            const uint32_t ppq16 = be32(rec + 8);
30✔
1371
            if (ppq16 > storedPpqLength || ppq16 > std::numeric_limits<uint32_t>::max() / 16u)
30✔
1372
                return false;
×
1373
            if (length == 0)
30✔
1374
                continue;
×
1375

1376
            VLSliceEntry s;
30✔
1377
            s.ppq_pos = ppq16 * 16u;
30✔
1378
            s.sample_start = start;
30✔
1379
            s.sample_length = length;
30✔
1380
            s.points = 0x7fff;
30✔
1381
            s.selected_flag = i == 0 ? 1 : 0;
30✔
1382
            s.state = kSliceNormal;
30✔
1383
            parsed.push_back(s);
30✔
1384
            ppqPositions.push_back(ppq16);
30✔
1385
            sourceLengths.push_back(length);
30✔
1386
        }
1387

1388
        if (parsed.empty())
3✔
1389
            return false;
×
1390

1391
        double samplesPerPpq = 0.0;
3✔
1392
        for (size_t i = 0; i < ppqPositions.size(); ++i)
33✔
1393
        {
1394
            uint32_t nextPpq = storedPpqLength;
30✔
1395
            for (size_t j = i + 1; j < ppqPositions.size(); ++j)
30✔
1396
            {
1397
                if (ppqPositions[j] != ppqPositions[i])
27✔
1398
                {
1399
                    nextPpq = ppqPositions[j];
27✔
1400
                    break;
27✔
1401
                }
1402
            }
1403

1404
            if (nextPpq <= ppqPositions[i])
30✔
1405
                continue;
×
1406

1407
            const uint32_t delta = nextPpq - ppqPositions[i];
30✔
1408
            samplesPerPpq = std::max(samplesPerPpq, (double)sourceLengths[i] / (double)delta);
30✔
1409
        }
1410

1411
        if (samplesPerPpq <= 0.0)
3✔
1412
            return false;
×
1413

1414
        ppqLength = storedPpqLength * 16u;
3✔
1415
        exportedFrameCount = (uint32_t)(samplesPerPpq * (double)storedPpqLength);
3✔
1416
        out = std::move(parsed);
3✔
1417
        return true;
3✔
1418
    }
3✔
1419

1420
    bool loadLegacyAIFF()
48✔
1421
    {
1422
        uint16_t channels = 0;
48✔
1423
        uint32_t frameCount = 0;
48✔
1424
        uint16_t bits = 0;
48✔
1425
        int32_t sampleRate = 0;
48✔
1426
        bool haveCOMM = false;
48✔
1427
        bool haveSSND = false;
48✔
1428
        size_t ssndOffset = 0;
48✔
1429
        size_t ssndSize = 0;
48✔
1430
        uint32_t markerLoopStart = 0;
48✔
1431
        uint32_t markerLoopEnd = 0;
48✔
1432
        bool haveLoopStart = false;
48✔
1433
        bool haveLoopEnd = false;
48✔
1434
        bool sawLegacyApp = false;
48✔
1435
        bool sawLegacyTempo = false;
48✔
1436
        bool legacyLoopLengthSet = true;
48✔
1437
        std::vector<VLSliceEntry> legacySlices;
48✔
1438
        bool legacySlicesHaveExplicitPpq = false;
48✔
1439
        uint32_t legacyRexPpqLength = 0;
48✔
1440
        uint32_t legacyRexExportedFrameCount = 0;
48✔
1441

1442
        const size_t formEnd = std::min(fileData.size(), (size_t)be32(&fileData[4]) + 8u);
48✔
1443
        size_t off = 12;
48✔
1444
        while (off + 8 <= formEnd && off + 8 <= fileData.size())
212✔
1445
        {
1446
            char id[5] = {};
164✔
1447
            std::memcpy(id, &fileData[off], 4);
164✔
1448
            const uint32_t sz = be32(&fileData[off + 4]);
164✔
1449
            const size_t payload = off + 8;
164✔
1450
            if (payload + sz > fileData.size())
164✔
1451
                break;
×
1452

1453
            if (std::strcmp(id, "COMM") == 0 && sz >= 18)
164✔
1454
            {
1455
                channels = be16(&fileData[payload]);
48✔
1456
                frameCount = be32(&fileData[payload + 2]);
48✔
1457
                bits = be16(&fileData[payload + 6]);
48✔
1458
                sampleRate = readAIFFExtendedRate(&fileData[payload + 8]);
48✔
1459
                haveCOMM = true;
48✔
1460
            }
1461
            else if (std::strcmp(id, "SSND") == 0 && sz >= 8)
116✔
1462
            {
1463
                const uint32_t dataOffset = be32(&fileData[payload]);
48✔
1464
                const size_t dataStart = payload + 8u + (size_t)dataOffset;
48✔
1465
                if (dataStart <= payload + sz && dataStart <= fileData.size())
48✔
1466
                {
1467
                    ssndOffset = dataStart;
48✔
1468
                    ssndSize = (payload + sz) - dataStart;
48✔
1469
                    haveSSND = true;
48✔
1470
                }
1471
            }
48✔
1472
            else if (std::strcmp(id, "MARK") == 0 && sz >= 2)
68✔
1473
            {
1474
                uint32_t pos = 2;
12✔
1475
                const uint16_t count = be16(&fileData[payload]);
12✔
1476
                for (uint16_t i = 0; i < count && pos + 7 <= sz; ++i)
36✔
1477
                {
1478
                    const uint32_t markerPos = be32(&fileData[payload + pos + 2]);
24✔
1479
                    const uint8_t nameLen = fileData[payload + pos + 6];
24✔
1480
                    pos += 7;
24✔
1481
                    if (pos + nameLen > sz)
24✔
1482
                        break;
×
1483

1484
                    const char* name = (const char*)&fileData[payload + pos];
24✔
1485
                    const bool isLoopStart = nameLen == 10 && std::memcmp(name, "Loop start", 10) == 0;
24✔
1486
                    const bool isLoopEnd = nameLen == 8 && std::memcmp(name, "Loop end", 8) == 0;
24✔
1487
                    if (isLoopStart)
24✔
1488
                    {
1489
                        markerLoopStart = markerPos;
12✔
1490
                        haveLoopStart = true;
12✔
1491
                    }
1492
                    else if (isLoopEnd)
12✔
1493
                    {
1494
                        markerLoopEnd = markerPos;
12✔
1495
                        haveLoopEnd = true;
12✔
1496
                    }
1497

1498
                    pos += nameLen + (((nameLen + 1u) & 1u) ? 1u : 0u);
24✔
1499
                }
1500
            }
12✔
1501
            else if (std::strcmp(id, "APPL") == 0 && sz >= 8)
56✔
1502
            {
1503
                const bool appIsREX = std::memcmp(&fileData[payload], "REX ", 4) == 0;
44✔
1504
                const bool appIsReCy = std::memcmp(&fileData[payload], "ReCy", 4) == 0;
44✔
1505
                if (appIsREX || appIsReCy)
44✔
1506
                {
1507
                    sawLegacyApp = true;
44✔
1508
                    if (appIsReCy)
44✔
1509
                    {
1510
                        legacyLoopLengthSet = sz > 12 && fileData[payload + 12] != 0;
41✔
1511
                        parseLegacyReCycleSlices(&fileData[payload], sz, legacySlices);
41✔
1512
                    }
1513
                    else if (appIsREX)
3✔
1514
                    {
1515
                        legacySlicesHaveExplicitPpq =
1516
                            parseLegacyREXSlices(&fileData[payload], sz, legacySlices, legacyRexPpqLength, legacyRexExportedFrameCount);
3✔
1517
                    }
1518
                    sawLegacyTempo = parseLegacyTempo(&fileData[payload], sz, appIsReCy) || sawLegacyTempo;
44✔
1519
                }
1520
            }
1521

1522
            off = payload + sz;
164✔
1523
            if (off & 1)
164✔
1524
                ++off;
×
1525
        }
1526

1527
        if (!sawLegacyApp)
48✔
1528
            return fail(VL_ERROR_FILE_CORRUPT);
4✔
1529

1530
        if (!sawLegacyTempo)
44✔
1531
            return fail(VL_ERROR_INVALID_TEMPO);
8✔
1532

1533
        if (!legacyLoopLengthSet)
36✔
1534
            return fail(VL_ERROR_ZERO_LOOP_LENGTH);
6✔
1535

1536
        if (!haveCOMM || !haveSSND || channels < 1 || channels > 2 || frameCount == 0 || sampleRate <= 0)
30✔
1537
        {
1538
            if (frameCount == 0)
12✔
1539
                return fail(VL_ERROR_INVALID_SIZE);
4✔
1540
            if (sampleRate <= 0)
8✔
1541
                return fail(VL_ERROR_INVALID_SAMPLE_RATE);
4✔
1542
            return fail(VL_ERROR_FILE_CORRUPT);
4✔
1543
        }
1544

1545
        if (bits != 8 && bits != 16 && bits != 24 && bits != 32)
18✔
1546
            return fail(VL_ERROR_FILE_CORRUPT);
4✔
1547

1548
        const size_t bytesPerSample = (size_t)((bits + 7u) / 8u);
14✔
1549
        const size_t frameBytes = bytesPerSample * (size_t)channels;
14✔
1550
        if (frameBytes == 0)
14✔
1551
            return fail(VL_ERROR_INVALID_SIZE);
×
1552

1553
        const uint32_t availableFrames = (uint32_t)std::min<size_t>(frameCount, ssndSize / frameBytes);
14✔
1554
        if (availableFrames == 0)
14✔
1555
            return fail(VL_ERROR_INVALID_SIZE);
4✔
1556

1557
        if (!pcm.assign((size_t)availableFrames * channels, 0))
10✔
1558
            return fail(VL_ERROR_OUT_OF_MEMORY);
×
1559

1560
        const uint8_t* src = &fileData[ssndOffset];
10✔
1561
        for (uint32_t frame = 0; frame < availableFrames; ++frame)
964,929✔
1562
        {
1563
            for (uint16_t ch = 0; ch < channels; ++ch)
1,929,838✔
1564
            {
1565
                const size_t sample = ((size_t)frame * channels + ch) * bytesPerSample;
964,919✔
1566
                pcm[(size_t)frame * channels + ch] = readSignedSampleBE(src + sample, bits);
964,919✔
1567
            }
1568
        }
1569

1570
        totalFrames = availableFrames;
10✔
1571
        loopStart = haveLoopStart && markerLoopStart < availableFrames ? markerLoopStart : 0;
10✔
1572
        loopEnd = haveLoopEnd && markerLoopEnd > loopStart && markerLoopEnd <= availableFrames ? markerLoopEnd : availableFrames;
10✔
1573

1574
        info.channels = channels;
10✔
1575
        info.sample_rate = sampleRate;
10✔
1576
        info.slice_count = 1;
10✔
1577
        info.ppq_length = legacyRexPpqLength > 0 ? (int32_t)legacyRexPpqLength : kREXPPQ * 4;
10✔
1578
        info.time_sig_num = 4;
10✔
1579
        info.time_sig_den = 4;
10✔
1580
        info.bit_depth = bits;
10✔
1581
        info.total_frames = (int32_t)totalFrames;
10✔
1582
        info.loop_start = (int32_t)loopStart;
10✔
1583
        info.loop_end = (int32_t)loopEnd;
10✔
1584
        info.processing_gain = processingGain;
10✔
1585
        info.transient_enabled = 0;
10✔
1586
        info.transient_attack = 0;
10✔
1587
        info.transient_decay = 1023;
10✔
1588
        info.transient_stretch = 0;
10✔
1589
        info.silence_selected = 0;
10✔
1590
        transientEnabled = false;
10✔
1591
        transientAttack = 0;
10✔
1592
        transientDecay = 1023;
10✔
1593
        transientStretch = 0;
10✔
1594

1595
        if (legacyRexExportedFrameCount > 0 && info.ppq_length > 0)
10✔
1596
        {
1597
            const double beats = (double)info.ppq_length / (double)kREXPPQ;
3✔
1598
            const double bpm = beats * 60.0 * (double)info.sample_rate / (double)legacyRexExportedFrameCount;
3✔
1599
            const int32_t derivedOriginalTempo = (int32_t)std::lround(bpm * 1000.0);
3✔
1600
            if (derivedOriginalTempo > 0)
3✔
1601
                info.original_tempo = derivedOriginalTempo;
3✔
1602
        }
1603

1604
        if (!legacySlices.empty())
10✔
1605
        {
1606
            std::sort(legacySlices.begin(), legacySlices.end(),
10✔
1607
                      [](const VLSliceEntry& a, const VLSliceEntry& b)
180✔
1608
                      {
1609
                          return a.sample_start < b.sample_start;
180✔
1610
                      });
1611
            legacySlices.erase(std::unique(legacySlices.begin(), legacySlices.end(),
10✔
1612
                                           [](const VLSliceEntry& a, const VLSliceEntry& b)
90✔
1613
                                           {
1614
                                               return a.sample_start == b.sample_start;
90✔
1615
                                           }),
1616
                               legacySlices.end());
10✔
1617

1618
            const uint32_t sliceEnd = loopEnd > loopStart ? loopEnd : totalFrames;
10✔
1619
            for (size_t i = 0; i < legacySlices.size(); ++i)
110✔
1620
            {
1621
                const uint32_t next = i + 1 < legacySlices.size() ? legacySlices[i + 1].sample_start : sliceEnd;
100✔
1622
                if (!legacySlicesHaveExplicitPpq)
100✔
1623
                    legacySlices[i].sample_length = next > legacySlices[i].sample_start ? next - legacySlices[i].sample_start : 1u;
70✔
1624
            }
1625

1626
            slices = std::move(legacySlices);
10✔
1627
            if (!legacySlicesHaveExplicitPpq)
10✔
1628
                finalizeSlices();
7✔
1629
            else
1630
                info.slice_count = (int32_t)slices.size();
3✔
1631
            finalizeRenderedLengths();
10✔
1632
        }
1633
        else
1634
        {
1635
            VLSliceEntry s;
×
1636
            s.ppq_pos = 0;
×
1637
            s.sample_start = loopStart;
×
1638
            s.sample_length = std::max<uint32_t>(1u, loopEnd > loopStart ? loopEnd - loopStart : totalFrames - loopStart);
×
1639
            s.rendered_length = s.sample_length;
×
1640
            s.points = 0x7fff;
×
1641
            s.selected_flag = 1;
×
1642
            s.state = kSliceNormal;
×
1643
            slices.push_back(s);
×
1644
            info.slice_count = (int32_t)slices.size();
×
1645
            finalizeRenderedLengths();
×
1646
        }
1647
        return true;
10✔
1648
    }
48✔
1649

1650
    void parseIFF(size_t start, size_t end, size_t& dwopOffset, size_t& dwopSize, bool& hasDWOP)
316✔
1651
    {
1652
        size_t off = start;
316✔
1653
        while (off + 8 < end && off + 8 < fileData.size())
5,305✔
1654
        {
1655
            char id[5] = {};
4,997✔
1656
            std::memcpy(id, &fileData[off], 4);
4,997✔
1657
            off += 4;
4,997✔
1658

1659
            const uint32_t sz = be32(&fileData[off]);
4,997✔
1660
            off += 4;
4,997✔
1661

1662
            if (off + sz > fileData.size())
4,997✔
1663
            {
1664
                fail(VL_ERROR_INVALID_SIZE);
8✔
1665
                break;
8✔
1666
            }
1667

1668
            if (std::strcmp(id, "HEAD") == 0)
4,989✔
1669
                parseHEAD(&fileData[off], sz);
128✔
1670

1671
            else if (std::strcmp(id, "CREI") == 0)
4,861✔
1672
                parseCREI(&fileData[off], sz);
9✔
1673

1674
            else if (std::strcmp(id, "SINF") == 0)
4,852✔
1675
                parseSINF(&fileData[off], sz);
126✔
1676

1677
            else if (std::strcmp(id, "GLOB") == 0)
4,726✔
1678
                parseGLOB(&fileData[off], sz);
128✔
1679

1680
            else if (std::strcmp(id, "TRSH") == 0)
4,598✔
1681
                parseTRSH(&fileData[off], sz);
94✔
1682

1683
            else if (std::strcmp(id, "RECY") == 0)
4,504✔
1684
                parseRECY(&fileData[off], sz);
94✔
1685

1686
            else if (std::strcmp(id, "SLCE") == 0)
4,410✔
1687
                parseSLCE(&fileData[off], sz);
3,907✔
1688

1689
            else if ((std::strcmp(id, "SDAT") == 0 || std::strcmp(id, "DWOP") == 0) && !hasDWOP)
503✔
1690
            {
1691
                dwopOffset = off;
118✔
1692
                dwopSize = sz;
118✔
1693
                hasDWOP = true;
118✔
1694
            }
1695

1696
            else if (std::strcmp(id, "CAT ") == 0 && sz >= 4)
385✔
1697
            {
1698
                parseIFF(off + 4, off + sz, dwopOffset, dwopSize, hasDWOP);
188✔
1699
            }
1700

1701
            off += sz;
4,989✔
1702
            if (off & 1)
4,989✔
1703
                ++off;
4,357✔
1704
        }
1705
    }
316✔
1706

1707
    void parseSINF(const uint8_t* d, uint32_t sz)
126✔
1708
    {
1709
        if (sz < 18)
126✔
1710
            return;
4✔
1711

1712
        info.channels = d[0];
122✔
1713
        const uint8_t bd = d[1];
122✔
1714
        info.sample_rate = (int32_t)be32(d + 2);
122✔
1715
        totalFrames = be32(d + 6);
122✔
1716
        loopStart = be32(d + 10);
122✔
1717
        loopEnd = be32(d + 14);
122✔
1718
        info.total_frames = (int32_t)totalFrames;
122✔
1719
        info.loop_start = (int32_t)loopStart;
122✔
1720
        info.loop_end = (int32_t)loopEnd;
122✔
1721

1722
        switch (bd)
122✔
1723
        {
1724
            case 1: info.bit_depth = 8; break;
1✔
1725

1726
            case 3: info.bit_depth = 16; break;
112✔
1727

1728
            case 5: info.bit_depth = 24; break;
7✔
1729

1730
            case 7: info.bit_depth = 32; break;
1✔
1731

1732
            default: info.bit_depth = 16; break;
1✔
1733
        }
1734

1735
        const uint32_t frames = (loopEnd > loopStart) ? (loopEnd - loopStart) : totalFrames;
122✔
1736
        if (frames > 0 && info.sample_rate > 0 && info.ppq_length > 0)
122✔
1737
        {
1738
            const double beats = (double)info.ppq_length / (double)kREXPPQ;
118✔
1739
            const double bpm = beats * 60.0 * (double)info.sample_rate / (double)frames;
118✔
1740

1741
            const int32_t t = (int32_t)std::lround(bpm * 1000.0);
118✔
1742
            if (t > 0)
118✔
1743
                info.original_tempo = t;
118✔
1744
        }
1745

1746
        if (info.original_tempo == 0)
122✔
1747
            info.original_tempo = info.tempo;
×
1748

1749
        if (info.channels != 1 && info.channels != 2)
122✔
1750
            info.channels = 1;
×
1751
    }
1752

1753
    void parseHEAD(const uint8_t* d, uint32_t sz)
128✔
1754
    {
1755
        if (sz < 6 || be32(d) != 0x490cf18du)
128✔
1756
        {
1757
            headerValid = false;
4✔
1758
            fail(VL_ERROR_FILE_CORRUPT);
4✔
1759
            return;
4✔
1760
        }
1761

1762
        if (d[4] != 0xbc)
124✔
1763
        {
1764
            headerValid = false;
×
1765
            fail(VL_ERROR_FILE_CORRUPT);
×
1766
            return;
×
1767
        }
1768

1769
        if (d[5] > 0x03)
124✔
1770
        {
1771
            headerValid = false;
6✔
1772
            fail(VL_ERROR_FILE_TOO_NEW);
6✔
1773
        }
1774
    }
1775

1776
    void parseGLOB(const uint8_t* d, uint32_t sz)
128✔
1777
    {
1778
        if (sz < 22)
128✔
1779
        {
1780
            fail(VL_ERROR_INVALID_SIZE);
6✔
1781
            return;
6✔
1782
        }
1783

1784
        info.slice_count = (int32_t)be32(d);
122✔
1785
        globBars = be16(d + 4);
122✔
1786
        globBeats = d[6];
122✔
1787
        info.time_sig_num = d[7];
122✔
1788
        info.time_sig_den = d[8];
122✔
1789
        analysisSensitivity = d[9];
122✔
1790
        gateSensitivity = be16(d + 10);
122✔
1791
        const uint32_t tempo = be32(d + 16);
122✔
1792
        if (tempo < 20000u || tempo > 450000u)
122✔
1793
            fail(VL_ERROR_INVALID_TEMPO);
4✔
1794
        info.tempo = (int32_t)tempo;
122✔
1795
        {
1796
            const int32_t timeSigNum = info.time_sig_num > 0 ? info.time_sig_num : 4;
122✔
1797
            const int32_t totalBeats = (int32_t)globBars * timeSigNum + (int32_t)globBeats;
122✔
1798
            const int64_t ppqLen = totalBeats > 0 ? (int64_t)totalBeats * kREXPPQ : (int64_t)4 * kREXPPQ;
122✔
1799
            info.ppq_length = (int32_t)std::clamp(ppqLen, (int64_t)1, (int64_t)INT32_MAX);
122✔
1800
        }
1801
        processingGain = be16(d + 12);
122✔
1802
        silenceSelected = d[21] != 0;
122✔
1803
        info.processing_gain = processingGain;
122✔
1804
        info.silence_selected = silenceSelected ? 1 : 0;
122✔
1805
        loadedFromFile = true;
122✔
1806
    }
1807

1808
    void parseCREI(const uint8_t* d, uint32_t sz)
9✔
1809
    {
1810
        uint32_t off = 0;
9✔
1811
        auto readString = [&](char* dst, size_t dstSize)
45✔
1812
        {
1813
            if (!dst || dstSize == 0)
45✔
1814
                return;
×
1815

1816
            dst[0] = '\0';
45✔
1817
            if (off + 4 > sz)
45✔
1818
                return;
4✔
1819

1820
            const uint32_t n = be32(d + off);
41✔
1821
            off += 4;
41✔
1822
            if (off + n > sz)
41✔
1823
            {
1824
                off = sz;
1✔
1825
                return;
1✔
1826
            }
1827

1828
            const size_t copy = std::min<size_t>(n, dstSize - 1);
40✔
1829
            if (copy)
40✔
1830
                std::memcpy(dst, d + off, copy);
33✔
1831

1832
            dst[copy] = '\0';
40✔
1833
            off += n;
40✔
1834
        };
9✔
1835

1836
        readString(creator.name, sizeof(creator.name));
9✔
1837
        readString(creator.copyright, sizeof(creator.copyright));
9✔
1838
        readString(creator.url, sizeof(creator.url));
9✔
1839
        readString(creator.email, sizeof(creator.email));
9✔
1840
        readString(creator.free_text, sizeof(creator.free_text));
9✔
1841
    }
9✔
1842

1843
    void parseTRSH(const uint8_t* d, uint32_t sz)
94✔
1844
    {
1845
        if (sz < 7)
94✔
1846
            return;
×
1847

1848
        transientEnabled = d[0] != 0;
94✔
1849
        transientAttack = be16(d + 1);
94✔
1850
        transientDecay = be16(d + 3);
94✔
1851
        transientStretch = be16(d + 5);
94✔
1852
        info.transient_enabled = transientEnabled ? 1 : 0;
94✔
1853
        info.transient_attack = transientAttack;
94✔
1854
        info.transient_decay = transientDecay;
94✔
1855
        info.transient_stretch = transientStretch;
94✔
1856
    }
1857

1858
    void parseRECY(const uint8_t* d, uint32_t sz)
94✔
1859
    {
1860
        if (sz < 12)
94✔
1861
            return;
×
1862

1863
        const int32_t t = (int32_t)be32(d + 8);
94✔
1864
        if (t > 0)
94✔
1865
            info.original_tempo = t;
91✔
1866
    }
1867

1868
    void parseSLCE(const uint8_t* d, uint32_t sz)
3,907✔
1869
    {
1870
        static constexpr size_t kMaxSlices = 1024;
1871

1872
        if (slices.size() >= kMaxSlices)
3,907✔
1873
            return;
×
1874

1875
        if (sz < 10)
3,907✔
1876
            return;
×
1877

1878
        VLSliceEntry s;
3,907✔
1879
        s.sample_start = be32(d);
3,907✔
1880
        s.sample_length = be32(d + 4);
3,907✔
1881
        s.points = be16(d + 8);
3,907✔
1882
        s.marker = s.sample_length <= 1;
3,907✔
1883

1884
        const uint8_t flags = sz > 10 ? d[10] : 0;
3,907✔
1885
        s.selected_flag = (flags & 0x04) ? 1 : 0;
3,907✔
1886

1887
        if (flags & 0x02)
3,907✔
1888
            s.state = kSliceLocked;
9✔
1889
        else if (flags & 0x01)
3,898✔
1890
            s.state = kSliceMuted;
1✔
1891
        else
1892
            s.state = kSliceNormal;
3,897✔
1893

1894
        slices.push_back(s);
3,907✔
1895
        info.slice_count = (int32_t)slices.size();
3,907✔
1896
    }
1897

1898
    static uint16_t rex2FilterPoints(uint8_t sensitivity)
671✔
1899
    {
1900
        const uint32_t sens = std::min<uint32_t>(sensitivity, 99u);
671✔
1901
        const uint32_t visibleRange = (sens * 0x7fffu + 98u) / 99u;
671✔
1902
        return (uint16_t)(0x7fffu - visibleRange);
671✔
1903
    }
1904

1905
    bool isVisibleSliceBoundary(const VLSliceEntry& s) const
3,851✔
1906
    {
1907
        if (s.sample_length > 1)
3,851✔
1908
            return true;
3,180✔
1909

1910
        if (s.selected_flag || s.state != kSliceNormal)
671✔
1911
            return true;
×
1912

1913
        return s.points > rex2FilterPoints(analysisSensitivity);
671✔
1914
    }
1915

1916
    uint32_t defaultSliceEnd(uint32_t start) const
3,199✔
1917
    {
1918
        if (loopEnd > loopStart && start < loopEnd)
3,199✔
1919
            return loopEnd;
3,195✔
1920

1921
        return totalFrames;
4✔
1922
    }
1923

1924
    uint32_t gateLengthFrames() const
109✔
1925
    {
1926
        if (gateSensitivity == 0)
109✔
1927
            return 0;
60✔
1928

1929
        const uint32_t sr = info.sample_rate > 0 ? (uint32_t)info.sample_rate : 44100u;
49✔
1930
        uint32_t frames = (uint32_t)(((uint64_t)gateSensitivity * sr + 4500u) / 9000u);
49✔
1931
        frames = std::max(1u, frames);
49✔
1932

1933
        return std::max(1u, ((frames + 64u) / 128u) * 128u);
49✔
1934
    }
1935

1936
    void finalizeSlices()
109✔
1937
    {
1938
        const uint32_t denom = (loopEnd > loopStart) ? (loopEnd - loopStart) : (totalFrames ? totalFrames : 1u);
109✔
1939
        const uint32_t gatedFrames = gateLengthFrames();
109✔
1940

1941
        std::sort(slices.begin(), slices.end(),
109✔
1942
                  [](const VLSliceEntry& a, const VLSliceEntry& b)
26,920✔
1943
                  {
1944
                      return a.sample_start < b.sample_start;
26,920✔
1945
                  });
1946

1947
        std::vector<VLSliceEntry> out;
109✔
1948
        for (auto s : slices)
4,062✔
1949
        {
1950
            if (loopEnd > loopStart && s.sample_start < totalFrames && s.sample_start >= loopEnd)
3,953✔
1951
                continue;
102✔
1952

1953
            if (!isVisibleSliceBoundary(s))
3,851✔
1954
                continue;
652✔
1955

1956
            const uint32_t rel = (s.sample_start > loopStart) ? (s.sample_start - loopStart) : 0;
3,199✔
1957

1958
            s.ppq_pos = (uint32_t)(((uint64_t)rel * info.ppq_length + denom / 2) / denom);
3,199✔
1959
            s.marker = false;
3,199✔
1960

1961
            out.push_back(s);
3,199✔
1962
        }
1963

1964
        for (size_t i = 0; i < out.size(); ++i)
3,308✔
1965
        {
1966
            const uint32_t start = out[i].sample_start;
3,199✔
1967
            uint32_t next = defaultSliceEnd(start);
3,199✔
1968
            for (size_t j = i + 1; j < out.size(); ++j)
3,200✔
1969
            {
1970
                if (out[j].sample_start > start)
3,111✔
1971
                {
1972
                    next = out[j].sample_start;
3,110✔
1973
                    break;
3,110✔
1974
                }
1975
            }
1976

1977
            const uint32_t derived = next > start ? next - start : 1u;
3,199✔
1978
            if (gateSensitivity == 0 || out[i].sample_length <= 1)
3,199✔
1979
            {
1980
                out[i].sample_length = derived;
1,837✔
1981
                if (gateSensitivity != 0 && gatedFrames > 0)
1,837✔
1982
                    out[i].sample_length = std::min(out[i].sample_length, gatedFrames);
×
1983
            }
1984
            else if (derived > 1)
1,362✔
1985
            {
1986
                out[i].sample_length = std::min(out[i].sample_length, derived);
1,361✔
1987
            }
1988

1989
            out[i].sample_length = std::max<uint32_t>(1u, out[i].sample_length);
3,199✔
1990
        }
1991

1992
        if (!out.empty() && loopEnd > loopStart && out.front().sample_start > loopStart && out.front().sample_start <= totalFrames)
109✔
1993
        {
1994
            VLSliceEntry leading;
9✔
1995
            leading.ppq_pos = 0;
9✔
1996
            leading.sample_start = loopStart;
9✔
1997
            leading.sample_length = out.front().sample_start - loopStart;
9✔
1998
            leading.points = 0x7fff;
9✔
1999
            leading.selected_flag = 1;
9✔
2000
            leading.state = kSliceNormal;
9✔
2001
            leading.synthetic_leading = true;
9✔
2002

2003
            out.insert(out.begin(), leading);
9✔
2004
        }
2005

2006
        slices.swap(out);
109✔
2007
        info.slice_count = (int32_t)slices.size();
109✔
2008
    }
109✔
2009

2010
    uint32_t sourceEndForSlice(const VLSliceEntry& s) const
15,128✔
2011
    {
2012
        const uint32_t start = s.sample_start;
15,128✔
2013
        if (start >= totalFrames)
15,128✔
2014
            return start;
8✔
2015

2016
        uint32_t frames = std::min(s.sample_length, totalFrames - start);
15,120✔
2017
        if (loopEnd > loopStart && start < loopEnd)
15,120✔
2018
            frames = std::min(frames, loopEnd - start);
14,008✔
2019

2020
        return start + frames;
15,120✔
2021
    }
2022

2023
public:
2024
    struct SegmentLoop
2025
    {
2026
        uint32_t start = 0;
2027
        uint32_t end = 0;
2028
        float volumeCompensation = 1.0f;
2029
    };
2030

2031
    SegmentLoop findSegmentLoop(uint32_t start, uint32_t end) const
8,120✔
2032
    {
2033
        SegmentLoop r{start, end};
8,120✔
2034

2035
        const uint32_t sr = info.sample_rate ? (uint32_t)info.sample_rate : 44100u;
8,120✔
2036
        const uint32_t srch = std::max(1u, (400u * sr) / 44100u);
8,120✔
2037
        const uint32_t mhl = std::max(1u, (20000u * sr) / 44100u);
8,120✔
2038

2039
        if (end <= start || end - start < srch * 3u)
8,120✔
2040
            return r;
4,075✔
2041

2042
        const uint32_t ch = std::max(1, info.channels);
4,045✔
2043
        auto leftAbs = [&](uint32_t f) -> int
3,002,638✔
2044
        {
2045
            const size_t i = (size_t)f * ch;
3,002,638✔
2046
            return i < pcm.size() ? std::abs((int)pcm[i]) : 0;
3,002,638✔
2047
        };
4,045✔
2048

2049
        uint32_t loopEnd = end - srch;
4,045✔
2050
        int peak = -1;
4,045✔
2051
        for (uint32_t i = 0, f = end - srch; i < srch && f > start; ++i, --f)
1,614,245✔
2052
        {
2053
            const int p = leftAbs(f);
1,610,200✔
2054
            if (p > peak)
1,610,200✔
2055
            {
2056
                peak = p;
79,198✔
2057
                loopEnd = f;
79,198✔
2058
            }
2059
        }
2060

2061
        uint32_t hl = std::min((loopEnd - start) / 2u, mhl);
4,045✔
2062
        uint32_t ls = loopEnd - hl;
4,045✔
2063
        uint32_t loopStart = ls;
4,045✔
2064
        int lspeak = -1;
4,045✔
2065
        for (uint32_t i = 0, f = ls; i < srch && f >= start; ++i)
1,396,483✔
2066
        {
2067
            const int p = leftAbs(f);
1,392,438✔
2068
            if (p > lspeak)
1,392,438✔
2069
            {
2070
                lspeak = p;
59,870✔
2071
                loopStart = f;
59,870✔
2072
            }
2073

2074
            if (f == 0)
1,392,438✔
2075
                break;
×
2076

2077
            --f;
1,392,438✔
2078
        }
2079

2080
        r.start = std::clamp(loopStart, start, end - 1u);
4,045✔
2081
        r.end = std::clamp(loopEnd, r.start + 1u, end);
4,045✔
2082

2083
        if (lspeak > 0 && peak > 0)
4,045✔
2084
            r.volumeCompensation = std::min(10.0f, (float)peak / (float)lspeak);
3,904✔
2085

2086
        return r;
4,045✔
2087
    }
2088

2089
private:
2090
    void cacheRenderLoop(VLSliceEntry& s) const
8,120✔
2091
    {
2092
        const uint32_t start = s.sample_start;
8,120✔
2093
        const uint32_t end = sourceEndForSlice(s);
8,120✔
2094
        const SegmentLoop loop = findSegmentLoop(start, end);
8,120✔
2095
        s.render_loop_start = loop.start;
8,120✔
2096
        s.render_loop_end = loop.end;
8,120✔
2097
        s.render_loop_volume_compensation = loop.volumeCompensation;
8,120✔
2098
    }
8,120✔
2099

2100
    uint32_t calcRenderedLength(const VLSliceEntry& s) const
7,008✔
2101
    {
2102
        const uint32_t start = s.sample_start;
7,008✔
2103
        const uint32_t end = sourceEndForSlice(s);
7,008✔
2104

2105
        if (end <= start)
7,008✔
2106
            return 1u;
4✔
2107

2108
        const uint32_t segLen = end - start;
7,004✔
2109
        if (!transientEnabled || transientStretch == 0)
7,004✔
2110
            return segLen;
3,328✔
2111

2112
        const uint32_t loopE = (s.render_loop_end > start && s.render_loop_end <= end) ? s.render_loop_end : end;
3,676✔
2113
        const uint32_t stretchN = (uint32_t)transientStretch + 1u;
3,676✔
2114
        const uint32_t stretchT = (uint32_t)(((uint64_t)(loopE - start) * stretchN) / 100u);
3,676✔
2115

2116
        return std::max(1u, segLen + stretchT);
3,676✔
2117
    }
2118

2119
public:
2120
    void cacheSliceRender(VLSliceEntry& s) const
7,008✔
2121
    {
2122
        cacheRenderLoop(s);
7,008✔
2123
        s.rendered_length = calcRenderedLength(s);
7,008✔
2124
    }
7,008✔
2125

2126
    void cacheSliceLoop(VLSliceEntry& s) const
1,112✔
2127
    {
2128
        cacheRenderLoop(s);
1,112✔
2129
    }
1,112✔
2130

2131
    void finalizeRenderedLengths()
216✔
2132
    {
2133
        for (auto& s : slices)
7,224✔
2134
            cacheSliceRender(s);
7,008✔
2135
    }
216✔
2136
};
2137

2138
int32_t storageBitDepth(int32_t bitDepth)
13,066,853✔
2139
{
2140
    return bitDepth == 24 ? 24 : 16;
13,066,853✔
2141
}
2142

2143
int32_t floatToPCM(float s, int32_t bitDepth)
6,407,432✔
2144
{
2145
    if (s > 1.0f)
6,407,432✔
2146
        s = 1.0f;
64✔
2147

2148
    if (s < -1.0f)
6,407,432✔
2149
        s = -1.0f;
64✔
2150

2151
    if (storageBitDepth(bitDepth) == 24)
6,407,432✔
2152
    {
2153
        const float scaled = s >= 0.0f ? s * 8388607.0f : s * 8388608.0f;
113,479✔
2154
        return (int32_t)std::lround(scaled);
113,479✔
2155
    }
2156

2157
    const float scaled = s >= 0.0f ? s * 32767.0f : s * 32768.0f;
6,293,953✔
2158
    return (int32_t)std::lround(scaled);
6,293,953✔
2159
}
2160

2161
float pcmToFloat(int32_t sample, int32_t bitDepth)
6,659,256✔
2162
{
2163
    return storageBitDepth(bitDepth) == 24 ? (float)sample / 8388608.0f : (float)sample / 32768.0f;
6,659,256✔
2164
}
2165

2166
uint8_t bitDepthCode(int32_t bitDepth)
116✔
2167
{
2168
    switch (bitDepth)
116✔
2169
    {
2170
        case 8: return 1; // LCOV_EXCL_LINE unsupported authored depth is normalized before writing.
2171

2172
        case 16: return 3;
110✔
2173

2174
        case 24: return 5;
6✔
2175

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

2178
        default: return 3; // LCOV_EXCL_LINE unsupported authored depth is normalized before writing.
2179
    }
2180
}
2181

2182
bool hasCreatorInfo(const VLCreatorInfo& c)
116✔
2183
{
2184
    return c.name[0] || c.copyright[0] || c.url[0] || c.email[0] || c.free_text[0];
116✔
2185
}
2186

2187
void writeCStringChunkString(VLIFFWriter& w, const char* s)
55✔
2188
{
2189
    const size_t n = s ? std::min<size_t>(std::strlen(s), 255) : 0;
55✔
2190

2191
    w.put32((uint32_t)n);
55✔
2192

2193
    if (n)
55✔
2194
        w.putBytes((const uint8_t*)s, n);
46✔
2195
}
55✔
2196

2197
void writeSimpleChunk(VLIFFWriter& w, const char id[4], const std::vector<uint8_t>& payload)
116✔
2198
{
2199
    const size_t c = w.beginChunk(id);
116✔
2200

2201
    if (!payload.empty())
116✔
2202
        w.putBytes(payload.data(), payload.size());
116✔
2203

2204
    w.endChunk(c);
116✔
2205
}
116✔
2206

2207
uint32_t calcSampleStartFromPPQ(const VLFileImpl& impl, uint32_t ppq)
634✔
2208
{
2209
    const VLFileInfo& info = impl.info;
634✔
2210
    if (info.ppq_length > 0 && info.loop_end > info.loop_start)
634✔
2211
    {
2212
        const uint32_t denom = (uint32_t)(info.loop_end - info.loop_start);
621✔
2213
        return (uint32_t)info.loop_start + (uint32_t)(((uint64_t)ppq * denom + (uint32_t)info.ppq_length / 2u) / (uint32_t)info.ppq_length);
621✔
2214
    }
2215

2216
    const uint32_t tempo = info.tempo > 0 ? (uint32_t)info.tempo : 120000u;
13✔
2217
    const uint32_t sr = info.sample_rate > 0 ? (uint32_t)info.sample_rate : 44100u;
13✔
2218
    return (uint32_t)(((uint64_t)ppq * sr * 60000u + (uint64_t)tempo * VLFileImpl::kREXPPQ / 2u) / ((uint64_t)tempo * VLFileImpl::kREXPPQ));
13✔
2219
}
2220

2221
void normaliseInfoForSave(VLFileImpl& impl)
116✔
2222
{
2223
    impl.info.channels = std::clamp(impl.info.channels, 1, 2);
116✔
2224

2225
    if (impl.info.sample_rate <= 0)
116✔
2226
        impl.info.sample_rate = 44100;
×
2227

2228
    if (impl.info.tempo <= 0)
116✔
2229
        impl.info.tempo = 120000;
×
2230

2231
    if (impl.info.original_tempo <= 0)
116✔
2232
        impl.info.original_tempo = impl.info.tempo;
2✔
2233

2234
    if (impl.info.time_sig_num <= 0)
116✔
2235
        impl.info.time_sig_num = 4;
2✔
2236

2237
    if (impl.info.time_sig_den <= 0)
116✔
2238
        impl.info.time_sig_den = 4;
2✔
2239

2240
    impl.info.bit_depth = storageBitDepth(impl.info.bit_depth);
116✔
2241
    impl.info.slice_count = (int32_t)impl.slices.size();
116✔
2242

2243
    impl.totalFrames = (uint32_t)(impl.pcm.size() / (size_t)impl.info.channels);
116✔
2244
    impl.info.total_frames = (int32_t)impl.totalFrames;
116✔
2245

2246
    if (impl.info.loop_start < 0 || (uint32_t)impl.info.loop_start >= impl.totalFrames)
116✔
2247
        impl.info.loop_start = 0;
1✔
2248

2249
    if (impl.info.loop_end <= impl.info.loop_start || (uint32_t)impl.info.loop_end > impl.totalFrames)
116✔
2250
        impl.info.loop_end = (int32_t)impl.totalFrames;
1✔
2251

2252
    impl.loopStart = (uint32_t)std::max(0, impl.info.loop_start);
116✔
2253
    impl.loopEnd = (uint32_t)std::max(impl.info.loop_start, impl.info.loop_end);
116✔
2254

2255
    if (!impl.loadedFromFile && impl.analysisSensitivity == 0)
116✔
2256
        impl.analysisSensitivity = 99;
25✔
2257

2258
    if (impl.info.ppq_length <= 0)
116✔
2259
    {
2260
        const uint32_t frames = impl.loopEnd > impl.loopStart ? impl.loopEnd - impl.loopStart : impl.totalFrames;
2✔
2261

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

2264
        impl.info.ppq_length = std::max(1, (int32_t)std::lround(beats * VLFileImpl::kREXPPQ));
2✔
2265
    }
2266

2267
    {
2268
        const int32_t timeSigNum = impl.info.time_sig_num > 0 ? impl.info.time_sig_num : 4;
116✔
2269
        const int32_t totalBeats = std::max(1, (impl.info.ppq_length + VLFileImpl::kREXPPQ / 2) / VLFileImpl::kREXPPQ);
116✔
2270
        impl.globBars = (uint16_t)(totalBeats / timeSigNum);
116✔
2271
        impl.globBeats = (uint8_t)(totalBeats % timeSigNum);
116✔
2272
    }
2273

2274
    impl.processingGain = (uint16_t)std::clamp(impl.info.processing_gain > 0 ? impl.info.processing_gain : 1000, 0, 1000);
116✔
2275
    impl.transientEnabled = impl.info.transient_enabled != 0;
116✔
2276
    impl.transientAttack = (uint16_t)std::clamp(impl.info.transient_attack, 0, 1023);
116✔
2277
    impl.transientDecay = (uint16_t)std::clamp(impl.info.transient_decay > 0 ? impl.info.transient_decay : 1023, 0, 1023);
116✔
2278
    impl.transientStretch = (uint16_t)std::clamp(impl.info.transient_stretch, 0, 100);
116✔
2279
    impl.silenceSelected = impl.info.silence_selected != 0;
116✔
2280
    impl.finalizeRenderedLengths();
116✔
2281
}
116✔
2282

2283
std::vector<uint8_t> buildREX2File(VLFileImpl& impl)
116✔
2284
{
2285
    normaliseInfoForSave(impl);
116✔
2286

2287
    VLDWOPCompressor comp;
116✔
2288
    const std::vector<uint8_t> sdat =
2289
        impl.info.channels == 2 ? comp.compressStereo(impl.pcm.data(), impl.totalFrames) : comp.compressMono(impl.pcm.data(), impl.totalFrames);
116✔
2290

2291
    VLIFFWriter w;
116✔
2292
    const size_t root = w.beginCat("REX2");
116✔
2293

2294
    {
2295
        const size_t c = w.beginChunk("HEAD");
116✔
2296
        const uint8_t head[] = {0x49, 0x0c, 0xf1, 0x8d, 0xbc, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
116✔
2297
                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
2298
        w.putBytes(head, sizeof(head));
116✔
2299
        w.endChunk(c);
116✔
2300
    }
2301

2302
    if (hasCreatorInfo(impl.creator))
116✔
2303
    {
2304
        const size_t c = w.beginChunk("CREI");
11✔
2305
        writeCStringChunkString(w, impl.creator.name);
11✔
2306
        writeCStringChunkString(w, impl.creator.copyright);
11✔
2307
        writeCStringChunkString(w, impl.creator.url);
11✔
2308
        writeCStringChunkString(w, impl.creator.email);
11✔
2309
        writeCStringChunkString(w, impl.creator.free_text);
11✔
2310
        w.endChunk(c);
11✔
2311
    }
2312

2313
    {
2314
        const size_t c = w.beginChunk("GLOB");
116✔
2315
        w.put32((uint32_t)impl.slices.size());
116✔
2316
        w.put16(impl.globBars);
116✔
2317
        w.put8(impl.globBeats);
116✔
2318
        w.put8((uint8_t)impl.info.time_sig_num);
116✔
2319
        w.put8((uint8_t)impl.info.time_sig_den);
116✔
2320
        w.put8(impl.analysisSensitivity);
116✔
2321
        const uint16_t gate = impl.slices.empty() ? impl.gateSensitivity : std::max<uint16_t>(1, impl.gateSensitivity);
116✔
2322
        w.put16(gate);
116✔
2323
        w.put16(impl.processingGain);
116✔
2324
        w.put16(1);
116✔
2325
        w.put32((uint32_t)impl.info.tempo);
116✔
2326
        w.put8(1);
116✔
2327
        w.put8(impl.silenceSelected ? 1 : 0);
116✔
2328
        w.endChunk(c);
116✔
2329
    }
2330

2331
    {
2332
        const size_t c = w.beginChunk("RECY");
116✔
2333
        w.put8(0xbc);
116✔
2334
        w.put8(0x02);
116✔
2335
        w.put8(0);
116✔
2336
        w.put8(0);
116✔
2337
        w.put8(0);
116✔
2338
        w.put8(1);
116✔
2339
        w.put8(0);
116✔
2340
        w.put32((uint32_t)(impl.totalFrames * impl.info.channels * (impl.info.bit_depth / 8)));
116✔
2341
        w.put32((uint32_t)impl.slices.size());
116✔
2342
        w.endChunk(c);
116✔
2343
    }
2344

2345
    {
2346
        const size_t devl = w.beginCat("DEVL");
116✔
2347
        const size_t trsh = w.beginChunk("TRSH");
116✔
2348
        w.put8(impl.transientEnabled ? 1 : 0);
116✔
2349
        w.put16(impl.transientAttack);
116✔
2350
        w.put16(impl.transientDecay);
116✔
2351
        w.put16(impl.transientStretch);
116✔
2352
        w.endChunk(trsh);
116✔
2353

2354
        const size_t eq = w.beginChunk("EQ  ");
116✔
2355
        const uint8_t eqPayload[] = {0x00, 0x00, 0x0f, 0x00, 0x64, 0x00, 0x00, 0x03, 0xe8, 0x09, 0xc4, 0x00, 0x00, 0x03, 0xe8, 0x4e, 0x20};
116✔
2356
        w.putBytes(eqPayload, sizeof(eqPayload));
116✔
2357
        w.endChunk(eq);
116✔
2358

2359
        const size_t comp = w.beginChunk("COMP");
116✔
2360
        const uint8_t compPayload[] = {0x00, 0x00, 0x4d, 0x00, 0x27, 0x00, 0x42, 0x00, 0x38};
116✔
2361
        w.putBytes(compPayload, sizeof(compPayload));
116✔
2362
        w.endChunk(comp);
116✔
2363

2364
        w.endChunk(devl);
116✔
2365
    }
2366

2367
    {
2368
        const size_t slcl = w.beginCat("SLCL");
116✔
2369
        std::vector<VLSliceEntry> sorted = impl.slices;
116✔
2370
        std::sort(sorted.begin(), sorted.end(),
116✔
2371
                  [](const VLSliceEntry& a, const VLSliceEntry& b)
28,758✔
2372
                  {
2373
                      return a.sample_start < b.sample_start;
28,758✔
2374
                  });
2375
        for (const auto& s : sorted)
3,886✔
2376
        {
2377
            const size_t c = w.beginChunk("SLCE");
3,770✔
2378
            w.put32(s.sample_start);
3,770✔
2379
            w.put32(std::max<uint32_t>(1, s.sample_length));
3,770✔
2380
            w.put16(s.points);
3,770✔
2381
            uint8_t flags = 0;
3,770✔
2382
            if (s.state == kSliceMuted)
3,770✔
2383
                flags |= 0x01;
6✔
2384
            else if (s.state == kSliceLocked)
3,764✔
2385
                flags |= 0x02;
12✔
2386
            if (s.selected_flag)
3,770✔
2387
                flags |= 0x04;
69✔
2388
            w.put8(flags);
3,770✔
2389
            w.endChunk(c);
3,770✔
2390
        }
2391
        w.endChunk(slcl);
116✔
2392
    }
116✔
2393

2394
    {
2395
        const size_t c = w.beginChunk("SINF");
116✔
2396
        w.put8((uint8_t)impl.info.channels);
116✔
2397
        w.put8(bitDepthCode(impl.info.bit_depth));
116✔
2398
        w.put32((uint32_t)impl.info.sample_rate);
116✔
2399
        w.put32(impl.totalFrames);
116✔
2400
        w.put32(impl.loopStart);
116✔
2401
        w.put32(impl.loopEnd);
116✔
2402
        w.endChunk(c);
116✔
2403
    }
2404

2405
    writeSimpleChunk(w, "SDAT", sdat);
116✔
2406
    w.endChunk(root);
116✔
2407
    return w.data;
232✔
2408
}
116✔
2409

2410
} // anonymous namespace
2411

2412
/* -----------------------------------------------------------------------
2413
   VLFile_s  —  the opaque handle
2414
   ----------------------------------------------------------------------- */
2415

2416
struct VLFile_s
2417
{
2418
    VLFileImpl impl;
2419
};
2420

2421
/* -----------------------------------------------------------------------
2422
   Utilities
2423
   ----------------------------------------------------------------------- */
2424

2425
namespace
2426
{
2427

2428
constexpr double kVLTwoPi = 6.283185307179586476925286766559;
2429

2430
bool isPowerOfTwo(int32_t value)
26✔
2431
{
2432
    return value > 0 && (value & (value - 1)) == 0;
26✔
2433
}
2434

2435
VLSuperFluxOptions superfluxDefaults()
26✔
2436
{
2437
    VLSuperFluxOptions o = {};
26✔
2438
    o.frame_size = 2048;
26✔
2439
    o.fps = 200;
26✔
2440
    o.filter_bands = 24;
26✔
2441
    o.max_bins = 3;
26✔
2442
    o.diff_frames = 0;
26✔
2443
    o.min_slice_frames = 0;
26✔
2444
    o.filter_equal = 0;
26✔
2445
    o.online = 0;
26✔
2446
    o.threshold = 1.1f;
26✔
2447
    o.combine_ms = 50.0f;
26✔
2448
    o.pre_avg = 0.15f;
26✔
2449
    o.pre_max = 0.01f;
26✔
2450
    o.post_avg = 0.0f;
26✔
2451
    o.post_max = 0.05f;
26✔
2452
    o.delay_ms = 0.0f;
26✔
2453
    o.ratio = 0.5f;
26✔
2454
    o.fmin = 30.0f;
26✔
2455
    o.fmax = 17000.0f;
26✔
2456
    o.log_mul = 1.0f;
26✔
2457
    o.log_add = 1.0f;
26✔
2458
    return o;
26✔
2459
}
2460

2461
VLError validateSuperFluxOptions(const VLSuperFluxOptions& o, int32_t sampleRate)
26✔
2462
{
2463
    if (!isPowerOfTwo(o.frame_size) || o.frame_size < 64 || o.frame_size > 32768)
26✔
NEW
2464
        return VL_ERROR_INVALID_ARG;
×
2465

2466
    if (o.fps <= 0 || o.fps > sampleRate)
26✔
NEW
2467
        return VL_ERROR_INVALID_ARG;
×
2468

2469
    if (o.filter_bands < 1 || o.max_bins < 1)
26✔
NEW
2470
        return VL_ERROR_INVALID_ARG;
×
2471

2472
    if (o.diff_frames < 0)
26✔
NEW
2473
        return VL_ERROR_INVALID_ARG;
×
2474

2475
    if (o.min_slice_frames < 0)
26✔
NEW
2476
        return VL_ERROR_INVALID_ARG;
×
2477

2478
    if (!std::isfinite(o.threshold) || !std::isfinite(o.combine_ms) || !std::isfinite(o.pre_avg) || !std::isfinite(o.pre_max) ||
52✔
2479
        !std::isfinite(o.post_avg) || !std::isfinite(o.post_max) || !std::isfinite(o.delay_ms) || !std::isfinite(o.ratio) ||
26✔
2480
        !std::isfinite(o.fmin) || !std::isfinite(o.fmax) || !std::isfinite(o.log_mul) || !std::isfinite(o.log_add))
52✔
2481
    {
NEW
2482
        return VL_ERROR_INVALID_ARG;
×
2483
    }
2484

2485
    if (o.combine_ms < 0.0f || o.pre_avg < 0.0f || o.pre_max < 0.0f || o.post_avg < 0.0f || o.post_max < 0.0f)
26✔
NEW
2486
        return VL_ERROR_INVALID_ARG;
×
2487

2488
    if (o.ratio < 0.0f || o.ratio > 1.0f)
26✔
NEW
2489
        return VL_ERROR_INVALID_ARG;
×
2490

2491
    if (o.fmin <= 0.0f || o.fmax <= o.fmin || o.log_mul <= 0.0f || o.log_add <= 0.0f)
26✔
NEW
2492
        return VL_ERROR_INVALID_ARG;
×
2493

2494
    return VL_OK;
26✔
2495
}
2496

2497
std::vector<float> makeHannWindow(int32_t frameSize)
26✔
2498
{
2499
    std::vector<float> window((size_t)frameSize, 1.0f);
26✔
2500
    if (frameSize <= 1)
26✔
NEW
2501
        return window;
×
2502

2503
    for (int32_t i = 0; i < frameSize; ++i)
53,274✔
2504
        window[(size_t)i] = (float)(0.5 - 0.5 * std::cos(kVLTwoPi * (double)i / (double)(frameSize - 1)));
53,248✔
2505

2506
    return window;
26✔
2507
}
2508

2509
std::vector<float> superfluxFrequencies(int32_t bands, float fmin, float fmax)
26✔
2510
{
2511
    std::vector<float> frequencies;
26✔
2512
    const double factor = std::pow(2.0, 1.0 / (double)bands);
26✔
2513

2514
    double freq = 440.0;
26✔
2515
    frequencies.push_back((float)freq);
26✔
2516
    while (freq <= (double)fmax)
3,328✔
2517
    {
2518
        freq *= factor;
3,302✔
2519
        frequencies.push_back((float)freq);
3,302✔
2520
    }
2521

2522
    freq = 440.0;
26✔
2523
    while (freq >= (double)fmin)
2,444✔
2524
    {
2525
        freq /= factor;
2,418✔
2526
        frequencies.push_back((float)freq);
2,418✔
2527
    }
2528

2529
    std::sort(frequencies.begin(), frequencies.end());
26✔
2530
    return frequencies;
26✔
NEW
2531
}
×
2532

2533
bool buildSuperFluxFilterbank(int32_t numFftBins,
26✔
2534
                              int32_t sampleRate,
2535
                              const VLSuperFluxOptions& options,
2536
                              std::vector<float>& filterbank,
2537
                              int32_t& numBands)
2538
{
2539
    const float fmax = std::min(options.fmax, (float)sampleRate * 0.5f);
26✔
2540
    std::vector<float> frequencies = superfluxFrequencies(options.filter_bands, options.fmin, fmax);
26✔
2541
    const double factor = ((double)sampleRate * 0.5) / (double)numFftBins;
26✔
2542

2543
    std::vector<int32_t> bins;
26✔
2544
    bins.reserve(frequencies.size());
26✔
2545
    for (float frequency : frequencies)
5,772✔
2546
    {
2547
        const int32_t bin = (int32_t)std::lround((double)frequency / factor);
5,746✔
2548
        if (bin >= 0 && bin < numFftBins)
5,746✔
2549
            bins.push_back(bin);
5,746✔
2550
    }
2551

2552
    std::sort(bins.begin(), bins.end());
26✔
2553
    bins.erase(std::unique(bins.begin(), bins.end()), bins.end());
26✔
2554
    if (bins.size() < 5)
26✔
NEW
2555
        return false;
×
2556

2557
    numBands = (int32_t)bins.size() - 2;
26✔
2558
    if (numBands < 3)
26✔
NEW
2559
        return false;
×
2560

2561
    filterbank.assign((size_t)numFftBins * (size_t)numBands, 0.0f);
26✔
2562
    for (int32_t band = 0; band < numBands; ++band)
3,692✔
2563
    {
2564
        const int32_t start = bins[(size_t)band];
3,666✔
2565
        const int32_t mid = bins[(size_t)band + 1u];
3,666✔
2566
        const int32_t stop = bins[(size_t)band + 2u];
3,666✔
2567
        if (mid <= start || stop <= mid)
3,666✔
NEW
2568
            continue;
×
2569

2570
        const float height = options.filter_equal ? 2.0f / (float)(stop - start) : 1.0f;
3,666✔
2571
        for (int32_t bin = start; bin < mid; ++bin)
23,868✔
2572
        {
2573
            const float t = (float)(bin - start) / (float)(mid - start);
20,202✔
2574
            filterbank[(size_t)bin * (size_t)numBands + (size_t)band] = t * height;
20,202✔
2575
        }
2576
        for (int32_t bin = mid; bin < stop; ++bin)
24,414✔
2577
        {
2578
            const float t = (float)(bin - mid) / (float)(stop - mid);
20,748✔
2579
            filterbank[(size_t)bin * (size_t)numBands + (size_t)band] = (1.0f - t) * height;
20,748✔
2580
        }
2581
    }
2582

2583
    return true;
26✔
2584
}
26✔
2585

2586
int32_t deriveSuperFluxDiffFrames(const std::vector<float>& window, double hopSize, float ratio)
26✔
2587
{
2588
    size_t sample = 0;
26✔
2589
    while (sample < window.size() && window[sample] <= ratio)
13,338✔
2590
        ++sample;
13,312✔
2591

2592
    const double diffSamples = (double)window.size() * 0.5 - (double)sample;
26✔
2593
    return std::max(1, (int32_t)std::lround(diffSamples / hopSize));
26✔
2594
}
2595

2596
bool computeSuperFluxActivations(const float* left,
26✔
2597
                                 const float* right,
2598
                                 int32_t channels,
2599
                                 int32_t frames,
2600
                                 int32_t sampleRate,
2601
                                 const VLSuperFluxOptions& options,
2602
                                 std::vector<float>& activations)
2603
{
2604
    const int32_t frameSize = options.frame_size;
26✔
2605
    const int32_t numFftBins = frameSize / 2;
26✔
2606
    const double hopSize = (double)sampleRate / (double)options.fps;
26✔
2607
    const int32_t numFrames = std::max(1, (int32_t)std::ceil((double)frames / hopSize));
26✔
2608

2609
    std::vector<float> window = makeHannWindow(frameSize);
26✔
2610
    std::vector<float> filterbank;
26✔
2611
    int32_t numBands = 0;
26✔
2612
    if (!buildSuperFluxFilterbank(numFftBins, sampleRate, options, filterbank, numBands))
26✔
NEW
2613
        return false;
×
2614

2615
    std::vector<float> spec((size_t)numFrames * (size_t)numBands, 0.0f);
26✔
2616
    std::vector<double> fftBuffer((size_t)frameSize);
52✔
2617
    std::vector<float> magnitudes((size_t)numFftBins);
52✔
2618
    std::vector<int> fftIp(2 + (size_t)frameSize, 0);
26✔
2619
    std::vector<double> fftW((size_t)frameSize / 2);
26✔
2620

2621
    for (int32_t frame = 0; frame < numFrames; ++frame)
17,725✔
2622
    {
2623
        const int32_t seek = options.online ? (int32_t)((double)(frame + 1) * hopSize - (double)frameSize)
17,699✔
2624
                                            : (int32_t)((double)frame * hopSize - (double)frameSize * 0.5);
17,699✔
2625

2626
        for (int32_t i = 0; i < frameSize; ++i)
36,265,251✔
2627
        {
2628
            const int32_t src = seek + i;
36,247,552✔
2629
            float sample = 0.0f;
36,247,552✔
2630
            if (src >= 0 && src < frames)
36,247,552✔
2631
            {
2632
                const float l = std::isfinite(left[src]) ? left[src] : 0.0f;
36,106,416✔
2633
                if (channels == 2)
36,106,416✔
2634
                {
2635
                    const float r = std::isfinite(right[src]) ? right[src] : 0.0f;
4,237,454✔
2636
                    sample = (l + r) * 0.5f;
4,237,454✔
2637
                }
2638
                else
2639
                {
2640
                    sample = l;
31,868,962✔
2641
                }
2642
            }
2643
            fftBuffer[(size_t)i] = (double)(sample * window[(size_t)i]);
36,247,552✔
2644
        }
2645

2646
        rdft(frameSize, 1, fftBuffer.data(), fftIp.data(), fftW.data());
17,699✔
2647

2648
        magnitudes[0] = (float)std::abs(fftBuffer[0]);
17,699✔
2649
        for (int32_t bin = 1; bin < numFftBins; ++bin)
18,123,776✔
2650
        {
2651
            const double re = fftBuffer[(size_t)bin * 2];
18,106,077✔
2652
            const double im = fftBuffer[(size_t)bin * 2 + 1];
18,106,077✔
2653
            magnitudes[(size_t)bin] = (float)std::sqrt(re * re + im * im);
18,106,077✔
2654
        }
2655

2656
        for (int32_t band = 0; band < numBands; ++band)
2,513,258✔
2657
        {
2658
            double value = 0.0;
2,495,559✔
2659
            for (int32_t bin = 0; bin < numFftBins; ++bin)
2,147,483,647✔
2660
                value += (double)magnitudes[(size_t)bin] * (double)filterbank[(size_t)bin * (size_t)numBands + (size_t)band];
2,147,483,647✔
2661

2662
            const float logged = std::log10(options.log_mul * (float)value + options.log_add);
2,495,559✔
2663
            spec[(size_t)frame * (size_t)numBands + (size_t)band] = logged;
2,495,559✔
2664
        }
2665
    }
2666

2667
    const int32_t diffFrames =
2668
        options.diff_frames > 0 ? options.diff_frames : deriveSuperFluxDiffFrames(window, hopSize, options.ratio);
26✔
2669

2670
    activations.assign((size_t)numFrames, 0.0f);
26✔
2671
    for (int32_t frame = diffFrames; frame < numFrames; ++frame)
17,673✔
2672
    {
2673
        float sum = 0.0f;
17,647✔
2674
        const int32_t prevFrame = frame - diffFrames;
17,647✔
2675
        for (int32_t band = 0; band < numBands; ++band)
2,505,874✔
2676
        {
2677
            int32_t first = band - options.max_bins / 2;
2,488,227✔
2678
            int32_t last = first + options.max_bins - 1;
2,488,227✔
2679
            if (first < 0)
2,488,227✔
2680
            {
2681
                last -= first;
17,647✔
2682
                first = 0;
17,647✔
2683
            }
2684
            if (last >= numBands)
2,488,227✔
2685
            {
2686
                first = std::max(0, first - (last - numBands + 1));
17,647✔
2687
                last = numBands - 1;
17,647✔
2688
            }
2689
            float prevMax = spec[(size_t)prevFrame * (size_t)numBands + (size_t)band];
2,488,227✔
2690
            for (int32_t k = first; k <= last; ++k)
9,952,908✔
2691
                prevMax = std::max(prevMax, spec[(size_t)prevFrame * (size_t)numBands + (size_t)k]);
7,464,681✔
2692

2693
            const float diff = spec[(size_t)frame * (size_t)numBands + (size_t)band] - prevMax;
2,488,227✔
2694
            if (diff > 0.0f)
2,488,227✔
2695
                sum += diff;
415,324✔
2696
        }
2697
        activations[(size_t)frame] = sum;
17,647✔
2698
    }
2699

2700
    return true;
26✔
2701
}
26✔
2702

2703
std::vector<double> pickSuperFluxOnsets(const std::vector<float>& activations, int32_t fps, const VLSuperFluxOptions& options)
26✔
2704
{
2705
    const int32_t count = (int32_t)activations.size();
26✔
2706
    const int32_t preAvg = std::max(0, (int32_t)std::lround((double)fps * (double)options.pre_avg));
26✔
2707
    const int32_t preMax = std::max(0, (int32_t)std::lround((double)fps * (double)options.pre_max));
26✔
2708
    const int32_t postAvg = options.online ? 0 : std::max(0, (int32_t)std::lround((double)fps * (double)options.post_avg));
26✔
2709
    const int32_t postMax = options.online ? 0 : std::max(0, (int32_t)std::lround((double)fps * (double)options.post_max));
26✔
2710
    const double combineSeconds = (double)options.combine_ms / 1000.0;
26✔
2711
    const double delaySeconds = (double)options.delay_ms / 1000.0;
26✔
2712

2713
    std::vector<double> detections;
26✔
2714
    double lastDetection = -std::numeric_limits<double>::infinity();
26✔
2715
    for (int32_t frame = 0; frame < count; ++frame)
17,725✔
2716
    {
2717
        const int32_t maxStart = std::max(0, frame - preMax);
17,699✔
2718
        const int32_t maxStop = std::min(count - 1, frame + postMax);
17,699✔
2719
        float movMax = 0.0f;
17,699✔
2720
        for (int32_t i = maxStart; i <= maxStop; ++i)
243,146✔
2721
            movMax = std::max(movMax, activations[(size_t)i]);
225,447✔
2722

2723
        const int32_t avgStart = std::max(0, frame - preAvg);
17,699✔
2724
        const int32_t avgStop = std::min(count - 1, frame + postAvg);
17,699✔
2725
        double avg = 0.0;
17,699✔
2726
        for (int32_t i = avgStart; i <= avgStop; ++i)
554,278✔
2727
            avg += activations[(size_t)i];
536,579✔
2728
        avg /= (double)(avgStop - avgStart + 1);
17,699✔
2729

2730
        const float value = activations[(size_t)frame];
17,699✔
2731
        if (value <= 0.0f || value != movMax || (double)value < avg + (double)options.threshold)
17,699✔
2732
            continue;
17,101✔
2733

2734
        const double time = (double)frame / (double)fps + delaySeconds;
598✔
2735
        if (detections.empty() || time - lastDetection > combineSeconds)
598✔
2736
        {
2737
            detections.push_back(time);
476✔
2738
            lastDetection = time;
476✔
2739
        }
2740
    }
2741

2742
    return detections;
26✔
NEW
2743
}
×
2744

2745
std::vector<uint32_t> superfluxSliceBoundaries(const std::vector<double>& detections,
26✔
2746
                                               int32_t frames,
2747
                                               int32_t sampleRate,
2748
                                               const VLSuperFluxOptions& options)
2749
{
2750
    const uint32_t combineFrames =
2751
        (uint32_t)std::max(1, (int32_t)std::lround((double)options.combine_ms / 1000.0 * (double)sampleRate));
26✔
2752
    const uint32_t minSliceFrames =
2753
        (uint32_t)(options.min_slice_frames > 0 ? (uint32_t)options.min_slice_frames : std::max(combineFrames, (uint32_t)std::max(1, (int32_t)std::lround((double)sampleRate * 0.01))));
26✔
2754

2755
    std::vector<uint32_t> boundaries;
26✔
2756
    boundaries.push_back(0);
26✔
2757

2758
    for (double detection : detections)
502✔
2759
    {
2760
        const int64_t rounded = (int64_t)std::llround(detection * (double)sampleRate);
476✔
2761
        if (rounded <= 0 || rounded >= frames)
476✔
NEW
2762
            continue;
×
2763

2764
        const uint32_t sample = (uint32_t)rounded;
476✔
2765
        if (sample - boundaries.back() >= minSliceFrames)
476✔
2766
            boundaries.push_back(sample);
457✔
2767
    }
2768

2769
    if ((uint32_t)frames - boundaries.back() < minSliceFrames && boundaries.size() > 1)
26✔
2770
        boundaries.pop_back();
2✔
2771

2772
    boundaries.push_back((uint32_t)frames);
26✔
2773
    return boundaries;
52✔
NEW
2774
}
×
2775

2776
int32_t addSliceAtSample(VLFile file, uint32_t sample_start, int32_t ppq_pos, const float* left, const float* right, int32_t frames)
1,115✔
2777
{
2778
    if (!file)
1,115✔
2779
        return (int32_t)VL_ERROR_INVALID_HANDLE;
×
2780

2781
    if (!left || frames <= 0 || ppq_pos < 0)
1,115✔
2782
        return (int32_t)VL_ERROR_INVALID_ARG;
2✔
2783

2784
    const int32_t channels = std::clamp(file->impl.info.channels, 1, 2);
1,113✔
2785
    if (channels == 2 && !right)
1,113✔
2786
        return (int32_t)VL_ERROR_INVALID_ARG;
1✔
2787

2788
    const uint32_t end = sample_start + (uint32_t)frames;
1,112✔
2789
    const uint32_t declaredFrames = file->impl.info.total_frames > 0 ? (uint32_t)file->impl.info.total_frames : 0u;
1,112✔
2790
    const uint32_t requiredFrames = std::max(end, declaredFrames);
1,112✔
2791
    const size_t required = (size_t)requiredFrames * (size_t)channels;
1,112✔
2792

2793
    if (required > file->impl.pcm.max_size())
1,112✔
2794
        return (int32_t)VL_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE max_size is not reachable in tests.
2795

2796
    if (file->impl.pcm.size() < required)
1,112✔
2797
    {
2798
        if (!file->impl.pcm.resize(required, 0))
55✔
2799
            return (int32_t)VL_ERROR_OUT_OF_MEMORY;
×
2800
    }
2801

2802
    for (int32_t f = 0; f < frames; ++f)
5,819,336✔
2803
    {
2804
        const size_t dst = ((size_t)sample_start + (size_t)f) * (size_t)channels;
5,818,224✔
2805
        file->impl.pcm[dst] = floatToPCM(left[f], file->impl.info.bit_depth);
5,818,224✔
2806
        if (channels == 2)
5,818,224✔
2807
            file->impl.pcm[dst + 1] = floatToPCM(right[f], file->impl.info.bit_depth);
589,208✔
2808
    }
2809

2810
    VLSliceEntry s;
1,112✔
2811
    s.ppq_pos = (uint32_t)ppq_pos;
1,112✔
2812
    s.sample_start = sample_start;
1,112✔
2813
    s.sample_length = (uint32_t)frames;
1,112✔
2814
    s.rendered_length = (uint32_t)frames;
1,112✔
2815
    s.points = 0x7fff;
1,112✔
2816
    s.selected_flag = 0;
1,112✔
2817
    s.state = kSliceNormal;
1,112✔
2818

2819
    file->impl.totalFrames = (uint32_t)(file->impl.pcm.size() / (size_t)channels);
1,112✔
2820
    file->impl.info.total_frames = (int32_t)file->impl.totalFrames;
1,112✔
2821
    file->impl.cacheSliceLoop(s);
1,112✔
2822

2823
    if (file->impl.slices.size() == file->impl.slices.max_size())
1,112✔
2824
        return (int32_t)VL_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE max_size is not reachable in tests.
2825
    file->impl.slices.push_back(s);
1,112✔
2826

2827
    file->impl.info.slice_count = (int32_t)file->impl.slices.size();
1,112✔
2828
    if (file->impl.info.loop_end <= file->impl.info.loop_start)
1,112✔
2829
        file->impl.info.loop_end = file->impl.info.total_frames;
9✔
2830

2831
    return (int32_t)file->impl.slices.size() - 1;
1,112✔
2832
}
2833

2834
} // anonymous namespace
2835

2836
/* -----------------------------------------------------------------------
2837
   Open / close
2838
   ----------------------------------------------------------------------- */
2839

2840
VLFile vl_open(const char* path, VLError* err)
95✔
2841
{
2842
    auto set = [&](VLError e)
2✔
2843
    {
2844
        if (err)
2✔
2845
            *err = e;
2✔
2846
    };
97✔
2847

2848
    if (!path)
95✔
2849
    {
2850
        set(VL_ERROR_INVALID_ARG);
1✔
2851
        return nullptr;
1✔
2852
    }
2853

2854
    std::ifstream f(path, std::ios::binary | std::ios::ate);
94✔
2855
    if (!f)
94✔
2856
    {
2857
        set(VL_ERROR_FILE_NOT_FOUND);
1✔
2858
        return nullptr;
1✔
2859
    }
2860

2861
    const std::streamsize sz = f.tellg();
93✔
2862
    f.seekg(0);
93✔
2863

2864
    std::vector<char> buf((size_t)sz);
93✔
2865
    if (!f.read(buf.data(), sz))
93✔
2866
    {
2867
        set(VL_ERROR_FILE_CORRUPT);
×
2868
        return nullptr;
×
2869
    }
2870

2871
    return vl_open_from_memory(buf.data(), (size_t)sz, err);
93✔
2872
}
94✔
2873

2874
VLFile vl_open_from_memory(const void* data, size_t size, VLError* err)
194✔
2875
{
2876
    auto set = [&](VLError e)
194✔
2877
    {
2878
        if (err)
194✔
2879
            *err = e;
194✔
2880
    };
388✔
2881

2882
    if (!data || size == 0)
194✔
2883
    {
2884
        set(VL_ERROR_INVALID_ARG);
1✔
2885
        return nullptr;
1✔
2886
    }
2887

2888
    VLFile_s* h = new (std::nothrow) VLFile_s();
193✔
2889
    if (!h)
193✔
2890
    {
2891
        set(VL_ERROR_OUT_OF_MEMORY);
×
2892
        return nullptr;
×
2893
    }
2894

2895
    if (!h->impl.loadFromBuffer((const char*)data, size))
193✔
2896
    {
2897
        const VLError loadError = h->impl.loadError != VL_OK ? h->impl.loadError : VL_ERROR_FILE_CORRUPT;
93✔
2898
        delete h;
93✔
2899
        set(loadError);
93✔
2900
        return nullptr;
93✔
2901
    }
2902

2903
    set(VL_OK);
100✔
2904
    return h;
100✔
2905
}
2906

2907
VLFile vl_create_new(int32_t channels, int32_t sample_rate, int32_t tempo, VLError* err)
67✔
2908
{
2909
    auto set = [&](VLError e)
67✔
2910
    {
2911
        if (err)
67✔
2912
            *err = e;
51✔
2913
    };
134✔
2914

2915
    if (channels != 1 && channels != 2)
67✔
2916
    {
2917
        set(VL_ERROR_INVALID_ARG);
4✔
2918
        return nullptr;
4✔
2919
    }
2920

2921
    if (sample_rate < 8000 || sample_rate > 192000)
63✔
2922
    {
2923
        set(VL_ERROR_INVALID_SAMPLE_RATE);
8✔
2924
        return nullptr;
8✔
2925
    }
2926

2927
    if (tempo <= 0)
55✔
2928
    {
2929
        set(VL_ERROR_INVALID_TEMPO);
4✔
2930
        return nullptr;
4✔
2931
    }
2932

2933
    VLFile_s* h = new (std::nothrow) VLFile_s();
51✔
2934
    if (!h)
51✔
2935
    {
2936
        set(VL_ERROR_OUT_OF_MEMORY);
×
2937
        return nullptr;
×
2938
    }
2939

2940
    h->impl.info.channels = channels;
51✔
2941
    h->impl.info.sample_rate = sample_rate;
51✔
2942
    h->impl.info.slice_count = 0;
51✔
2943
    h->impl.info.tempo = tempo;
51✔
2944
    h->impl.info.original_tempo = tempo;
51✔
2945
    h->impl.info.ppq_length = VLFileImpl::kREXPPQ * 4;
51✔
2946
    h->impl.info.time_sig_num = 4;
51✔
2947
    h->impl.info.time_sig_den = 4;
51✔
2948
    h->impl.info.bit_depth = 16;
51✔
2949
    h->impl.info.total_frames = 0;
51✔
2950
    h->impl.info.loop_start = 0;
51✔
2951
    h->impl.info.loop_end = 0;
51✔
2952
    h->impl.info.processing_gain = 1000;
51✔
2953
    h->impl.info.transient_enabled = 1;
51✔
2954
    h->impl.info.transient_attack = 0;
51✔
2955
    h->impl.info.transient_decay = 1023;
51✔
2956
    h->impl.info.transient_stretch = 0;
51✔
2957
    h->impl.info.silence_selected = 0;
51✔
2958
    h->impl.processingGain = 1000;
51✔
2959
    h->impl.transientEnabled = true;
51✔
2960
    h->impl.transientAttack = 0;
51✔
2961
    h->impl.transientDecay = 1023;
51✔
2962
    h->impl.transientStretch = 0;
51✔
2963
    h->impl.silenceSelected = false;
51✔
2964

2965
    set(VL_OK);
51✔
2966
    return h;
51✔
2967
}
2968

2969
void vl_superflux_default_options(VLSuperFluxOptions* out)
9✔
2970
{
2971
    if (out)
9✔
2972
        *out = superfluxDefaults();
8✔
2973
}
9✔
2974

2975
VLFile vl_create_from_superflux(int32_t channels,
31✔
2976
                                int32_t sample_rate,
2977
                                int32_t tempo,
2978
                                const float* left,
2979
                                const float* right,
2980
                                int32_t frames,
2981
                                const VLSuperFluxOptions* options,
2982
                                VLError* err)
2983
{
2984
    auto set = [&](VLError e)
31✔
2985
    {
2986
        if (err)
31✔
2987
            *err = e;
31✔
2988
    };
62✔
2989

2990
    if (channels != 1 && channels != 2)
31✔
2991
    {
2992
        set(VL_ERROR_INVALID_ARG);
1✔
2993
        return nullptr;
1✔
2994
    }
2995

2996
    if (sample_rate < 8000 || sample_rate > 192000)
30✔
2997
    {
2998
        set(VL_ERROR_INVALID_SAMPLE_RATE);
1✔
2999
        return nullptr;
1✔
3000
    }
3001

3002
    if (tempo <= 0)
29✔
3003
    {
3004
        set(VL_ERROR_INVALID_TEMPO);
1✔
3005
        return nullptr;
1✔
3006
    }
3007

3008
    if (!left || frames <= 0 || (channels == 2 && !right))
28✔
3009
    {
3010
        set(VL_ERROR_INVALID_ARG);
2✔
3011
        return nullptr;
2✔
3012
    }
3013

3014
    VLSuperFluxOptions opts = options ? *options : superfluxDefaults();
26✔
3015
    VLError optionError = validateSuperFluxOptions(opts, sample_rate);
26✔
3016
    if (optionError != VL_OK)
26✔
3017
    {
NEW
3018
        set(optionError);
×
NEW
3019
        return nullptr;
×
3020
    }
3021

3022
    try
3023
    {
3024
        std::vector<float> activations;
26✔
3025
        if (!computeSuperFluxActivations(left, right, channels, frames, sample_rate, opts, activations))
26✔
3026
        {
NEW
3027
            set(VL_ERROR_INVALID_ARG);
×
NEW
3028
            return nullptr;
×
3029
        }
3030

3031
        const std::vector<double> detections = pickSuperFluxOnsets(activations, opts.fps, opts);
26✔
3032
        const std::vector<uint32_t> boundaries = superfluxSliceBoundaries(detections, frames, sample_rate, opts);
26✔
3033

3034
        VLError createError = VL_OK;
26✔
3035
        VLFile file = vl_create_new(channels, sample_rate, tempo, &createError);
26✔
3036
        if (!file)
26✔
3037
        {
NEW
3038
            set(createError);
×
NEW
3039
            return nullptr;
×
3040
        }
3041

3042
        VLFileInfo info = {};
26✔
3043
        if (vl_get_info(file, &info) != VL_OK)
26✔
3044
        {
NEW
3045
            vl_close(file);
×
3046
            set(VL_ERROR_INVALID_HANDLE); // LCOV_EXCL_LINE freshly-created handles are valid.
NEW
3047
            return nullptr;
×
3048
        }
3049

3050
        const double beats = ((double)frames * (double)tempo) / (60000.0 * (double)sample_rate);
26✔
3051
        info.ppq_length = std::max(1, (int32_t)std::lround(beats * (double)VLFileImpl::kREXPPQ));
26✔
3052
        info.total_frames = frames;
26✔
3053
        info.loop_start = 0;
26✔
3054
        info.loop_end = frames;
26✔
3055
        info.transient_enabled = 0;
26✔
3056
        info.transient_stretch = 0;
26✔
3057
        info.transient_attack = 0;
26✔
3058
        info.transient_decay = 1023;
26✔
3059
        VLError infoError = vl_set_info(file, &info);
26✔
3060
        if (infoError != VL_OK)
26✔
3061
        {
NEW
3062
            vl_close(file);
×
NEW
3063
            set(infoError);
×
NEW
3064
            return nullptr;
×
3065
        }
3066

3067
        int32_t previousPpq = -1;
26✔
3068
        for (size_t i = 0; i + 1 < boundaries.size(); ++i)
507✔
3069
        {
3070
            const uint32_t start = boundaries[i];
481✔
3071
            const uint32_t stop = boundaries[i + 1u];
481✔
3072
            if (stop <= start)
481✔
NEW
3073
                continue;
×
3074

3075
            int32_t ppq = (int32_t)(((uint64_t)start * (uint64_t)info.ppq_length + (uint32_t)frames / 2u) / (uint32_t)frames);
481✔
3076
            if (ppq <= previousPpq)
481✔
NEW
3077
                ppq = previousPpq + 1;
×
3078
            ppq = std::min(ppq, std::max(0, info.ppq_length - 1));
481✔
3079
            previousPpq = ppq;
481✔
3080

3081
            const int32_t sliceIndex =
3082
                addSliceAtSample(file, start, ppq, left + start, channels == 2 ? right + start : nullptr, (int32_t)(stop - start));
481✔
3083
            if (sliceIndex < 0)
481✔
3084
            {
NEW
3085
                const VLError sliceError = (VLError)sliceIndex;
×
NEW
3086
                vl_close(file);
×
NEW
3087
                set(sliceError);
×
NEW
3088
                return nullptr;
×
3089
            }
3090
        }
3091

3092
        if (file->impl.slices.empty())
26✔
3093
        {
NEW
3094
            vl_close(file);
×
3095
            set(VL_ERROR_INVALID_ARG); // LCOV_EXCL_LINE boundaries always create at least one slice for frames > 0.
NEW
3096
            return nullptr;
×
3097
        }
3098

3099
        set(VL_OK);
26✔
3100
        return file;
26✔
3101
    }
26✔
NEW
3102
    catch (const std::bad_alloc&)
×
3103
    {
NEW
3104
        set(VL_ERROR_OUT_OF_MEMORY);
×
NEW
3105
        return nullptr;
×
NEW
3106
    }
×
NEW
3107
    catch (...)
×
3108
    {
NEW
3109
        set(VL_ERROR_OUT_OF_MEMORY);
×
NEW
3110
        return nullptr;
×
NEW
3111
    }
×
3112
}
3113

3114
void vl_close(VLFile file)
209✔
3115
{
3116
    delete file;
209✔
3117
}
209✔
3118

3119
/* -----------------------------------------------------------------------
3120
   Read: metadata
3121
   ----------------------------------------------------------------------- */
3122

3123
VLError vl_get_info(VLFile file, VLFileInfo* out)
162✔
3124
{
3125
    if (!file)
162✔
3126
        return VL_ERROR_INVALID_HANDLE;
1✔
3127

3128
    if (!out)
161✔
3129
        return VL_ERROR_INVALID_ARG;
16✔
3130

3131
    *out = file->impl.info;
145✔
3132
    return VL_OK;
145✔
3133
}
3134

3135
VLError vl_get_creator_info(VLFile file, VLCreatorInfo* out)
35✔
3136
{
3137
    if (!file)
35✔
3138
        return VL_ERROR_INVALID_HANDLE;
1✔
3139

3140
    if (!out)
34✔
3141
        return VL_ERROR_INVALID_ARG;
16✔
3142

3143
    const VLCreatorInfo& src = file->impl.creator;
18✔
3144
    if (!src.name[0] && !src.copyright[0] && !src.url[0] && !src.email[0] && !src.free_text[0])
18✔
3145
        return VL_ERROR_NO_CREATOR_INFO;
16✔
3146

3147
    *out = src;
2✔
3148
    return VL_OK;
2✔
3149
}
3150

3151
/* -----------------------------------------------------------------------
3152
   Read: slice enumeration
3153
   ----------------------------------------------------------------------- */
3154

3155
VLError vl_get_slice_info(VLFile file, int32_t index, VLSliceInfo* out)
2,242✔
3156
{
3157
    if (!file)
2,242✔
3158
        return VL_ERROR_INVALID_HANDLE;
1✔
3159

3160
    if (!out)
2,241✔
3161
        return VL_ERROR_INVALID_ARG;
32✔
3162

3163
    if (index < 0 || (size_t)index >= file->impl.slices.size())
2,209✔
3164
        return VL_ERROR_INVALID_SLICE;
32✔
3165

3166
    const VLSliceEntry& s = file->impl.slices[(size_t)index];
2,177✔
3167
    out->ppq_pos = (int32_t)s.ppq_pos;
2,177✔
3168
    out->sample_length = (int32_t)s.sample_length;
2,177✔
3169
    out->sample_start = (int32_t)s.sample_start;
2,177✔
3170
    out->analysis_points = (int32_t)s.points;
2,177✔
3171
    out->flags = sliceFlags(s);
2,177✔
3172
    return VL_OK;
2,177✔
3173
}
3174

3175
VLError vl_set_slice_info(VLFile file, int32_t index, int32_t flags, int32_t analysis_points)
39✔
3176
{
3177
    if (!file)
39✔
3178
        return VL_ERROR_INVALID_HANDLE;
1✔
3179

3180
    if (index < 0 || (size_t)index >= file->impl.slices.size())
38✔
3181
        return VL_ERROR_INVALID_SLICE;
32✔
3182

3183
    VLError err = applySliceFlags(file->impl.slices[(size_t)index], flags, analysis_points);
6✔
3184
    if (err != VL_OK)
6✔
3185
        return err;
3✔
3186

3187
    return VL_OK;
3✔
3188
}
3189

3190
/* -----------------------------------------------------------------------
3191
   Read: sample extraction
3192
   ----------------------------------------------------------------------- */
3193

3194
int32_t vl_get_slice_frame_count(VLFile file, int32_t index)
1,573✔
3195
{
3196
    if (!file)
1,573✔
3197
        return (int32_t)VL_ERROR_INVALID_HANDLE;
1✔
3198

3199
    if (index < 0 || (size_t)index >= file->impl.slices.size())
1,572✔
3200
        return (int32_t)VL_ERROR_INVALID_SLICE;
34✔
3201

3202
    return (int32_t)file->impl.slices[(size_t)index].rendered_length;
1,538✔
3203
}
3204

3205
VLError vl_decode_slice(VLFile file, int32_t index, float* left, float* right, int32_t frame_offset, int32_t capacity, int32_t* frames_out)
1,588✔
3206
{
3207
    if (!file)
1,588✔
3208
        return VL_ERROR_INVALID_HANDLE;
1✔
3209

3210
    if (!left)
1,587✔
3211
        return VL_ERROR_INVALID_ARG;
14✔
3212

3213
    if (frame_offset < 0)
1,573✔
3214
        return VL_ERROR_INVALID_ARG;
14✔
3215

3216
    if (index < 0 || (size_t)index >= file->impl.slices.size())
1,559✔
3217
        return VL_ERROR_INVALID_SLICE;
28✔
3218

3219
    const VLSliceEntry& s = file->impl.slices[(size_t)index];
1,531✔
3220
    const int32_t renderedFrames = (int32_t)s.rendered_length;
1,531✔
3221
    if (frame_offset > renderedFrames)
1,531✔
3222
        return VL_ERROR_INVALID_ARG;
14✔
3223

3224
    const int32_t needed = renderedFrames - frame_offset;
1,517✔
3225
    if (capacity < needed)
1,517✔
3226
        return VL_ERROR_BUFFER_TOO_SMALL;
15✔
3227

3228
    const auto& pcm = file->impl.pcm;
1,502✔
3229
    if (pcm.empty())
1,502✔
3230
        return VL_ERROR_FILE_CORRUPT;
×
3231

3232
    const uint32_t requestedFrames = (uint32_t)renderedFrames;
1,502✔
3233
    uint32_t sourceStart = s.sample_start;
1,502✔
3234
    if (sourceStart >= file->impl.totalFrames)
1,502✔
3235
    {
3236
        std::fill(left, left + needed, 0.f);
1✔
3237

3238
        if (right)
1✔
3239
            std::fill(right, right + needed, 0.f);
1✔
3240

3241
        if (frames_out)
1✔
3242
            *frames_out = needed;
1✔
3243

3244
        return VL_OK;
1✔
3245
    }
3246

3247
    uint32_t sourceFrames = s.sample_length;
1,501✔
3248
    if (file->impl.loopEnd > file->impl.loopStart && sourceStart < file->impl.loopEnd)
1,501✔
3249
        sourceFrames = std::min<uint32_t>(sourceFrames, file->impl.loopEnd - sourceStart);
1,265✔
3250
    sourceFrames = std::min<uint32_t>(sourceFrames, file->impl.totalFrames - sourceStart);
1,501✔
3251

3252
    const uint32_t sourceEnd = sourceStart + sourceFrames;
1,501✔
3253
    const VLFileImpl::SegmentLoop segmentLoop{
3254
        s.render_loop_start,
1,501✔
3255
        s.render_loop_end,
1,501✔
3256
        s.render_loop_volume_compensation,
1,501✔
3257
    };
1,501✔
3258
    const int32_t ch = std::max(1, file->impl.info.channels);
1,501✔
3259
    const float samplerGain = (float)file->impl.processingGain * 0.000833333354f;
1,501✔
3260

3261
    uint32_t samplePos = std::min(sourceStart + 2u, sourceEnd);
1,501✔
3262
    int loopPhase = 0; // 0: forward source, 1: forward loop, 2: backward loop
1,501✔
3263
    bool stretchPhase = false;
1,501✔
3264
    float stretchEnv = 1.0f;
1,501✔
3265
    const uint32_t stretchFrameCount =
3266
        std::max<uint32_t>(1u, sourceEnd - segmentLoop.end + (requestedFrames > sourceFrames ? requestedFrames - sourceFrames : 0u));
1,501✔
3267
    const float stretchEnvDec = 1.0f / (float)stretchFrameCount;
1,501✔
3268
    float loopLevelComp = 1.0f;
1,501✔
3269
    const float loopLevelCompInc =
1,501✔
3270
        (segmentLoop.end > segmentLoop.start) ? (1.0f - segmentLoop.volumeCompensation) / (float)(segmentLoop.end - segmentLoop.start) : 0.0f;
1,501✔
3271

3272
    for (uint32_t f = 0; f < requestedFrames; ++f)
5,986,316✔
3273
    {
3274
        const size_t src = (size_t)samplePos * (size_t)ch;
5,984,815✔
3275
        float l = 0.f;
5,984,815✔
3276
        float r = 0.f;
5,984,815✔
3277

3278
        if (src < pcm.size())
5,984,815✔
3279
        {
3280
            const float level = samplerGain * stretchEnv * loopLevelComp;
5,984,815✔
3281
            l = pcmToFloat(pcm[src], file->impl.info.bit_depth) * level;
5,984,815✔
3282
            r = (ch >= 2 && src + 1 < pcm.size()) ? pcmToFloat(pcm[src + 1], file->impl.info.bit_depth) * level : l;
5,984,815✔
3283
        }
3284

3285
        if ((int32_t)f >= frame_offset)
5,984,815✔
3286
        {
3287
            const size_t dst = (size_t)((int32_t)f - frame_offset);
5,970,771✔
3288
            left[dst] = l;
5,970,771✔
3289
            if (right)
5,970,771✔
3290
                right[dst] = r;
674,441✔
3291
        }
3292

3293
        if (stretchPhase)
5,984,815✔
3294
        {
3295
            stretchEnv = std::max(0.0f, stretchEnv - stretchEnvDec);
709,283✔
3296
            if (loopPhase == 1)
709,283✔
3297
                loopLevelComp += loopLevelCompInc;
66,954✔
3298
            else if (loopPhase == 2)
642,329✔
3299
                loopLevelComp -= loopLevelCompInc;
642,329✔
3300
        }
3301

3302
        if (loopPhase <= 1)
5,984,815✔
3303
        {
3304
            ++samplePos;
5,342,486✔
3305
            if (samplePos >= segmentLoop.end)
5,342,486✔
3306
            {
3307
                stretchPhase = true;
1,503✔
3308
                loopPhase = 2;
1,503✔
3309
                if (samplePos > 0)
1,503✔
3310
                    --samplePos;
1,503✔
3311
                if (samplePos <= segmentLoop.start)
1,503✔
3312
                    loopPhase = 1;
×
3313
            }
3314
        }
3315
        else
3316
        {
3317
            if (samplePos > 0)
642,329✔
3318
                --samplePos;
642,329✔
3319
            if (samplePos <= segmentLoop.start)
642,329✔
3320
                loopPhase = 1;
33✔
3321
        }
3322
    }
3323

3324
    if (frames_out)
1,501✔
3325
        *frames_out = needed;
1,501✔
3326

3327
    return VL_OK;
1,501✔
3328
}
3329

3330
/* -----------------------------------------------------------------------
3331
   Write: assembly from audio slices
3332
   ----------------------------------------------------------------------- */
3333

3334
VLError vl_set_info(VLFile file, const VLFileInfo* info)
54✔
3335
{
3336
    if (!file)
54✔
3337
        return VL_ERROR_INVALID_HANDLE;
1✔
3338

3339
    if (!info)
53✔
3340
        return VL_ERROR_INVALID_ARG;
1✔
3341

3342
    if (!file->impl.slices.empty() || !file->impl.pcm.empty())
52✔
3343
        return VL_ERROR_ALREADY_HAS_DATA;
1✔
3344

3345
    if (info->channels != 1 && info->channels != 2)
51✔
3346
        return VL_ERROR_INVALID_ARG;
×
3347

3348
    if (info->sample_rate < 8000 || info->sample_rate > 192000)
51✔
3349
        return VL_ERROR_INVALID_SAMPLE_RATE;
1✔
3350

3351
    if (info->tempo <= 0)
50✔
3352
        return VL_ERROR_INVALID_TEMPO;
1✔
3353

3354
    file->impl.info = *info;
49✔
3355
    file->impl.info.channels = info->channels;
49✔
3356
    file->impl.info.sample_rate = info->sample_rate;
49✔
3357
    file->impl.info.bit_depth = storageBitDepth(info->bit_depth);
49✔
3358
    file->impl.processingGain = (uint16_t)std::clamp(info->processing_gain > 0 ? info->processing_gain : 1000, 0, 1000);
49✔
3359
    file->impl.transientEnabled = info->transient_enabled != 0;
49✔
3360
    file->impl.transientAttack = (uint16_t)std::clamp(info->transient_attack, 0, 1023);
49✔
3361
    file->impl.transientDecay = (uint16_t)std::clamp(info->transient_decay > 0 ? info->transient_decay : 1023, 0, 1023);
49✔
3362
    file->impl.transientStretch = (uint16_t)std::clamp(info->transient_stretch, 0, 100);
49✔
3363
    file->impl.silenceSelected = info->silence_selected != 0;
49✔
3364

3365
    return VL_OK;
49✔
3366
}
3367

3368
VLError vl_set_creator_info(VLFile file, const VLCreatorInfo* info)
6✔
3369
{
3370
    if (!file)
6✔
3371
        return VL_ERROR_INVALID_HANDLE;
1✔
3372

3373
    if (!info)
5✔
3374
        return VL_ERROR_INVALID_ARG;
1✔
3375

3376
    if (!file->impl.slices.empty() || !file->impl.pcm.empty())
4✔
3377
        return VL_ERROR_ALREADY_HAS_DATA;
1✔
3378

3379
    file->impl.creator = *info;
3✔
3380
    file->impl.creator.name[sizeof(file->impl.creator.name) - 1] = '\0';
3✔
3381
    file->impl.creator.copyright[sizeof(file->impl.creator.copyright) - 1] = '\0';
3✔
3382
    file->impl.creator.url[sizeof(file->impl.creator.url) - 1] = '\0';
3✔
3383
    file->impl.creator.email[sizeof(file->impl.creator.email) - 1] = '\0';
3✔
3384
    file->impl.creator.free_text[sizeof(file->impl.creator.free_text) - 1] = '\0';
3✔
3385

3386
    return VL_OK;
3✔
3387
}
3388

3389
int32_t vl_add_slice(VLFile file, int32_t ppq_pos, const float* left, const float* right, int32_t frames)
636✔
3390
{
3391
    if (!file)
636✔
3392
        return (int32_t)VL_ERROR_INVALID_HANDLE;
1✔
3393

3394
    if (ppq_pos < 0)
635✔
3395
        return (int32_t)VL_ERROR_INVALID_ARG;
1✔
3396

3397
    const uint32_t start = calcSampleStartFromPPQ(file->impl, (uint32_t)ppq_pos);
634✔
3398
    return addSliceAtSample(file, start, ppq_pos, left, right, frames);
634✔
3399
}
3400

3401
VLError vl_remove_slice(VLFile file, int32_t index)
4✔
3402
{
3403
    if (!file)
4✔
3404
        return VL_ERROR_INVALID_HANDLE;
1✔
3405

3406
    if (index < 0 || (size_t)index >= file->impl.slices.size())
3✔
3407
        return VL_ERROR_INVALID_SLICE;
2✔
3408

3409
    file->impl.slices.erase(file->impl.slices.begin() + index);
1✔
3410
    file->impl.info.slice_count = (int32_t)file->impl.slices.size();
1✔
3411

3412
    return VL_OK;
1✔
3413
}
3414

3415
VLError vl_save(VLFile file, const char* path)
5✔
3416
{
3417
    if (!file)
5✔
3418
        return VL_ERROR_INVALID_HANDLE;
1✔
3419

3420
    if (!path)
4✔
3421
        return VL_ERROR_INVALID_ARG;
1✔
3422

3423
    size_t size = 0;
3✔
3424
    VLError e = vl_save_to_memory(file, nullptr, &size);
3✔
3425
    if (e != VL_OK)
3✔
3426
        return e;
1✔
3427

3428
    std::vector<uint8_t> buf(size);
2✔
3429
    e = vl_save_to_memory(file, buf.data(), &size);
2✔
3430
    if (e != VL_OK)
2✔
3431
        return e;
×
3432

3433
    std::ofstream out(path, std::ios::binary);
2✔
3434
    if (!out)
2✔
3435
        return VL_ERROR_INVALID_ARG;
1✔
3436

3437
    out.write((const char*)buf.data(), (std::streamsize)size);
1✔
3438
    return out ? VL_OK : VL_ERROR_FILE_CORRUPT;
1✔
3439
}
2✔
3440

3441
VLError vl_save_to_memory(VLFile file, void* buf, size_t* size_out)
135✔
3442
{
3443
    if (!file)
135✔
3444
        return VL_ERROR_INVALID_HANDLE;
1✔
3445

3446
    if (!size_out)
134✔
3447
        return VL_ERROR_INVALID_ARG;
16✔
3448

3449
    if (file->impl.pcm.empty())
118✔
3450
        return VL_ERROR_INVALID_ARG;
2✔
3451

3452
    std::vector<uint8_t> encoded = buildREX2File(file->impl);
116✔
3453

3454
    if (!buf)
116✔
3455
    {
3456
        *size_out = encoded.size();
43✔
3457
        return VL_OK;
43✔
3458
    }
3459

3460
    if (*size_out < encoded.size())
73✔
3461
    {
3462
        *size_out = encoded.size();
30✔
3463
        return VL_ERROR_BUFFER_TOO_SMALL;
30✔
3464
    }
3465

3466
    std::memcpy(buf, encoded.data(), encoded.size());
43✔
3467
    *size_out = encoded.size();
43✔
3468
    return VL_OK;
43✔
3469
}
116✔
3470

3471
/* -----------------------------------------------------------------------
3472
   Utility
3473
   ----------------------------------------------------------------------- */
3474

3475
const char* vl_error_string(VLError err)
17✔
3476
{
3477
    switch (static_cast<int32_t>(err))
17✔
3478
    {
3479
        case static_cast<int32_t>(VL_OK): return "OK";
1✔
3480
        case static_cast<int32_t>(VL_ERROR_INVALID_HANDLE): return "invalid handle";
1✔
3481
        case static_cast<int32_t>(VL_ERROR_INVALID_ARG): return "invalid argument";
1✔
3482
        case static_cast<int32_t>(VL_ERROR_FILE_NOT_FOUND): return "file not found";
1✔
3483
        case static_cast<int32_t>(VL_ERROR_FILE_CORRUPT): return "file corrupt or unsupported format";
1✔
3484
        case static_cast<int32_t>(VL_ERROR_OUT_OF_MEMORY): return "out of memory";
1✔
3485
        case static_cast<int32_t>(VL_ERROR_INVALID_SLICE): return "invalid slice index";
1✔
3486
        case static_cast<int32_t>(VL_ERROR_INVALID_SAMPLE_RATE): return "invalid sample rate";
1✔
3487
        case static_cast<int32_t>(VL_ERROR_BUFFER_TOO_SMALL): return "buffer too small";
1✔
3488
        case static_cast<int32_t>(VL_ERROR_NO_CREATOR_INFO): return "no creator info available";
1✔
3489
        case static_cast<int32_t>(VL_ERROR_NOT_IMPLEMENTED): return "not implemented";
1✔
3490
        case static_cast<int32_t>(VL_ERROR_ALREADY_HAS_DATA): return "already has data";
1✔
3491
        case static_cast<int32_t>(VL_ERROR_FILE_TOO_NEW): return "file too new";
1✔
3492
        case static_cast<int32_t>(VL_ERROR_ZERO_LOOP_LENGTH): return "zero loop length";
1✔
3493
        case static_cast<int32_t>(VL_ERROR_INVALID_SIZE): return "invalid size";
1✔
3494
        case static_cast<int32_t>(VL_ERROR_INVALID_TEMPO): return "invalid tempo";
1✔
3495
        default: return "unknown error";
1✔
3496
    }
3497
}
3498

3499
const char* vl_version_string(void)
1✔
3500
{
3501
    return "0.2.0";
1✔
3502
}
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