Home My Page Projects Code Snippets Project Openings 3D graphics for Standard ML
Summary Activity SCM

SCM Repository

[sml3d] View of /trunk/sml3d/src/openal/openal-glue.c
ViewVC logotype

View of /trunk/sml3d/src/openal/openal-glue.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 861 - (download) (as text) (annotate)
Mon Apr 26 02:46:02 2010 UTC (9 years, 4 months ago) by jhr
File size: 8963 byte(s)
  Working on OpenAL support
/*! \file openal-glue.c
 *
 * \author John Reppy
 *
 * Support for loading WAV files.  This code is derived from the freealut library, which
 * is available from openal.org.
 *
 * TODO:
 *	support for other file formats (RAW; AU; AIFF; MP3?)
 *	support for streaming
 */

/*
 * COPYRIGHT (c) 2010 John Reppy (http://cs.uchicago.edu/~jhr)
 * All rights reserved.
 */

#include "openal-glue.h"

/* Error codes: these match the codes used in freealut */
#define ALUT_ERROR_NO_ERROR                    0
#define ALUT_ERROR_OUT_OF_MEMORY               0x200
#define ALUT_ERROR_INVALID_ENUM                0x201
#define ALUT_ERROR_INVALID_VALUE               0x202
#define ALUT_ERROR_INVALID_OPERATION           0x203
#define ALUT_ERROR_NO_CURRENT_CONTEXT          0x204
#define ALUT_ERROR_AL_ERROR_ON_ENTRY           0x205
#define ALUT_ERROR_ALC_ERROR_ON_ENTRY          0x206
#define ALUT_ERROR_OPEN_DEVICE                 0x207
#define ALUT_ERROR_CLOSE_DEVICE                0x208
#define ALUT_ERROR_CREATE_CONTEXT              0x209
#define ALUT_ERROR_MAKE_CONTEXT_CURRENT        0x20A
#define ALUT_ERROR_DESTROY_CONTEXT             0x20B
#define ALUT_ERROR_GEN_BUFFERS                 0x20C
#define ALUT_ERROR_BUFFER_DATA                 0x20D
#define ALUT_ERROR_IO_ERROR                    0x20E
#define ALUT_ERROR_UNSUPPORTED_FILE_TYPE       0x20F
#define ALUT_ERROR_UNSUPPORTED_FILE_SUBTYPE    0x210
#define ALUT_ERROR_CORRUPT_OR_TRUNCATED_DATA   0x211

typedef struct {
    void	*data;
    size_t	length;
    int		numChannels;
    int		bitsPerSample;
    float	sampleFrequency;
} BufferData_t;

typedef struct {
    char	*fileName;
    size_t	remainingLength;
    FILE	*fileDescriptor;
    ALenum	error;
} InputStream_t;


static void *InputStreamRead (InputStream_t *stream, size_t length);
static bool InputStreamSkip (InputStream_t *stream, size_t numBytesToSkip);
static bool InputStreamReadUInt16LE (InputStream_t *stream, uint16_t *value);
static bool InputStreamReadInt32BE (InputStream_t *stream, int32_t *value);
static bool InputStreamReadUInt32LE (InputStream_t *stream, uint32_t *value);

static inline bool InputStreamEOF (InputStream_t *stream)
{
    return feof (stream->fileDescriptor);
}

static inline size_t StreamRead (InputStream_t *stream, void *buf, size_t numBytesToRead)
{
    size_t numBytesRead = fread (buf, 1, numBytesToRead, stream->fileDescriptor);

    if (numBytesToRead != numBytesRead) {
	stream->error = ERROR_IO_ERROR;
	return false;
    }
    else
	return true;
}


/********** Public functions **********/

/*! \brief load sound data from a file
 */
