Login
1 branch 0 tags
Ben (Desktop/Arch) Reorganized things 6d669c1 1 month ago 48 Commits
moon / src / metadata / wav.c
#include "../audio_metadata.h"
#include "helper.h"

#include <stdio.h>
#include <string.h>

bool parse_wav(const char *path, audio_metadata_t *meta) {
    FILE *f = fopen(path, "rb");
    if (!f) return false;

    // Read RIFF header (12 bytes)
    uint8_t header[12];
    if (fread(header, 1, 12, f) != 12) {
        fclose(f);
        return false;
    }

    // Verify "RIFF" and "WAVE"
    if (memcmp(header, "RIFF", 4) != 0 || memcmp(header + 8, "WAVE", 4) != 0) {
        fclose(f);
        return false;
    }

    uint32_t byte_rate = 0;
    uint32_t data_size = 0;
    bool got_fmt = false;
    bool got_data = false;

    // Parse sub-chunks
    while (!got_fmt || !got_data) {
        uint8_t chunk_header[8];
        if (fread(chunk_header, 1, 8, f) != 8) break;

        char chunk_id[5] = {chunk_header[0], chunk_header[1], chunk_header[2], chunk_header[3], 0};
        uint32_t chunk_size = (uint32_t)chunk_header[4] |
                              ((uint32_t)chunk_header[5] << 8) |
                              ((uint32_t)chunk_header[6] << 16) |
                              ((uint32_t)chunk_header[7] << 24);

        if (strcmp(chunk_id, "fmt ") == 0) {
            if (chunk_size < 16) break;
            uint8_t fmt[16];
            if (fread(fmt, 1, 16, f) != 16) break;

            meta->channels = (uint8_t)(fmt[2] | (fmt[3] << 8));
            meta->sample_rate = (uint32_t)fmt[4] | ((uint32_t)fmt[5] << 8) |
                                ((uint32_t)fmt[6] << 16) | ((uint32_t)fmt[7] << 24);
            byte_rate = (uint32_t)fmt[8] | ((uint32_t)fmt[9] << 8) |
                        ((uint32_t)fmt[10] << 16) | ((uint32_t)fmt[11] << 24);

            // Skip remaining fmt data
            if (chunk_size > 16) {
                fseek(f, chunk_size - 16, SEEK_CUR);
            }
            got_fmt = true;
        } else if (strcmp(chunk_id, "data") == 0) {
            data_size = chunk_size;
            got_data = true;
            // Don't need to read data, just skip
            fseek(f, chunk_size, SEEK_CUR);
        } else {
            // Skip unknown chunk
            fseek(f, chunk_size, SEEK_CUR);
        }
    }

    fclose(f);

    if (!got_fmt || !got_data || byte_rate == 0) return false;

    meta->bitrate = byte_rate * 8;
    meta->duration_ms = (uint32_t)((uint64_t)data_size * 1000 / byte_rate);
    meta->valid = true;
    return true;
}