filehdr.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This file contains a set of Very Paranoid routines to convert
* audio file headers to in-core audio headers and vice versa.
*
* They are robust enough to handle any random file input without
* crashing miserably. Of course, bad audio headers coming from
* the calling program can cause significant problems.
*/
#include <stdlib.h>
#include <memory.h>
#include <fcntl.h>
#include <errno.h> /* needed for large file error checking */
#include <stdio.h>
#include <libintl.h>
#include <math.h>
#include <libaudio_impl.h> /* include other audio hdr's */
/* Round up to a double boundary */
#define HEADER_BUFFER 100
static int audio_encode_aiff(Audio_hdr *, unsigned char *, unsigned int *);
static int audio_encode_au(Audio_hdr *, char *, unsigned int,
unsigned char *, unsigned int *);
static int audio_encode_wav(Audio_hdr *, unsigned char *, unsigned int *);
static double convert_from_ieee_extended(unsigned char *);
static void convert_to_ieee_extended(double, unsigned char *);
/*
* Write an audio file header to an output stream.
*
* The file header is encoded from the supplied Audio_hdr structure.
* If 'infop' is not NULL, it is the address of a buffer containing 'info'
* data. 'ilen' specifies the size of this buffer.
* The entire file header will be zero-padded to a double-word boundary.
*
* Note that the file header is stored on-disk in big-endian format,
* regardless of the machine type.
*
* Note also that the output file descriptor must not have been set up
* non-blocking i/o. If non-blocking behavior is desired, set this
* flag after writing the file header.
*/
int
unsigned int ilen)
/* file descriptor */
/* audio header */
/* audio header type */
/* info buffer pointer */
/* buffer size */
{
int err;
unsigned blen;
unsigned char *buf; /* temporary buffer */
/* create tmp buf for the encoding routines to work with */
return (AUDIO_UNIXERROR);
}
switch (file_type) {
case FILE_AU:
break;
case FILE_WAV:
break;
case FILE_AIFF:
break;
default:
return (AUDIO_ERR_BADFILETYPE);
}
if (err != AUDIO_SUCCESS) {
return (err);
}
/* Write and free the holding buffer */
return (AUDIO_SUCCESS);
}
/*
* Rewrite the aiff header chunk length and the data chunk length fields.
*/
static int
unsigned int bytes_per_sample)
{
unsigned int offset;
unsigned int tmp_uint;
unsigned int tmp_uint2;
unsigned int total_size;
/* first fix aiff_hdr_size */
AUDIO_AIFF_COMM_CHUNK_SIZE + sizeof (aiff_ssnd_chunk_t);
offset = sizeof (int);
return (AUDIO_ERR_NOEFFECT);
}
return (AUDIO_ERR_NOEFFECT);
}
/* fix the frame count */
sizeof (short);
return (AUDIO_ERR_NOEFFECT);
}
return (AUDIO_ERR_NOEFFECT);
}
/* fix the data size */
sizeof (int);
return (AUDIO_ERR_NOEFFECT);
}
return (AUDIO_ERR_NOEFFECT);
}
return (AUDIO_SUCCESS);
}
/*
* Rewrite the data size field for the .au file format. Rewrite the audio
* file header au_data_size field with the supplied value. Otherwise,
* return AUDIO_ERR_NOEFFECT.
*/
static int
{
int err;
int data;
int offset;
/* seek to the position of the au_data_size member */
return (AUDIO_ERR_NOEFFECT);
}
/* Encode the 32-bit integer header field */
/* Write the data */
return (AUDIO_SUCCESS);
}
/*
* Rewrite the riff header chunk length and the data chunk length fields.
*/
static int
{
int calc_size;
int err;
int data;
int offset;
/* seek to the position of the riff header chunk length */
sizeof (fhdr.wav_riff_size);
return (AUDIO_ERR_NOEFFECT);
}
/* Write the data */
/* now seek to the position of the data chunk length */
return (AUDIO_ERR_NOEFFECT);
}
/* Write the data */
return (AUDIO_SUCCESS);
}
/*
* Rewrite the data size field of an audio header to the output stream if
* the output file is capable of seeking.
*/
int
unsigned int channels, unsigned int bytes_per_sample)
/* file descriptor */
/* audio file type */
/* new data size */
/* number of channels */
/* number of bytes per sample */
{
int fcntl_err;
/* Can we seek back in this file and write without appending? */
/* Large file encountered (probably) */
perror("fcntl");
exit(1);
return (AUDIO_ERR_NOEFFECT);
}
switch (file_type) {
case FILE_AU:
case FILE_WAV:
case FILE_AIFF:
default:
return (AUDIO_ERR_BADFILETYPE);
}
}
/*
* Decode an audio file header from an input stream.
*
* The file header is decoded into the supplied Audio_hdr structure, regardless
* of the file format. Thus .wav and .aiff files look like .au files once the
* header is decoded.
*
* If 'infop' is not NULL, it is the address of a buffer to which the
* 'info' portion of the file header will be copied. 'ilen' specifies
* the maximum number of bytes to copy. The buffer will be NULL-terminated,
* even if it means over-writing the last byte.
*
* Note that the .au file header is stored on-disk in big-endian format,
* regardless of the machine type. This may not have been true if
* the file was written on a non-Sun machine. For now, such
* files will appear invalid.
*
* Note also that the input file descriptor must not have been set up
* non-blocking i/o. If non-blocking behavior is desired, set this
* flag after reading the file header.
*/
int
unsigned int ilen)
/* input file descriptor */
/* output audio header */
/* audio file type */
/* info buffer pointer */
/* buffer size */
{
int err;
int dsize;
int isize;
unsigned resid;
unsigned char buf[HEADER_BUFFER];
/* decode the file header and fill in the hdrp structure */
goto checkerror;
}
/* Stat the file, to determine if it is a regular file. */
if (err < 0) {
return (AUDIO_UNIXERROR);
}
/*
* If au_data_size is not indeterminate (i.e., this isn't a pipe),
* try to validate the au_offset and au_data_size.
*/
/* Only trust the size for regular files */
_MGET_("Warning: More audio data "
"than the file header specifies\n"));
_MGET_("Warning: Less audio data "
"than the file header specifies\n"));
}
}
}
/*
* Deal with extra header data.
*/
/*
* If infop is non-NULL, try to read in the info data
*/
goto checkerror;
/* Zero any residual bytes in the text buffer */
else
}
/*
* If we truncated the info, seek or read data until info size
* is satisfied. If regular file, seek nearly to end and check
* for eof.
*/
if (resid != 0) {
if ((err < 0) ||
goto checkerror;
} else while (resid != 0) {
goto checkerror;
}
}
return (AUDIO_SUCCESS);
perror("read");
exit(1);
} else {
}
return (AUDIO_SUCCESS);
}
/*
* Return TRUE if the named file is an audio file. Else, return FALSE.
*/
int
audio_isaudiofile(char *name)
{
int fd;
int err;
int file_type; /* ignored */
int isize;
unsigned char buf[sizeof (au_filehdr_t)];
/* Open the file (set O_NONBLOCK in case the name refers to a device) */
if (fd < 0) {
perror("open");
exit(1);
} else {
return (FALSE);
}
}
/* Read the header (but not the text info). */
if (err < 0) {
perror("open");
exit(1);
} else {
return (FALSE);
}
}
AUDIO_SUCCESS)) {
} else {
return (FALSE);
}
}
/*
* audio_endian()
*
* This routine tests the magic number at the head of a buffer
* containing the file header. The first thing in the header
* should be the magic number.
*/
static int
{
unsigned int magic1;
unsigned int magic2;
/* put the buffer into an int that is aligned properly */
return (AUDIO_ENDIAN_BIG);
return (AUDIO_ENDIAN_SMALL);
} else if (magic1 == AUDIO_AIFF_HDR_CHUNK_ID ||
return (AUDIO_ENDIAN_BIG);
}
return (AUDIO_ENDIAN_UNKNOWN);
}
/*
* Decode an aiff file header. Unlike .au and .wav, we have to process
* by chunk.
*/
static int
{
int data_type;
int hdr_sizes;
int sr;
short bits_per_sample;
short channels;
/* we've read in 4 bytes, read in the rest of the wav header */
/* read in the rest of the header */
return (AUDIO_UNIXERROR);
}
/* see which kind of audio file we have */
if (data_type != AUDIO_AIFF_HDR_FORM_AIFF) {
/* we can't play this version of a .aiff file */
return (AUDIO_ERR_BADFILEHDR);
}
/*
* We don't know what the chunk order will be, so read each, getting
* the data we need from each. Eventually we'll get to the end of
* the file, in which case we should have all of the info on the
* file that we need. We then lseek() back to the data to play.
*
* We start each loop by reading the chunk ID.
*/
switch (ID) {
case AUDIO_AIFF_COMM_ID:
/* read in the rest of the COMM chunk */
sizeof (comm_chunk.aiff_comm_ID);
size) {
return (AUDIO_UNIXERROR);
}
break;
case AUDIO_AIFF_SSND_ID:
/* read in the rest of the INST chunk */
size = sizeof (ssnd_chunk) -
sizeof (ssnd_chunk.aiff_ssnd_ID);
size) {
return (AUDIO_UNIXERROR);
}
/*
* This has to be the last chunk because the audio data
* follows. So we should have all we need to tell the
* app the format information.
*/
&channels);
/* use channels to convert from short to int */
switch (bits_per_sample) {
break;
break;
default:
return (AUDIO_ERR_BADFILEHDR);
}
&size);
sizeof (ssnd_chunk.aiff_ssnd_block_size);
hdr_sizes += sizeof (ssnd_chunk);
return (AUDIO_SUCCESS);
default:
/*
* Unknown chunk. Read the size, which is right after
* the ID. Then seek past it to get to the next chunk.
*/
return (AUDIO_UNIXERROR);
}
return (AUDIO_UNIXERROR);
}
break;
}
}
return (AUDIO_SUCCESS);
} /* decode_aiff() */
/*
* Decode an au file header.
*/
static int
{
int offset;
int size;
if (read_info) {
/* read in the rest of the au header */
return (AUDIO_UNIXERROR);
}
}
/* put the buffer into a structure that is aligned properly */
/* Decode the 32-bit integer header fields. */
/* Set the info field size (ie, number of bytes left before data). */
return (AUDIO_SUCCESS);
} /* decode_au() */
/*
* Decode a wav file header.
*
* .wav files are stored on-disk in little-endian format.
*/
static int
{
short bits_per_sample;
short encoding;
/* we've read in 4 bytes, read in the rest of the wav header */
/* read in the rest of the header */
return (AUDIO_UNIXERROR);
}
/* put the buffer into a structure that is aligned properly */
/* make sure we have the correct RIFF type */
if (ID != AUDIO_WAV_TYPE_ID) {
/* not a wave file */
return (AUDIO_ERR_BADFILEHDR);
}
/* decode the fields */
if (ID != AUDIO_WAV_FORMAT_ID) {
/* mangled format */
return (AUDIO_ERR_BADFILEHDR);
}
/* convert .wav encodings to .au encodings */
switch (encoding) {
switch (bits_per_sample) {
break;
break;
default:
return (AUDIO_ERR_BADFILEHDR);
}
break;
break;
break;
default:
return (AUDIO_ERR_BADFILEHDR);
}
return (AUDIO_SUCCESS);
} /* decode_wav() */
/*
* Try to decode buffer containing an audio file header into an audio header.
*/
int
/* file descriptor */
/* buffer address */
/* audio file type */
/* output audio header */
/* output size of info */
{
int err;
/* Test for .au first */
/*
* When cat'ing a file, audioconvert will read the whole header
* trying to figure out the file. audioplay however, does not.
* Hence we check if this is a pipe and do not attempt to read
* any more header info if the file type is already known.
* Otherwise we overwrite the header data already in the buffer.
*/
return (AUDIO_ERR_BADFILEHDR);
}
} else {
/*
* Not an au file, or file type unknown. Reread the header's
* magic number. Fortunately this is always an int.
*/
/* test the magic number to determine the endian */
return (AUDIO_ERR_BADFILEHDR);
}
}
/* decode the different file types, putting the data into hdrp */
switch (*file_type) {
case FILE_AU:
return (err);
}
break;
case FILE_WAV:
return (err);
}
break;
case FILE_AIFF:
return (err);
}
break;
default:
return (AUDIO_ERR_BADFILEHDR);
}
/* Convert from file format info to audio format info */
case AUDIO_AU_ENCODING_ULAW:
break;
case AUDIO_AU_ENCODING_ALAW:
break;
} else {
}
break;
break;
break;
break;
case AUDIO_AU_ENCODING_FLOAT:
break;
case AUDIO_AU_ENCODING_DOUBLE:
break;
break;
break;
break;
default:
return (AUDIO_ERR_BADFILEHDR);
}
return (AUDIO_SUCCESS);
}
/*
* Encode a .aiff file header from the supplied Audio_hdr structure and
* store in the supplied char* buffer. blen is the size of the buffer to
* store the header in. Unlike .au and .wav we can't cast to a data structure.
* We have to build it one chunk at a time.
*
* NOTE: .aiff doesn't support unsigned 8-bit linear PCM.
*/
static int
/* audio header */
/* output buffer */
/* output buffer size */
{
int buf_size = 0;
int encoding;
/* the only encoding we support for .aiff is signed linear PCM */
return (AUDIO_ERR_ENCODING);
}
/* build the header chunk */
/* needs to be fixed when closed */
/* build the COMM chunk */
/* needs to be fixed when closed */
/* build the SSND chunk */
/* needs to be fixed when closed */
buf_size += sizeof (ssnd_chunk);
return (AUDIO_SUCCESS);
} /* audio_encode_aiff() */
/*
* Encode a .au file header from the supplied Audio_hdr structure and
* store in the supplied char* buffer. blen is the size of the buffer to
* store the header in. If 'infop' is not NULL, it is the address of a
* buffer containing 'info' data. 'ilen' specifies the size of this buffer.
* The entire file header will be zero-padded to a double-word boundary.
*
* NOTE: .au doesn't support unsigned 8-bit linear PCM.
*/
static int
/* audio header */
/* info buffer pointer */
/* info buffer size */
/* output buffer */
/* output buffer size */
{
int encoding;
int hdrsize;
int magic;
int offset;
/*
* Set the size of the real header (hdr size + info size).
* If no supplied info, make sure a minimum size is accounted for.
* Also, round the whole thing up to double-word alignment.
*/
ilen = 4;
}
/* Check the data encoding. */
case AUDIO_ENCODING_LINEAR8:
return (AUDIO_ERR_ENCODING); /* we don't support ulinear */
case AUDIO_ENCODING_ULAW:
return (AUDIO_ERR_BADHDR);
switch (hdrp->bytes_per_unit) {
case 1:
break;
default:
return (AUDIO_ERR_BADHDR);
}
break;
case AUDIO_ENCODING_ALAW:
return (AUDIO_ERR_BADHDR);
switch (hdrp->bytes_per_unit) {
case 1:
break;
default:
return (AUDIO_ERR_BADHDR);
}
break;
case AUDIO_ENCODING_LINEAR:
return (AUDIO_ERR_BADHDR);
switch (hdrp->bytes_per_unit) {
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
default:
return (AUDIO_ERR_BADHDR);
}
break;
case AUDIO_ENCODING_FLOAT:
return (AUDIO_ERR_BADHDR);
switch (hdrp->bytes_per_unit) {
case 4:
break;
case 8:
break;
default:
return (AUDIO_ERR_BADHDR);
}
break;
case AUDIO_ENCODING_G721:
return (AUDIO_ERR_BADHDR);
return (AUDIO_ERR_BADHDR);
else
break;
case AUDIO_ENCODING_G723:
return (AUDIO_ERR_BADHDR);
else
return (AUDIO_ERR_BADHDR);
break;
default:
return (AUDIO_ERR_BADHDR);
}
/* copy the fhdr into the supplied buffer - make sure it'll fit */
/* XXX - is this apropriate? */
return (AUDIO_EOF);
}
/* reset blen to actual size of hdr data */
/* Encode the audio header structure. */
/* Copy to the buffer */
/* Copy the info data, if present */
}
}
/* buf now has the data, just return ... */
return (AUDIO_SUCCESS);
} /* audio_encode_au() */
/*
* Encode a .wav file header from the supplied Audio_hdr structure and
* store in the supplied char* buffer. blen is the size of the buffer to
* store the header in. .wav doesn't support an information string like
* .au does.
*
* NOTE: .wav only supports a few encoding methods.
*/
static int
/* audio header */
/* output buffer */
/* output buffer size */
{
int bytes_per_second;
int bytes_per_sample;
int bits_per_sample;
int id;
int length;
int type;
short encoding;
/* make sure we've got valid encoding and precision settings for .wav */
case AUDIO_ENCODING_LINEAR8:
return (AUDIO_ERR_ENCODING);
}
break;
case AUDIO_ENCODING_ULAW:
return (AUDIO_ERR_ENCODING);
}
break;
case AUDIO_ENCODING_ALAW:
return (AUDIO_ERR_ENCODING);
}
break;
case AUDIO_ENCODING_LINEAR:
return (AUDIO_ERR_ENCODING);
}
break;
default:
return (AUDIO_ERR_ENCODING);
}
/* fill in the riff chunk */
/* fill in the type chunk */
/* fill in the format chunk */
/* fill in the data chunk */
/* copy to the buffer */
return (AUDIO_SUCCESS);
} /* audio_encode_wav() */
/*
* Utility routine used to convert 10 byte IEEE extended float into
* a regular double. Raw data arrives in an unsigned char array. Because
* this is for sample rate, which is always positive, we don't worry
* about the sign.
*/
static double
convert_from_ieee_extended(unsigned char *data)
{
double value = 0.0;
unsigned long high_mantissa;
unsigned long low_mantissa;
int exponent;
/* first 2 bytes are the exponent */
(unsigned long)data[5];
(unsigned long)data[9];
/* convert exponent and mantissas into a real double */
/* everything is 0, so we're done */
value = 0.0;
} else {
} else {
/* convert exponent from being unsigned to signed */
exponent -= 0x3fff;
exponent -= 31;
exponent -= 32;
}
}
return (value);
}
/*
* Utility routine to convert a double into 10 byte IEEE extended floating
* point. The new number is placed into the unsigned char array. This is a
* very brain dead convesion routine. It only supports integers, but then
* that should be all we need for sample rate.
*/
static void
{
double fmantissa;
int exponent;
int mantissa;
exponent = 16398;
while (fmantissa < 44000) {
fmantissa *= 2;
exponent--;
}
data[6] = 0;
data[7] = 0;
data[8] = 0;
data[9] = 0;
}