File size: 4,957 Bytes
3ce1a40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include "audio.h"
#include <cstring>

namespace unify {

static uint32_t rd32(const uint8_t* p) { return uint32_t(p[0]) | uint32_t(p[1]) << 8 | uint32_t(p[2]) << 16 | uint32_t(p[3]) << 24; }
static uint16_t rd16(const uint8_t* p) { return uint16_t(p[0] | p[1] << 8); }

bool parse_wav_header(FILE* f, WavInfo& info) {
    uint8_t hdr[12];
    if (std::fread(hdr, 1, 12, f) != 12 || std::memcmp(hdr, "RIFF", 4) || std::memcmp(hdr + 8, "WAVE", 4)) return false;
    bool have_fmt = false, have_data = false;
    uint8_t ch[8];
    while (std::fread(ch, 1, 8, f) == 8) {
        uint32_t size = rd32(ch + 4);
        long body = std::ftell(f);
        if (!std::memcmp(ch, "fmt ", 4)) {
            uint8_t fmt[16];
            if (size < 16 || std::fread(fmt, 1, 16, f) != 16) return false;
            if (rd16(fmt) != 1) return false;  // PCM only
            info.channels = rd16(fmt + 2);
            info.sample_rate = rd32(fmt + 4);
            info.bits = rd16(fmt + 14);
            have_fmt = true;
        } else if (!std::memcmp(ch, "data", 4)) {
            info.data_offset = uint64_t(body);
            if (have_fmt && info.channels) info.frames = size / (uint32_t(info.channels) * (info.bits / 8));
            have_data = true;
        } else if (!std::memcmp(ch, "smpl", 4) && size >= 36 + 24) {
            std::vector<uint8_t> s(size);
            if (std::fread(s.data(), 1, size, f) != size) return false;
            if (rd32(&s[28]) > 0) {  // num sample loops
                info.loop_start = rd32(&s[36 + 8]);
                info.loop_end = int64_t(rd32(&s[36 + 12])) + 1;  // 'end' is inclusive in the spec
            }
        }
        std::fseek(f, body + long(size + (size & 1)), SEEK_SET);
    }
    return have_fmt && have_data && info.bits == 16 && (info.channels == 1 || info.channels == 2);
}

WavDecoder::~WavDecoder() { if (file_) std::fclose(file_); }

bool WavDecoder::open(const std::string& path) {
    if (file_) std::fclose(file_);
    file_ = std::fopen(path.c_str(), "rb");
    if (!file_ || !parse_wav_header(file_, info_)) { if (file_) std::fclose(file_); file_ = nullptr; return false; }
    return seek(0);
}

bool WavDecoder::seek(uint32_t frame) {
    if (!file_) return false;
    if (frame > info_.frames) frame = info_.frames;
    pos_ = frame;
    return std::fseek(file_, long(info_.data_offset + uint64_t(frame) * info_.channels * 2), SEEK_SET) == 0;
}

uint32_t WavDecoder::read(float* out, uint32_t frames) {
    if (!file_) return 0;
    uint32_t n = std::min(frames, info_.frames - pos_);
    scratch_.resize(size_t(n) * info_.channels);
    size_t got = std::fread(scratch_.data(), 2 * info_.channels, n, file_);
    for (size_t i = 0; i < got; i++) {
        if (info_.channels == 2) {
            out[2 * i] = scratch_[2 * i] / 32768.0f;
            out[2 * i + 1] = scratch_[2 * i + 1] / 32768.0f;
        } else {
            out[2 * i] = out[2 * i + 1] = scratch_[i] / 32768.0f;
        }
    }
    pos_ += uint32_t(got);
    return uint32_t(got);
}

bool write_wav(const std::string& path, const std::vector<int16_t>& pcm, uint16_t channels, uint32_t rate, int64_t loop_start, int64_t loop_end) {
    FILE* f = std::fopen(path.c_str(), "wb");
    if (!f) return false;
    auto w32 = [&](uint32_t v) { uint8_t b[4] = {uint8_t(v), uint8_t(v >> 8), uint8_t(v >> 16), uint8_t(v >> 24)}; std::fwrite(b, 1, 4, f); };
    auto w16 = [&](uint16_t v) { uint8_t b[2] = {uint8_t(v), uint8_t(v >> 8)}; std::fwrite(b, 1, 2, f); };
    uint32_t data_bytes = uint32_t(pcm.size() * 2);
    bool has_loop = loop_start >= 0 && loop_end > loop_start;
    uint32_t smpl_size = has_loop ? 36 + 24 : 0;
    std::fwrite("RIFF", 1, 4, f);
    w32(4 + (8 + 16) + (8 + data_bytes) + (has_loop ? 8 + smpl_size : 0));
    std::fwrite("WAVE", 1, 4, f);
    std::fwrite("fmt ", 1, 4, f); w32(16);
    w16(1); w16(channels); w32(rate); w32(rate * channels * 2); w16(uint16_t(channels * 2)); w16(16);
    if (has_loop) {
        std::fwrite("smpl", 1, 4, f); w32(smpl_size);
        for (int i = 0; i < 7; i++) w32(i == 2 ? 1000000000u / rate : 0);  // manufacturer..SMPTE; [2]=sample period ns
        w32(1); w32(0);                                                    // num loops, sampler data
        w32(0); w32(0); w32(uint32_t(loop_start)); w32(uint32_t(loop_end - 1)); w32(0); w32(0);
    }
    std::fwrite("data", 1, 4, f); w32(data_bytes);
    std::fwrite(pcm.data(), 2, pcm.size(), f);
    return std::fclose(f) == 0;
}

bool AudioClip::load_wav(const std::string& path) {
    WavDecoder d;
    if (!d.open(path)) return false;
    sample_rate = d.info().sample_rate;
    samples.resize(size_t(d.info().frames) * 2);
    uint32_t got = d.read(samples.data(), d.info().frames);
    samples.resize(size_t(got) * 2);
    loop_start = d.info().loop_start >= 0 ? uint32_t(d.info().loop_start) : 0;
    loop_end = d.info().loop_end > 0 ? uint32_t(d.info().loop_end) : 0;
    return true;
}

}  // namespace unify