add support for aiff files

This commit is contained in:
Alexandre Ratchov 2015-01-06 11:41:24 +01:00
parent 2e1306cbeb
commit f2c235166b
3 changed files with 272 additions and 2 deletions

View File

@ -44,6 +44,14 @@ typedef struct {
unsigned char lw[2];
} le16_t;
typedef struct {
unsigned char bd[4];
} be32_t;
typedef struct {
unsigned char bw[2];
} be16_t;
struct wavriff {
char magic[4];
le32_t size;
@ -79,6 +87,40 @@ struct wavhdr {
struct wavchunk data_hdr;
};
struct aiff_form {
char magic[4];
be32_t size;
char type[4];
};
struct aiff_chunk {
char id[4];
be32_t size;
};
struct aiff_comm {
be16_t nch;
be32_t nfr;
be16_t bits;
/* rate in 80-bit floating point */
be16_t rate_ex;
be32_t rate_hi;
be32_t rate_lo;
};
struct aiff_data {
be32_t offs;
be32_t blksz;
};
struct aiff_hdr {
struct aiff_form form; /* 00..11 */
struct aiff_chunk comm_hdr; /* 12..20 */
struct aiff_comm comm;
struct aiff_chunk data_hdr;
struct aiff_data data;
};
char wav_id_riff[4] = { 'R', 'I', 'F', 'F' };
char wav_id_wave[4] = { 'W', 'A', 'V', 'E' };
char wav_id_data[4] = { 'd', 'a', 't', 'a' };
@ -90,6 +132,11 @@ char wav_guid[14] = {
0x9B, 0x71
};
char aiff_id_form[4] = {'F', 'O', 'R', 'M'};
char aiff_id_aiff[4] = { 'A', 'I', 'F', 'F' };
char aiff_id_data[4] = { 'S', 'S', 'N', 'D' };
char aiff_id_comm[4] = { 'C', 'O', 'M', 'M' };
static inline unsigned int
le16_get(le16_t *p)
{
@ -121,6 +168,37 @@ le32_set(le32_t *p, unsigned int v)
p->ld[3] = v >> 24;
}
static inline unsigned int
be16_get(be16_t *p)
{
return p->bw[1] | p->bw[0] << 8;
}
static inline void
be16_set(be16_t *p, unsigned int v)
{
p->bw[1] = v;
p->bw[0] = v >> 8;
}
static inline unsigned int
be32_get(be32_t *p)
{
return p->bd[3] |
p->bd[2] << 8 |
p->bd[1] << 16 |
p->bd[0] << 24;
}
static inline void
be32_set(be32_t *p, unsigned int v)
{
p->bd[3] = v;
p->bd[2] = v >> 8;
p->bd[1] = v >> 16;
p->bd[0] = v >> 24;
}
static int
afile_wav_readfmt(struct afile *f, unsigned int csize)
{
@ -316,6 +394,170 @@ afile_wav_writehdr(struct afile *f)
return 1;
}
static int
afile_aiff_readcomm(struct afile *f, unsigned int csize, unsigned int *nfr)
{
struct aiff_comm comm;
unsigned int nch, rate, bits, bps, enc;
unsigned int e, m;
if (csize != sizeof(struct aiff_comm)) {
log_putu(csize);
log_puts(": bugus comm chunk size\n");
return 0;
}
if (read(f->fd, &comm, csize) != csize) {
log_puts("failed to read .aiff comm chunk\n");
return 0;
}
nch = be16_get(&comm.nch);
if (nch == 0) {
log_puts("zero number of channels in .aiff file\n");
return 0;
}
e = be16_get(&comm.rate_ex);
m = be32_get(&comm.rate_hi);
if (e < 0x3fff || e > 0x3fff + 31) {
log_puts(f->path);
log_puts(": bad sample rate in .aiff file\n");
return 0;
}
rate = m >> (0x3fff + 31 - e);
if (rate < RATE_MIN || rate > RATE_MAX) {
log_putu(rate);
log_puts(": sample rate out of range in .aiff file\n");
return 0;
}
bits = be16_get(&comm.bits);
if (bits < BITS_MIN || bits > BITS_MAX) {
log_putu(bits);
log_puts(": bad number of bits in .aiff file\n");
return 0;
}
f->nch = nch;
f->par.bps = (bits + 7) / 8;
f->par.bits = bits;
f->par.le = 0;
f->par.sig = 1;
f->par.msb = 1;
*nfr = be32_get(&comm.nfr);
return 1;
}
static int
afile_aiff_readhdr(struct afile *f)
{
struct aiff_form form;
struct aiff_chunk chunk;
unsigned int csize, rsize, nfr, pos = 0;
int comm_done = 0;
if (lseek(f->fd, 0, SEEK_SET) < 0) {
log_puts("failed to seek to beginning of .aiff file\n");
return 0;
}
if (read(f->fd, &form, sizeof(form)) != sizeof(form)) {
log_puts("failed to read .aiff file form header\n");
return 0;
}
if (memcmp(&form.magic, &aiff_id_form, 4) != 0 ||
memcmp(&form.type, &aiff_id_aiff, 4)) {
log_puts("not a .aiff file\n");
return 0;
}
rsize = be32_get(&form.size);
for (;;) {
if (pos + sizeof(struct aiff_chunk) > rsize) {
log_puts("missing data chunk in .aiff file\n");
return 0;
}
if (read(f->fd, &chunk, sizeof(chunk)) != sizeof(chunk)) {
log_puts("failed to read .aiff chunk header\n");
return 0;
}
csize = be32_get(&chunk.size);
if (memcmp(chunk.id, aiff_id_comm, 4) == 0) {
if (!afile_aiff_readcomm(f, csize, &nfr))
return 0;
comm_done = 1;
} else if (memcmp(chunk.id, aiff_id_data, 4) == 0) {
f->startpos = pos + sizeof(form) + sizeof(chunk);
break;
} else {
#ifdef DEBUG
if (log_level >= 2)
log_puts("skipped unknown .aiff file chunk\n");
#endif
}
/*
* next chunk
*/
pos += sizeof(struct aiff_chunk) + csize;
if (lseek(f->fd, sizeof(form) + pos, SEEK_SET) < 0) {
log_puts("filed to seek to chunk in .aiff file\n");
return 0;
}
}
if (!comm_done) {
log_puts("missing comm chunk in .wav file\n");
return 0;
}
f->endpos = f->startpos + f->par.bps * f->nch * nfr;
return 1;
}
/*
* Write header and seek to start position
*/
static int
afile_aiff_writehdr(struct afile *f)
{
struct aiff_hdr hdr;
unsigned int bpf;
unsigned int e, m;
/* convert rate to 80-bit float (exponent and fraction part) */
m = f->rate;
e = 0x3fff + 31;
while ((m & 0x80000000) == 0) {
e--;
m <<= 1;
}
/* bytes per frame */
bpf = f->nch * f->par.bps;
memset(&hdr, 0, sizeof(struct aiff_hdr));
memcpy(hdr.form.magic, aiff_id_form, 4);
memcpy(hdr.form.type, aiff_id_aiff, 4);
be32_set(&hdr.form.size, f->endpos - sizeof(hdr.form));
memcpy(hdr.comm_hdr.id, aiff_id_comm, 4);
be32_set(&hdr.comm_hdr.size, sizeof(hdr.comm));
be16_set(&hdr.comm.nch, f->nch);
be16_set(&hdr.comm.bits, f->par.bits);
be16_set(&hdr.comm.rate_ex, e);
be32_set(&hdr.comm.rate_hi, m);
be32_set(&hdr.comm.rate_lo, 0);
be32_set(&hdr.comm.nfr, (f->endpos - f->startpos) / bpf);
memcpy(hdr.data_hdr.id, aiff_id_data, 4);
be32_set(&hdr.data_hdr.size, f->endpos - f->startpos);
be32_set(&hdr.data.offs, 0);
be32_set(&hdr.data.blksz, 0);
if (lseek(f->fd, 0, SEEK_SET) < 0) {
log_puts("failed to seek back to .aiff file header\n");
return 0;
}
if (write(f->fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
log_puts("failed to write .aiff file header\n");
return 0;
}
f->curpos = f->startpos;
return 1;
}
size_t
afile_read(struct afile *f, void *data, size_t count)
{
@ -408,6 +650,8 @@ afile_close(struct afile *f)
if (f->flags & WAV_FWRITE) {
if (f->hdr == HDR_WAV)
afile_wav_writehdr(f);
else if (f->hdr == HDR_AIFF)
afile_aiff_writehdr(f);
}
close(f->fd);
}
@ -429,7 +673,10 @@ afile_open(struct afile *f, char *path, int hdr, int flags,
ext = strrchr(path, '.');
if (ext != NULL) {
ext++;
if (strcasecmp(ext, "afile") == 0)
if (strcasecmp(ext, "aif") == 0 ||
strcasecmp(ext, "aiff") == 0)
f->hdr = HDR_AIFF;
else if (strcasecmp(ext, "wav") == 0)
f->hdr = HDR_WAV;
}
}
@ -449,6 +696,9 @@ afile_open(struct afile *f, char *path, int hdr, int flags,
if (f->hdr == HDR_WAV) {
if (!afile_wav_readhdr(f))
goto bad_close;
} else if (f->hdr == HDR_AIFF) {
if (!afile_aiff_readhdr(f))
goto bad_close;
} else {
f->startpos = 0;
f->endpos = -1; /* read until EOF */
@ -485,6 +735,21 @@ afile_open(struct afile *f, char *path, int hdr, int flags,
log_puts(": failed reserve space for .wav header\n");
goto bad_close;
}
} else if (f->hdr == HDR_AIFF) {
f->par.bps = (f->par.bits + 7) >> 3;
if (f->par.bps > 1)
f->par.le = 0;
f->par.sig = 1;
if (f->par.bits & 7)
f->par.msb = 1;
f->endpos = f->startpos = sizeof(struct aiff_hdr);
f->maxpos = WAV_MAXPOS;
memset(&dummy, 0xd0, sizeof(struct aiff_hdr));
if (write(f->fd, &dummy, sizeof(struct aiff_hdr)) < 0) {
log_puts(f->path);
log_puts(": failed reserve space for .aiff header\n");
goto bad_close;
}
} else {
f->endpos = f->startpos = 0;
f->maxpos = -1;

View File

@ -32,6 +32,7 @@ struct afile {
#define HDR_AUTO 0
#define HDR_RAW 1
#define HDR_WAV 2
#define HDR_AIFF 3
int hdr; /* header type */
int fd; /* file descriptor */
#define WAV_FREAD 1 /* open for reading */

View File

@ -1196,10 +1196,14 @@ opt_hdr(char *s, int *hdr)
*hdr = HDR_RAW;
return 1;
}
if (strcmp("afile", s) == 0) {
if (strcmp("wav", s) == 0) {
*hdr = HDR_WAV;
return 1;
}
if (strcmp("aiff", s) == 0) {
*hdr = HDR_AIFF;
return 1;
}
log_puts(s);
log_puts(": bad header type\n");
return 0;