void *SML3D_LoadFromFile (
    const char *fileName,	/* sound filename */
    int32_t	*nbytes,	/* [out] for returning size of data */
    int32_t	*nchan,		/* [out] number of channels */
    int32_t	*bps,		/* [out] bits/sample */
    float	*freq,		/* [out] sample frequency */
    int32_t	*sts)		/* [out] load status */
{
    int32_t magic;

    InputStream_t *stream = InputStreamConstructFromFile (fileName);
    if (stream == 0) {
	*sts = ALUT_ERROR_IO_ERROR;
	return 0;
    }

#ifdef RAW_SUPPORT
  /* Raw files have no magic number - so check the fileName extension */
    if (hasSuffixIgnoringCase (fileName, ".raw")) {
	if (nbytes != 0) *nbytes = stream->bytesRemaining;
	if (nchan != 0) *nchan = 1;
	if (bps != 0) *bps = 8;
	if (freq != 0) *freq = 8000.0f;
	return loadRawFile (stream);
    }
#endif

  /* For other file formats, read the quasi-standard four byte magic number */
    if (! InputStreamReadInt32BE (stream, &magic)) {
	DestroyStream (stream);
	return 0;
    }

  /* Magic number 'RIFF' == Microsoft '.wav' format */
    if (magic == 0x52494646) {
	return loadWavFile (stream);
    }

  /* Magic number '.snd' == Sun & Next's '.au' format */
  if (magic == 0x2E736E64)
    {
      return loadAUFile (stream);
    }

  _alutSetError (ALUT_ERROR_UNSUPPORTED_FILE_TYPE);
  return AL_FALSE;
}


static BufferData_t *loadWavFile (InputStream_t *stream)
{
    bool found_header = false;
    uint32_t chunkLength;
    Int32BigEndian magic;
    uint16_t audioFormat;
    uint16_t numChannels;
    uint32_t sampleFrequency;
    uint32_t byteRate;
    uint16_t blockAlign;
    uint16_t bitsPerSample;
    Codec_t *codec = CodecLinear;

    if (!InputStreamReadUInt32LE (stream, &chunkLength)
    || !InputStreamReadInt32BE (stream, &magic))
	return 0;

    if (magic != 0x57415645) {	/* "WAVE" */
	stream->error = ERROR_UNSUPPORTED_FILE_SUBTYPE;
	return 0;
    }

    while (true) {
	if ((! InputStreamReadInt32BE (stream, &magic))
	|| (! InputStreamReadUInt32LE (stream, &chunkLength)))
	    return 0;

	if (magic == 0x666d7420) { /* "fmt " */
	    found_header = true;

	    if (chunkLength < 16) {
		_alutSetError (ALUT_ERROR_CORRUPT_OR_TRUNCATED_DATA);
		return 0;
	    }

	    if ((! InputStreamReadUInt16LE (stream, &audioFormat))
            || (! InputStreamReadUInt16LE (stream, &numChannels))
            || (! InputStreamReadUInt32LE (stream, &sampleFrequency))
            || (! InputStreamReadUInt32LE (stream, &byteRate))
            || (! InputStreamReadUInt16LE (stream, &blockAlign))
            || (! InputStreamReadUInt16LE (stream, &bitsPerSample)))
		return 0;

	    if (!_alutInputStreamSkip (stream, chunkLength - 16))
		return 0;

	    switch (audioFormat) {
	      case 1:            /* PCM */
		codec = (bitsPerSample == 8
			 || endianess () ==
			 LittleEndian) ? _alutCodecLinear : _alutCodecPCM16;
		break;
	      case 7:            /* uLaw */
		bitsPerSample *= 2;       /* ToDo: ??? */
		codec = _alutCodecULaw;
		break;
	      default:
		_alutSetError (ALUT_ERROR_UNSUPPORTED_FILE_SUBTYPE);
		return NULL;
	    } /* switch */
	}
	else if (magic == 0x64617461) {/* "data" */
	    void *data;
	    if (!found_header) {
		/* ToDo: A bit wrong to check here, fmt chunk could come later... */
		stream->error = ALUT_ERROR_CORRUPT_OR_TRUNCATED_DATA;
		return 0;
	    }
	    data = InputStreamRead (stream, chunkLength);
	    if (data == 0)
		return 0;
	  // linear codec
	    return BufferDataConstruct (data, chunkLength, numChannels, bitsPerSample,
			  (ALfloat) sampleFrequency);
	}
	else {
	    if (! InputStreamSkip (stream, chunkLength))
		return 0;
	}

	if ((chunkLength & 1) && !InputStreamEOF (stream)
	&& !_alutInputStreamSkip (stream, 1))
	    return 0;
    }
}

