Skip to content

File syn_wav.c

File List > audio > syn_wav.c

Go to the documentation of this file

#include "syn_wav.h"

#include <string.h>

static inline uint16_t peek_u16_le(const uint8_t *p)
{
    return (uint16_t)((uint16_t)p[0] | ((uint16_t)p[1] << 8));
}

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

SYN_Status syn_wav_parse_header(const uint8_t *buffer, size_t buffer_size, SYN_WAV_Info *info)
{
    if ((buffer == NULL) || (info == NULL) || (buffer_size < 44U)) {
        return SYN_INVALID_PARAM;
    }

    memset(info, 0, sizeof(SYN_WAV_Info));

    /* Check 'RIFF' magic header */
    if (memcmp(&buffer[0], "RIFF", 4) != 0) {
        return SYN_ERROR;
    }

    /* Check 'WAVE' format */
    if (memcmp(&buffer[8], "WAVE", 4) != 0) {
        return SYN_ERROR;
    }

    size_t offset = 12U;
    bool fmt_found = false;
    bool data_found = false;

    while (offset + 8U <= buffer_size) {
        const uint8_t *chunk = &buffer[offset];
        uint32_t chunk_size = peek_u32_le(&chunk[4]);

        if (memcmp(&chunk[0], "fmt ", 4) == 0) {
            if (offset + 8U + 16U > buffer_size) {
                return SYN_ERROR;
            }

            info->audio_format = peek_u16_le(&chunk[8]);
            info->num_channels = peek_u16_le(&chunk[10]);
            info->sample_rate = peek_u32_le(&chunk[12]);
            info->byte_rate = peek_u32_le(&chunk[16]);
            info->block_align = peek_u16_le(&chunk[20]);
            info->bits_per_sample = peek_u16_le(&chunk[22]);

            fmt_found = true;
        } else if (memcmp(&chunk[0], "data", 4) == 0) {
            info->data_offset = offset + 8U;
            info->data_size = chunk_size;
            data_found = true;
            break; /* Found data payload */
        }

        if (chunk_size > buffer_size - offset - 8U) {
            break;
        }

        offset += 8U + chunk_size;
        /* Word align chunk padding */
        if ((chunk_size & 1U) != 0U) {
            offset++;
        }
    }

    if (!fmt_found || !data_found) {
        return SYN_ERROR;
    }

    /* Calculate total sample count */
    if (info->audio_format == SYN_WAV_FORMAT_PCM) {
        uint32_t bytes_per_sample = info->bits_per_sample / 8U;
        if ((info->num_channels > 0U) && (bytes_per_sample > 0U)) {
            info->total_samples = info->data_size / (info->num_channels * bytes_per_sample);
        }
    } else if (info->audio_format == SYN_WAV_FORMAT_IMA_ADPCM) {
        /* 4 bits per sample mono = 2 samples per byte */
        info->total_samples = info->data_size * 2U;
    }

    return SYN_OK;
}