Skip to content
Snippets Groups Projects
ao_wav.c 6.53 KiB
Newer Older
Jack Moffitt's avatar
Jack Moffitt committed
/*
 *
 *  ao_wav.c
 *
 *      Original Copyright (C) Aaron Holtzman - May 1999
 *      Modifications Copyright (C) Stan Seibert - July 2000, July 2001
Jack Moffitt's avatar
Jack Moffitt committed
 *
 *  This file is part of libao, a cross-platform audio output library.  See
Jack Moffitt's avatar
Jack Moffitt committed
 *  README for a history of this source code.
 *
 *  libao is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  libao is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
Jack Moffitt's avatar
Jack Moffitt committed

#define WAVE_FORMAT_PCM  0x0001
#define FORMAT_MULAW     0x0101
#define IBM_FORMAT_ALAW  0x0102
#define IBM_FORMAT_ADPCM 0x0103

#define WAV_HEADER_LEN 44

#define WRITE_U32(buf, x) *(buf)     = (unsigned char)(x&0xff);\
						  *((buf)+1) = (unsigned char)((x>>8)&0xff);\
						  *((buf)+2) = (unsigned char)((x>>16)&0xff);\
						  *((buf)+3) = (unsigned char)((x>>24)&0xff);

#define WRITE_U16(buf, x) *(buf)     = (unsigned char)(x&0xff);\
						  *((buf)+1) = (unsigned char)((x>>8)&0xff);

#define DEFAULT_SWAP_BUFFER_SIZE 2048

struct riff_struct {
	unsigned char id[4];   /* RIFF */
	unsigned int len;
	unsigned char wave_id[4]; /* WAVE */
Jack Moffitt's avatar
Jack Moffitt committed
};


struct chunk_struct 
{
	unsigned char id[4];
	unsigned int len;
};

struct common_struct 
{
	unsigned short wFormatTag;
	unsigned short wChannels;
	unsigned int dwSamplesPerSec;
	unsigned int dwAvgBytesPerSec;
	unsigned short wBlockAlign;
	unsigned short wBitsPerSample;  /* Only for PCM */
};

struct wave_header 
{
	struct riff_struct   riff;
	struct chunk_struct  format;
	struct common_struct common;
	struct chunk_struct  data;
};


static ao_info ao_wav_info =
Jack Moffitt's avatar
Jack Moffitt committed
{
Jack Moffitt's avatar
Jack Moffitt committed
	"WAV file output",
	"wav",
	"Aaron Holtzman <aholtzma@ess.engr.uvic.ca>",
	"Sends output to a .wav file",
	AO_FMT_LITTLE,
	0,
	NULL, /* No options */
	0
Jack Moffitt's avatar
Jack Moffitt committed
};

typedef struct ao_wav_internal
Jack Moffitt's avatar
Jack Moffitt committed
{
	struct wave_header wave;
static int ao_wav_test(void)
Jack Moffitt's avatar
Jack Moffitt committed
{
	return 1; /* File driver always works */
}
static ao_info *ao_wav_driver_info(void)
{
	return &ao_wav_info;
Jack Moffitt's avatar
Jack Moffitt committed
}


static int ao_wav_device_init(ao_device *device)
Jack Moffitt's avatar
Jack Moffitt committed
{
	ao_wav_internal *internal;
Jack Moffitt's avatar
Jack Moffitt committed

	internal = (ao_wav_internal *) malloc(sizeof(ao_wav_internal));
Jack Moffitt's avatar
Jack Moffitt committed

	if (internal == NULL)	
		return 0; /* Could not initialize device memory */
	
	memset(&(internal->wave), 0, sizeof(internal->wave));
	
	device->internal = internal;
Jack Moffitt's avatar
Jack Moffitt committed

	return 1; /* Memory alloc successful */
}
static int ao_wav_set_option(ao_device *device, const char *key, 
			     const char *value)
{
	return 1; /* No options! */
}
static int ao_wav_open(ao_device *device, ao_sample_format *format)
{
	ao_wav_internal *internal = (ao_wav_internal *) device->internal;
	unsigned char buf[WAV_HEADER_LEN];
	int size = 0x7fffffff; /* Use a bogus size initially */
Jack Moffitt's avatar
Jack Moffitt committed

	/* Store information */
	internal->wave.common.wChannels = format->channels;
	internal->wave.common.wBitsPerSample = format->bits;
	internal->wave.common.dwSamplesPerSec = format->rate;
Jack Moffitt's avatar
Jack Moffitt committed

	memset(buf, 0, WAV_HEADER_LEN);

	/* Fill out our wav-header with some information. */
	strncpy(internal->wave.riff.id, "RIFF",4);
	internal->wave.riff.len = size - 8;
	strncpy(internal->wave.riff.wave_id, "WAVE",4);

	strncpy(internal->wave.format.id, "fmt ",4);
	internal->wave.format.len = 16;

	internal->wave.common.wFormatTag = WAVE_FORMAT_PCM;
	internal->wave.common.dwAvgBytesPerSec = 
		internal->wave.common.wChannels * 
		internal->wave.common.dwSamplesPerSec *
		(internal->wave.common.wBitsPerSample >> 3);

	internal->wave.common.wBlockAlign = 
		internal->wave.common.wChannels * 
		(internal->wave.common.wBitsPerSample >> 3);

	strncpy(internal->wave.data.id, "data",4);

	internal->wave.data.len = size - 44;

	strncpy(buf, internal->wave.riff.id, 4);
	WRITE_U32(buf+4, internal->wave.riff.len);
	strncpy(buf+8, internal->wave.riff.wave_id, 4);
	strncpy(buf+12, internal->wave.format.id,4);
	WRITE_U32(buf+16, internal->wave.format.len);
	WRITE_U16(buf+20, internal->wave.common.wFormatTag);
	WRITE_U16(buf+22, internal->wave.common.wChannels);
	WRITE_U32(buf+24, internal->wave.common.dwSamplesPerSec);
	WRITE_U32(buf+28, internal->wave.common.dwAvgBytesPerSec);
	WRITE_U16(buf+32, internal->wave.common.wBlockAlign);
	WRITE_U16(buf+34, internal->wave.common.wBitsPerSample);
	strncpy(buf+36, internal->wave.data.id, 4);
	WRITE_U32(buf+40, internal->wave.data.len);

	if (fwrite(buf, sizeof(char), WAV_HEADER_LEN, device->file) 
	    != WAV_HEADER_LEN) {
		return 0; /* Could not write wav header */
Jack Moffitt's avatar
Jack Moffitt committed
	}

	device->driver_byte_format = AO_FMT_LITTLE;
Jack Moffitt's avatar
Jack Moffitt committed

Jack Moffitt's avatar
Jack Moffitt committed
}


/*
 * play the sample to the already opened file descriptor
 */
static int ao_wav_play(ao_device *device, const char *output_samples, 
			uint_32 num_bytes)
Jack Moffitt's avatar
Jack Moffitt committed
{
	if (fwrite(output_samples, sizeof(char), num_bytes, 
		   device->file) < num_bytes)
		return 0;
	else
		return 1;
	
Jack Moffitt's avatar
Jack Moffitt committed
}

static int ao_wav_close(ao_device *device)
Jack Moffitt's avatar
Jack Moffitt committed
{
	ao_wav_internal *internal = (ao_wav_internal *) device->internal;
	unsigned char buf[4];  /* For holding length values */
Jack Moffitt's avatar
Jack Moffitt committed

Jack Moffitt's avatar
Jack Moffitt committed

	/* Find how long our file is in total, including header */
	size = ftell(device->file);
Jack Moffitt's avatar
Jack Moffitt committed

		return 0;  /* Wav header corrupt */
Jack Moffitt's avatar
Jack Moffitt committed
	}

	/* Go back and set correct length info */
Jack Moffitt's avatar
Jack Moffitt committed

	internal->wave.riff.len = size - 8;
	internal->wave.data.len = size - 44;

	/* Rewind to riff len and write it */
	if (fseek(device->file, 4, SEEK_SET) < 0)
		return 0; /* Wav header corrupt */
	
	WRITE_U32(buf, internal->wave.riff.len);
	if (fwrite(buf, sizeof(char), 4, device->file) < 4)
	  return 0; /* Wav header corrupt */
	/* Rewind to data len and write it */
	if (fseek(device->file, 40, SEEK_SET) < 0)
		return 0; /* Wav header corrupt */
	
	WRITE_U32(buf, internal->wave.data.len);
	if (fwrite(buf, sizeof(char), 4, device->file) < 4)
	  return 0; /* Wav header corrupt */

       
	return 1; /* Wav header correct */
Jack Moffitt's avatar
Jack Moffitt committed
}

static void ao_wav_device_clear(ao_device *device)
	ao_wav_internal *internal = (ao_wav_internal *) device->internal;
ao_functions ao_wav = {
	ao_wav_test,
	ao_wav_driver_info,
	ao_wav_device_init,
	ao_wav_set_option,
	ao_wav_open,
	ao_wav_play,
	ao_wav_close,
	ao_wav_device_clear
Jack Moffitt's avatar
Jack Moffitt committed
};