Blame src/read-wav.c

Packit 3ae693
/*-*- Mode: C; c-basic-offset: 8 -*-*/
Packit 3ae693
Packit 3ae693
/***
Packit 3ae693
  This file is part of libcanberra.
Packit 3ae693
Packit 3ae693
  Copyright 2008 Lennart Poettering
Packit 3ae693
Packit 3ae693
  libcanberra is free software; you can redistribute it and/or modify
Packit 3ae693
  it under the terms of the GNU Lesser General Public License as
Packit 3ae693
  published by the Free Software Foundation, either version 2.1 of the
Packit 3ae693
  License, or (at your option) any later version.
Packit 3ae693
Packit 3ae693
  libcanberra is distributed in the hope that it will be useful, but
Packit 3ae693
  WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 3ae693
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Packit 3ae693
  Lesser General Public License for more details.
Packit 3ae693
Packit 3ae693
  You should have received a copy of the GNU Lesser General Public
Packit 3ae693
  License along with libcanberra. If not, see
Packit 3ae693
  <http://www.gnu.org/licenses/>.
Packit 3ae693
***/
Packit 3ae693
Packit 3ae693
#ifdef HAVE_CONFIG_H
Packit 3ae693
#include <config.h>
Packit 3ae693
#endif
Packit 3ae693
Packit 3ae693
#include "canberra.h"
Packit 3ae693
#include "read-wav.h"
Packit 3ae693
#include "macro.h"
Packit 3ae693
#include "malloc.h"
Packit 3ae693
Packit 3ae693
#define FILE_SIZE_MAX (64U*1024U*1024U)
Packit 3ae693
Packit 3ae693
/* Stores the bit indexes in dwChannelMask */
Packit 3ae693
enum {
Packit 3ae693
        BIT_FRONT_LEFT,
Packit 3ae693
        BIT_FRONT_RIGHT,
Packit 3ae693
        BIT_FRONT_CENTER,
Packit 3ae693
        BIT_LOW_FREQUENCY,
Packit 3ae693
        BIT_BACK_LEFT,
Packit 3ae693
        BIT_BACK_RIGHT,
Packit 3ae693
        BIT_FRONT_LEFT_OF_CENTER,
Packit 3ae693
        BIT_FRONT_RIGHT_OF_CENTER,
Packit 3ae693
        BIT_BACK_CENTER,
Packit 3ae693
        BIT_SIDE_LEFT,
Packit 3ae693
        BIT_SIDE_RIGHT,
Packit 3ae693
        BIT_TOP_CENTER,
Packit 3ae693
        BIT_TOP_FRONT_LEFT,
Packit 3ae693
        BIT_TOP_FRONT_CENTER,
Packit 3ae693
        BIT_TOP_FRONT_RIGHT,
Packit 3ae693
        BIT_TOP_BACK_LEFT,
Packit 3ae693
        BIT_TOP_BACK_CENTER,
Packit 3ae693
        BIT_TOP_BACK_RIGHT,
Packit 3ae693
        _BIT_MAX
Packit 3ae693
};
Packit 3ae693
Packit 3ae693
static const ca_channel_position_t channel_table[_BIT_MAX] = {
Packit 3ae693
        [BIT_FRONT_LEFT] = CA_CHANNEL_FRONT_LEFT,
Packit 3ae693
        [BIT_FRONT_RIGHT] = CA_CHANNEL_FRONT_RIGHT,
Packit 3ae693
        [BIT_FRONT_CENTER] = CA_CHANNEL_FRONT_CENTER,
Packit 3ae693
        [BIT_LOW_FREQUENCY] = CA_CHANNEL_LFE,
Packit 3ae693
        [BIT_BACK_LEFT] = CA_CHANNEL_REAR_LEFT,
Packit 3ae693
        [BIT_BACK_RIGHT] = CA_CHANNEL_REAR_RIGHT,
Packit 3ae693
        [BIT_FRONT_LEFT_OF_CENTER] = CA_CHANNEL_FRONT_LEFT_OF_CENTER,
Packit 3ae693
        [BIT_FRONT_RIGHT_OF_CENTER] = CA_CHANNEL_FRONT_RIGHT_OF_CENTER,
Packit 3ae693
        [BIT_BACK_CENTER] = CA_CHANNEL_REAR_CENTER,
Packit 3ae693
        [BIT_SIDE_LEFT] = CA_CHANNEL_SIDE_LEFT,
Packit 3ae693
        [BIT_SIDE_RIGHT] = CA_CHANNEL_SIDE_RIGHT,
Packit 3ae693
        [BIT_TOP_CENTER] = CA_CHANNEL_TOP_CENTER,
Packit 3ae693
        [BIT_TOP_FRONT_LEFT] = CA_CHANNEL_TOP_FRONT_LEFT,
Packit 3ae693
        [BIT_TOP_FRONT_CENTER] = CA_CHANNEL_TOP_FRONT_CENTER,
Packit 3ae693
        [BIT_TOP_FRONT_RIGHT] = CA_CHANNEL_TOP_FRONT_RIGHT,
Packit 3ae693
        [BIT_TOP_BACK_LEFT] = CA_CHANNEL_TOP_REAR_LEFT,
Packit 3ae693
        [BIT_TOP_BACK_CENTER] = CA_CHANNEL_TOP_REAR_CENTER,
Packit 3ae693
        [BIT_TOP_BACK_RIGHT] = CA_CHANNEL_TOP_REAR_RIGHT
Packit 3ae693
};
Packit 3ae693
Packit 3ae693
struct ca_wav {
Packit 3ae693
        FILE *file;
Packit 3ae693
Packit 3ae693
        off_t data_size;
Packit 3ae693
        unsigned nchannels;
Packit 3ae693
        unsigned rate;
Packit 3ae693
        unsigned depth;
Packit 3ae693
        uint32_t channel_mask;
Packit 3ae693
Packit 3ae693
        ca_channel_position_t channel_map[_BIT_MAX];
Packit 3ae693
};
Packit 3ae693
Packit 3ae693
#define CHUNK_ID_DATA 0x61746164U
Packit 3ae693
#define CHUNK_ID_FMT 0x20746d66U
Packit 3ae693
Packit 3ae693
static const uint8_t pcm_guid[16] = {
Packit 3ae693
        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
Packit 3ae693
        0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
Packit 3ae693
};
Packit 3ae693
Packit 3ae693
static int skip_to_chunk(ca_wav *w, uint32_t id, uint32_t *size) {
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(w, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(size, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        for (;;) {
Packit 3ae693
                uint32_t chunk[2];
Packit 3ae693
                uint32_t s;
Packit 3ae693
Packit 3ae693
                if (fread(chunk, sizeof(uint32_t), CA_ELEMENTSOF(chunk), w->file) != CA_ELEMENTSOF(chunk))
Packit 3ae693
                        goto fail_io;
Packit 3ae693
Packit 3ae693
                s = CA_UINT32_FROM_LE(chunk[1]);
Packit 3ae693
Packit 3ae693
                if (s <= 0 || s >= FILE_SIZE_MAX)
Packit 3ae693
                        return CA_ERROR_TOOBIG;
Packit 3ae693
Packit 3ae693
                if (CA_UINT32_FROM_LE(chunk[0]) == id) {
Packit 3ae693
                        *size = s;
Packit 3ae693
                        break;
Packit 3ae693
                }
Packit 3ae693
Packit 3ae693
                if (fseek(w->file, (long) s, SEEK_CUR) < 0)
Packit 3ae693
                        return CA_ERROR_SYSTEM;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        return CA_SUCCESS;
Packit 3ae693
Packit 3ae693
fail_io:
Packit 3ae693
Packit 3ae693
        if (feof(w->file))
Packit 3ae693
                return CA_ERROR_CORRUPT;
Packit 3ae693
        else if (ferror(w->file))
Packit 3ae693
                return CA_ERROR_SYSTEM;
Packit 3ae693
Packit 3ae693
        ca_assert_not_reached();
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
int ca_wav_open(ca_wav **_w, FILE *f)  {
Packit 3ae693
        uint32_t header[3], fmt_chunk[10];
Packit 3ae693
        int ret;
Packit 3ae693
        ca_wav *w;
Packit 3ae693
        uint32_t file_size, fmt_size, data_size;
Packit 3ae693
        ca_bool_t extensible;
Packit 3ae693
        uint32_t format;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(_w, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(f, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        if (!(w = ca_new(ca_wav, 1)))
Packit 3ae693
                return CA_ERROR_OOM;
Packit 3ae693
Packit 3ae693
        w->file = f;
Packit 3ae693
Packit 3ae693
        if (fread(header, sizeof(uint32_t), CA_ELEMENTSOF(header), f) != CA_ELEMENTSOF(header))
Packit 3ae693
                goto fail_io;
Packit 3ae693
Packit 3ae693
        if (CA_UINT32_FROM_LE(header[0]) != 0x46464952U ||
Packit 3ae693
            CA_UINT32_FROM_LE(header[2]) != 0x45564157U) {
Packit 3ae693
                ret = CA_ERROR_CORRUPT;
Packit 3ae693
                goto fail;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        file_size = CA_UINT32_FROM_LE(header[1]);
Packit 3ae693
Packit 3ae693
        if (file_size <= 0 || file_size >= FILE_SIZE_MAX) {
Packit 3ae693
                ret = CA_ERROR_TOOBIG;
Packit 3ae693
                goto fail;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        /* Skip to the fmt chunk */
Packit 3ae693
        if ((ret = skip_to_chunk(w, CHUNK_ID_FMT, &fmt_size)) < 0)
Packit 3ae693
                goto fail;
Packit 3ae693
Packit 3ae693
        switch (fmt_size) {
Packit 3ae693
Packit 3ae693
        case 14: /* WAVEFORMAT */
Packit 3ae693
        case 16:
Packit 3ae693
        case 18: /* WAVEFORMATEX */
Packit 3ae693
                extensible = FALSE;
Packit 3ae693
                break;
Packit 3ae693
Packit 3ae693
        case 40: /* WAVEFORMATEXTENSIBLE */
Packit 3ae693
                extensible = TRUE;
Packit 3ae693
                break;
Packit 3ae693
Packit 3ae693
        default:
Packit 3ae693
                ret = CA_ERROR_NOTSUPPORTED;
Packit 3ae693
                goto fail;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        if (fread(fmt_chunk, 1, fmt_size, f) != fmt_size)
Packit 3ae693
                goto fail_io;
Packit 3ae693
Packit 3ae693
        /* PCM? or WAVEX? */
Packit 3ae693
        format = (CA_UINT32_FROM_LE(fmt_chunk[0]) & 0xFFFF);
Packit 3ae693
        if ((!extensible && format != 0x0001) ||
Packit 3ae693
            (extensible && format != 0xFFFE)) {
Packit 3ae693
                ret = CA_ERROR_NOTSUPPORTED;
Packit 3ae693
                goto fail;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        if (extensible) {
Packit 3ae693
                if (memcmp(fmt_chunk + 6, pcm_guid, 16) != 0) {
Packit 3ae693
                        ret = CA_ERROR_NOTSUPPORTED;
Packit 3ae693
                        goto fail;
Packit 3ae693
                }
Packit 3ae693
Packit 3ae693
                w->channel_mask = CA_UINT32_FROM_LE(fmt_chunk[5]);
Packit 3ae693
        } else
Packit 3ae693
                w->channel_mask = 0;
Packit 3ae693
Packit 3ae693
        w->nchannels = CA_UINT32_FROM_LE(fmt_chunk[0]) >> 16;
Packit 3ae693
        w->rate = CA_UINT32_FROM_LE(fmt_chunk[1]);
Packit 3ae693
        w->depth = CA_UINT32_FROM_LE(fmt_chunk[3]) >> 16;
Packit 3ae693
Packit 3ae693
        if (w->nchannels <= 0 || w->rate <= 0) {
Packit 3ae693
                ret = CA_ERROR_CORRUPT;
Packit 3ae693
                goto fail;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        if (w->depth != 16 && w->depth != 8) {
Packit 3ae693
                ret = CA_ERROR_NOTSUPPORTED;
Packit 3ae693
                goto fail;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        /* Skip to the data chunk */
Packit 3ae693
        if ((ret = skip_to_chunk(w, CHUNK_ID_DATA, &data_size)) < 0)
Packit 3ae693
                goto fail;
Packit 3ae693
        w->data_size = (off_t) data_size;
Packit 3ae693
Packit 3ae693
        if ((w->data_size % (w->depth/8)) != 0) {
Packit 3ae693
                ret = CA_ERROR_CORRUPT;
Packit 3ae693
                goto fail;
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        *_w = w;
Packit 3ae693
Packit 3ae693
        return CA_SUCCESS;
Packit 3ae693
Packit 3ae693
fail_io:
Packit 3ae693
Packit 3ae693
        if (feof(f))
Packit 3ae693
                ret = CA_ERROR_CORRUPT;
Packit 3ae693
        else if (ferror(f))
Packit 3ae693
                ret = CA_ERROR_SYSTEM;
Packit 3ae693
        else
Packit 3ae693
                ca_assert_not_reached();
Packit 3ae693
Packit 3ae693
fail:
Packit 3ae693
Packit 3ae693
        ca_free(w);
Packit 3ae693
Packit 3ae693
        return ret;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
void ca_wav_close(ca_wav *w) {
Packit 3ae693
        ca_assert(w);
Packit 3ae693
Packit 3ae693
        fclose(w->file);
Packit 3ae693
        ca_free(w);
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
unsigned ca_wav_get_nchannels(ca_wav *w) {
Packit 3ae693
        ca_assert(w);
Packit 3ae693
Packit 3ae693
        return w->nchannels;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
unsigned ca_wav_get_rate(ca_wav *w) {
Packit 3ae693
        ca_assert(w);
Packit 3ae693
Packit 3ae693
        return w->rate;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
const ca_channel_position_t* ca_wav_get_channel_map(ca_wav *w) {
Packit 3ae693
        unsigned c;
Packit 3ae693
        ca_channel_position_t *p;
Packit 3ae693
Packit 3ae693
        ca_assert(w);
Packit 3ae693
Packit 3ae693
        if (!w->channel_mask)
Packit 3ae693
                return NULL;
Packit 3ae693
Packit 3ae693
        p = w->channel_map;
Packit 3ae693
Packit 3ae693
        for (c = 0; c < _BIT_MAX; c++)
Packit 3ae693
                if ((w->channel_mask & (1 << c)))
Packit 3ae693
                        *(p++) = channel_table[c];
Packit 3ae693
Packit 3ae693
        ca_assert(p <= w->channel_map + _BIT_MAX);
Packit 3ae693
Packit 3ae693
        if (p != w->channel_map + w->nchannels)
Packit 3ae693
                return NULL;
Packit 3ae693
Packit 3ae693
        return w->channel_map;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
ca_sample_type_t ca_wav_get_sample_type(ca_wav *w) {
Packit 3ae693
        ca_assert(w);
Packit 3ae693
Packit 3ae693
        return w->depth == 16 ?
Packit 3ae693
#ifdef WORDS_BIGENDIAN
Packit 3ae693
                CA_SAMPLE_S16RE
Packit 3ae693
#else
Packit 3ae693
                CA_SAMPLE_S16NE
Packit 3ae693
#endif
Packit 3ae693
                : CA_SAMPLE_U8;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
int ca_wav_read_s16le(ca_wav *w, int16_t *d, size_t *n) {
Packit 3ae693
        off_t remaining;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(w, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(w->depth == 16, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(d, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(n, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        remaining = w->data_size / (off_t) sizeof(int16_t);
Packit 3ae693
Packit 3ae693
        if ((off_t) *n > remaining)
Packit 3ae693
                *n = (size_t) remaining;
Packit 3ae693
Packit 3ae693
        if (*n > 0) {
Packit 3ae693
                *n = fread(d, sizeof(int16_t), *n, w->file);
Packit 3ae693
Packit 3ae693
                if (*n <= 0 && ferror(w->file))
Packit 3ae693
                        return CA_ERROR_SYSTEM;
Packit 3ae693
Packit 3ae693
                ca_assert(w->data_size >= (off_t) *n * (off_t) sizeof(int16_t));
Packit 3ae693
                w->data_size -= (off_t) *n * (off_t) sizeof(int16_t);
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        return CA_SUCCESS;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
int ca_wav_read_u8(ca_wav *w, uint8_t *d, size_t *n) {
Packit 3ae693
        off_t remaining;
Packit 3ae693
Packit 3ae693
        ca_return_val_if_fail(w, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(w->depth == 8, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(d, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(n, CA_ERROR_INVALID);
Packit 3ae693
        ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID);
Packit 3ae693
Packit 3ae693
        remaining = w->data_size / (off_t) sizeof(uint8_t);
Packit 3ae693
Packit 3ae693
        if ((off_t) *n > remaining)
Packit 3ae693
                *n = (size_t) remaining;
Packit 3ae693
Packit 3ae693
        if (*n > 0) {
Packit 3ae693
                *n = fread(d, sizeof(uint8_t), *n, w->file);
Packit 3ae693
Packit 3ae693
                if (*n <= 0 && ferror(w->file))
Packit 3ae693
                        return CA_ERROR_SYSTEM;
Packit 3ae693
Packit 3ae693
                ca_assert(w->data_size >= (off_t) *n * (off_t) sizeof(uint8_t));
Packit 3ae693
                w->data_size -= (off_t) *n * (off_t) sizeof(uint8_t);
Packit 3ae693
        }
Packit 3ae693
Packit 3ae693
        return CA_SUCCESS;
Packit 3ae693
}
Packit 3ae693
Packit 3ae693
off_t ca_wav_get_size(ca_wav *v) {
Packit 3ae693
        ca_return_val_if_fail(v, (off_t) -1);
Packit 3ae693
Packit 3ae693
        return v->data_size;
Packit 3ae693
}