static BufferData *BufferDataConstruct (void *data, size_t length, int numChannels, int bitsPerSample, float sampleFrequency)
{
    BufferData *bufferData = (BufferData *)malloc (sizeof (BufferData));

    if (bufferData != 0) {
	bufferData->data = data;
	bufferData->length = length;
	bufferData->numChannels = numChannels;
	bufferData->bitsPerSample = bitsPerSample;
	bufferData->sampleFrequency = sampleFrequency;
    }

    return bufferData;
}

static InputStream_t *InputStreamConstructFromFile (const char *fileName)
{
    InputStream_t *stream = (InputStream *) malloc (sizeof (InputStream));
    if (stream == 0)
	return 0;

    FILE *fileDescriptor = fopen (fileName, "rb");
    if (fileDescriptor == NULL) {
	free (stream);
	return 0;
    }

  // get the size of the file in a portable way
    fseek (fileDescriptor, 0, SEEK_END);
    long size = ftell (fileDescriptor);
    fseek (fileDescriptor, 0, SEEK_SET);

    char *fileNameBuffer = (char *) malloc (strlen(fileName) + 1);
    if (fileNameBuffer == 0) {
	free (stream);
	return 0;
    }

    stream->fileName = strcpy (fileNameBuffer, fileName);
    stream->remainingLength = size;
    stream->fileDescriptor = fileDescriptor;
    stream->error = ERROR_NO_ERROR;

    return stream;
}


/***** I/O utility functions *****/

static void *InputStreamRead (InputStream_t *stream, size_t length)
{
  void *data = malloc (length);

    if (data != 0) {
	if (! streamRead (stream, data, length)) {
	    free (data);
	    return 0;
	}
    }

    return data;

}

static bool InputStreamSkip (InputStream_t *stream, size_t numBytesToSkip)
{
    if (numBytesToSkip == 0)
	return true;

    if (fseek (stream->fileDescriptor, (long)numBytesToSkip, SEEK_CUR) < 0)
	return false;
    else
	return true;
}

static bool InputStreamReadUInt16LE (InputStream_t *stream, uint16_t *value)
{
    unsigned char buf[2];
    if (!streamRead (stream, buf, sizeof(buf)))
	return false;

    *value = ((uint16_t) buf[1] << 8) | ((uint16_t) buf[0]);

    return true;
}

static bool InputStreamReadInt32BE (InputStream_t *stream, int32_t *value)
{
    unsigned char buf[4];
    if (!streamRead (stream, buf, sizeof (buf)))
	return false;
    *value =
      ((int32_t) buf[0] << 24) |
      ((int32_t) buf[1] << 16) |
      ((int32_t) buf[2] << 8) | ((int32_t) buf[3]);

    return true;
}

static bool InputStreamReadUInt32LE (InputStream_t *stream, uint32_t *value)
{
    unsigned char buf[4];
    if (!streamRead (stream, buf, sizeof (buf)))
      return false;

    *value =
      ((uint32_t) buf[3] << 24) |
      ((int32_t) buf[2] << 16) |
      ((uint32_t) buf[1] << 8) | ((uint32_t) buf[0]);
    return true;
}

root@smlnj-gforge.cs.uchicago.edu
ViewVC Help
Powered by ViewVC 1.0.0