aboutsummaryrefslogtreecommitdiff
path: root/src/3rdparty/wav/wav.c
blob: 6f9b2abed05263923778a21beaf263f2779b8914 (plain)
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
/**
* Copyright (c) 2018 chai
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See LICENSE for details.
*/

#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include "wav.h"

typedef unsigned short Uint16;
typedef unsigned int   Uint32;

static const char *findSubChunk(
    const char *data, size_t len, const char *id, size_t *size
) {
    /* TODO : Error handling on malformed wav file */
    size_t idLen = strlen(id);
    const char *p = data + 12;
next:
    *size = *((Uint32*)(p + 4));
    if (memcmp(p, id, idLen)) {
        p += 8 + *size;
        if (p > data + len) return NULL;
        goto next;
    }
    return p + 8;
}

int wav_read(wav_t *w, const void *data, size_t len) {
    int bitdepth, channels, samplerate, format;
    size_t sz;
    const char *p = (const char*)data;
    memset(w, 0, sizeof(*w));
    /* Check header */
    if (memcmp(p, "RIFF", 4) || memcmp(p + 8, "WAVE", 4)) {
        return WAV_EBADHEADER;
    }
    /* Find fmt subchunk */
    p = findSubChunk((const char*)data, len, "fmt", &sz);
    if (!p) return WAV_ENOFMT;
    /* Load fmt info */
    format = *((Uint16*)(p));
    channels = *((Uint16*)(p + 2));
    samplerate = *((Uint32*)(p + 4));
    bitdepth = *((Uint16*)(p + 14));
    if (format != 1) {
        return WAV_ENOSUPPORT;
    }
    if (channels == 0 || samplerate == 0 || bitdepth == 0) {
        return WAV_EBADFMT;
    }
    /* Find data subchunk */
    p = findSubChunk((const char*)data, len, "data", &sz);
    if (!p) return WAV_ENODATA;
    /* copy p to new buffer */
    char* buffer = (char*)malloc(sz);
    memmove(buffer, p, sz);
    /* Init wav_t struct */
    w->data = buffer;
    w->samplerate = samplerate;
    w->channels = channels;
    w->length = (sz / (bitdepth / 8)) / channels;
    w->bitdepth = bitdepth;
    /* Done! */
    return WAV_ESUCCESS;
}

const char *wav_strerror(int err) {
    switch (err) {
    case WAV_ESUCCESS: return "success";
    case WAV_EFAILURE: return "failure";
    case WAV_EBADHEADER: return "bad header data";
    case WAV_EBADFMT: return "bad fmt data";
    case WAV_ENOFMT: return "missing 'fmt' subchunk";
    case WAV_ENODATA: return "missing 'data' subchunk";
    case WAV_ENOSUPPORT: return "unsupported format; "
        "expected uncompressed PCM";
    }
    return "unknown error";
}