📄 aiff.c
字号:
/* * September 25, 1991 * Copyright 1991 Guido van Rossum And Sundry Contributors * This source code is freely redistributable and may be used for * any purpose. This copyright notice must be maintained. * Guido van Rossum And Sundry Contributors are not responsible for * the consequences of using this software. *//* * Sound Tools SGI/Amiga AIFF format. * Used by SGI on 4D/35 and Indigo. * This is a subformat of the EA-IFF-85 format. * This is related to the IFF format used by the Amiga. * But, apparently, not the same. * * Jan 93: new version from Guido Van Rossum that * correctly skips unwanted sections. * * Jan 94: add loop & marker support * Jul 97: added comments I/O by Leigh Smith * Nov 97: added verbose chunk comments * * June 1, 1998 - Chris Bagwell (cbagwell@sprynet.com) * Fixed compile warnings reported by Kjetil Torgrim Homme * <kjetilho@ifi.uio.no> * * Sept 9, 1998 - fixed loop markers. * */#include <math.h>#include <stdlib.h>#include <string.h>#include "st.h"#ifndef SEEK_SET#define SEEK_SET 0 /* nasty nasty */#endif#ifndef SEEK_CUR#define SEEK_CUR 1 /* nasty nasty */#endif /* SEEK_CUR *//* Private data used by writer */struct aiffpriv { ULONG nsamples; /* number of 1-channel samples read or written */};double read_ieee_extended();LONG rawread(P3(ft_t, LONG *, LONG));void aiffwriteheader(P2(ft_t, LONG));void rawwrite(P3(ft_t, LONG *, LONG));void write_ieee_extended(P2(ft_t, double));double ConvertFromIeeeExtended();void ConvertToIeeeExtended(P2(double, char *));void textChunk(P3(char **text, char *chunkDescription, ft_t ft));void reportInstrument(P1(ft_t ft));void aiffstartread(ft) ft_t ft;{ struct aiffpriv *p = (struct aiffpriv *) ft->priv; char buf[5]; ULONG totalsize; LONG chunksize; int channels = 0; ULONG frames; int bits = 0; double rate = 0.0; ULONG offset = 0; ULONG blocksize = 0; int littlendian = 0; char *endptr; int foundcomm = 0, foundmark = 0, foundinstr = 0; struct mark { int id, position; char name[40]; } marks[32]; int i, j; LONG nmarks = 0; LONG sustainLoopBegin = 0, sustainLoopEnd = 0, releaseLoopBegin = 0, releaseLoopEnd = 0; LONG seekto = 0L, ssndsize = 0L; char *author; char *copyright; char *nametext; /* FORM chunk */ if (fread(buf, 1, 4, ft->fp) != 4 || strncmp(buf, "FORM", 4) != 0) fail("AIFF header does not begin with magic word 'FORM'"); totalsize = rblong(ft); if (fread(buf, 1, 4, ft->fp) != 4 || strncmp(buf, "AIFF", 4) != 0) fail("AIFF 'FORM' chunk does not specify 'AIFF' as type"); /* Skip everything but the COMM chunk and the SSND chunk */ /* The SSND chunk must be the last in the file */ while (1) { if (fread(buf, 1, 4, ft->fp) != 4) if (ssndsize > 0) break; else fail("Missing SSND chunk in AIFF file"); if (strncmp(buf, "COMM", 4) == 0) { /* COMM chunk */ chunksize = rblong(ft); if (chunksize != 18) fail("AIFF COMM chunk has bad size"); channels = rbshort(ft); frames = rblong(ft); bits = rbshort(ft); rate = read_ieee_extended(ft); foundcomm = 1; } else if (strncmp(buf, "SSND", 4) == 0) { /* SSND chunk */ chunksize = rblong(ft); offset = rblong(ft); blocksize = rblong(ft); chunksize -= 8; ssndsize = chunksize; /* if can't seek, just do sound now */ if (!ft->seekable) break; /* else, seek to end of sound and hunt for more */ seekto = ftell(ft->fp); fseek(ft->fp, chunksize, SEEK_CUR); } else if (strncmp(buf, "MARK", 4) == 0) { /* MARK chunk */ chunksize = rblong(ft); nmarks = rbshort(ft); chunksize -= 2; for(i = 0; i < nmarks; i++) { int len; marks[i].id = rbshort(ft); marks[i].position = rblong(ft); chunksize -= 6; len = getc(ft->fp); chunksize -= len + 1; for(j = 0; j < len ; j++) marks[i].name[j] = getc(ft->fp); marks[i].name[j] = 0; if ((len & 1) == 0) { chunksize--; getc(ft->fp); } } /* HA HA! Sound Designer (and others) makes */ /* bogus files. It spits out bogus chunksize */ /* for MARK field */ while(chunksize-- > 0) getc(ft->fp); foundmark = 1; } else if (strncmp(buf, "INST", 4) == 0) { /* INST chunk */ chunksize = rblong(ft); ft->instr.MIDInote = getc(ft->fp); getc(ft->fp); /* detune */ ft->instr.MIDIlow = getc(ft->fp); ft->instr.MIDIhi = getc(ft->fp); getc(ft->fp); /* low velocity */ getc(ft->fp); /* hi velocity */ rbshort(ft); /* gain */ ft->loops[0].type = rbshort(ft); /* sustain loop */ sustainLoopBegin = rbshort(ft); /* begin marker */ sustainLoopEnd = rbshort(ft); /* end marker */ ft->loops[1].type = rbshort(ft); /* release loop */ releaseLoopBegin = rbshort(ft); /* begin marker */ releaseLoopEnd = rbshort(ft); /* end marker */ foundinstr = 1; } else if (strncmp(buf, "APPL", 4) == 0) { chunksize = rblong(ft); while(chunksize-- > 0) getc(ft->fp); } else if (strncmp(buf, "ALCH", 4) == 0) { /* I think this is bogus and gets grabbed by APPL */ /* INST chunk */ rblong(ft); /* ENVS - jeez! */ chunksize = rblong(ft); while(chunksize-- > 0) getc(ft->fp); } else if (strncmp(buf, "ANNO", 4) == 0) { /* Old form of comment chunk */ chunksize = rblong(ft); /* allocate enough memory to hold the comment */ ft->comment = (char *) malloc((size_t) chunksize); if (ft->comment == NULL) fail("AIFF: Couldn't allocate ANNO header"); if (fread(ft->comment, 1, chunksize, ft->fp) != chunksize) fail("AIFF: Unexpected EOF in ANNO header"); } else if (strncmp(buf, "AUTH", 4) == 0) { /* Author chunk */ textChunk(&author, "Author:", ft); free(author); } else if (strncmp(buf, "NAME", 4) == 0) { /* Name chunk */ textChunk(&nametext, "Name:", ft); free(nametext); } else if (strncmp(buf, "(c) ", 4) == 0) { /* Copyright chunk */ textChunk(©right, "Copyright:", ft); free(copyright); } else { buf[4] = 0; /* bogus file, probably from the Mac */ if ((buf[0] < 'A' || buf[0] > 'Z') || (buf[1] < 'A' || buf[1] > 'Z') || (buf[2] < 'A' || buf[2] > 'Z') || (buf[3] < 'A' || buf[3] > 'Z')) break; if (feof(ft->fp)) break; report("AIFFstartread: ignoring '%s' chunk\n", buf); chunksize = rblong(ft); if (feof(ft->fp)) break; /* Skip the chunk using getc() so we may read from a pipe */ while (chunksize-- > 0) { if (getc(ft->fp) == EOF) break; } } if (feof(ft->fp)) break; } /* * if a pipe, we lose all chunks after sound. * Like, say, instrument loops. */ if (ft->seekable) if (seekto > 0) fseek(ft->fp, seekto, SEEK_SET); else fail("AIFF: no sound data on input file"); /* SSND chunk just read */ if (blocksize != 0) fail("AIFF header specifies nonzero blocksize?!?!"); while ((LONG) (--offset) >= 0) { if (getc(ft->fp) == EOF) fail("unexpected EOF while skipping AIFF offset"); } if (foundcomm) { ft->info.channels = channels; ft->info.rate = rate; ft->info.style = SIGN2; switch (bits) { case 8: ft->info.size = BYTE; break; case 16: ft->info.size = WORD; break; default: fail("unsupported sample size in AIFF header: %d", bits); /*NOTREACHED*/ } } else { if ((ft->info.channels == -1) || (ft->info.rate == -1) || (ft->info.style == -1) || (ft->info.size == -1)) { report("You must specify # channels, sample rate, signed/unsigned,\n"); report("and 8/16 on the command line."); fail("Bogus AIFF file: no COMM section."); } } p->nsamples = ssndsize / ft->info.size; /* leave out channels */ /* process instrument and marker notations. */ if (foundmark && !foundinstr) fail("Bogus AIFF file: MARKers but no INSTrument."); if (!foundmark && foundinstr) fail("Bogus AIFF file: INSTrument but no MARKers."); if (foundmark && foundinstr) { int i; int slbIndex = 0, sleIndex = 0; int rlbIndex = 0, rleIndex = 0; /* find our loop markers and save their marker indexes */ for(i = 0; i < nmarks; i++) { if(marks[i].id == sustainLoopBegin) slbIndex = i; if(marks[i].id == sustainLoopEnd) sleIndex = i; if(marks[i].id == releaseLoopBegin) rlbIndex = i; if(marks[i].id == releaseLoopEnd) rleIndex = i; } ft->instr.nloops = 0; if (ft->loops[0].type != 0) { ft->loops[0].start = marks[slbIndex].position; ft->loops[0].length = marks[sleIndex].position - marks[slbIndex].position; /* really the loop count should be infinite */ ft->loops[0].count = 1; ft->instr.loopmode = LOOP_SUSTAIN_DECAY | ft->loops[0].type; ft->instr.nloops++; } if (ft->loops[1].type != 0) { ft->loops[1].start = marks[rlbIndex].position; ft->loops[1].length = marks[rleIndex].position - marks[rlbIndex].position; /* really the loop count should be infinite */ ft->loops[1].count = 1; ft->instr.loopmode = LOOP_SUSTAIN_DECAY | ft->loops[1].type; ft->instr.nloops++; } } if (verbose) reportInstrument(ft); endptr = (char *) &littlendian; *endptr = 1; if (littlendian == 1) ft->swap = 1;}/* print out the MIDI key allocations, loop points, directions etc */void reportInstrument(ft)ft_t ft;{ int loopNum; if(ft->instr.nloops > 0) fprintf(stderr, "AIFF Loop markers:\n"); for(loopNum = 0; loopNum < ft->instr.nloops; loopNum++) { if (ft->loops[loopNum].count) { fprintf(stderr, "Loop %d: start: %6d", loopNum, ft->loops[loopNum].start); fprintf(stderr, " end: %6d", ft->loops[loopNum].start + ft->loops[loopNum].length); fprintf(stderr, " count: %6d", ft->loops[loopNum].count); fprintf(stderr, " type: "); switch(ft->loops[loopNum].type & ~LOOP_SUSTAIN_DECAY) { case 0: fprintf(stderr, "off\n"); break; case 1: fprintf(stderr, "forward\n"); break; case 2: fprintf(stderr, "forward/backward\n"); break; } } } fprintf(stderr, "Unity MIDI Note: %d\n", ft->instr.MIDInote); fprintf(stderr, "Low MIDI Note: %d\n", ft->instr.MIDIlow); fprintf(stderr, "High MIDI Note: %d\n", ft->instr.MIDIhi);}/* Process a text chunk, allocate memory, display it if verbose and return */void textChunk(text, chunkDescription, ft) char **text;char *chunkDescription;ft_t ft;{ LONG chunksize = rblong(ft); /* allocate enough memory to hold the text including a terminating \0 */ *text = (char *) malloc((size_t) chunksize + 1); if (*text == NULL) fail("AIFF: Couldn't allocate %s header", chunkDescription); if (fread(*text, 1, chunksize, ft->fp) != chunksize) fail("AIFF: Unexpected EOF in %s header", chunkDescription); *(*text + chunksize) = '\0'; if(verbose) { printf("%-10s \"%s\"\n", chunkDescription, *text); }}LONG aiffread(ft, buf, len)ft_t ft;LONG *buf, len;{ struct aiffpriv *p = (struct aiffpriv *) ft->priv;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -