Blame src/libout123/wav.c

Packit c32a2d
/*
Packit c32a2d
	wav.c: write wav/au/cdr files (and headerless raw
Packit c32a2d
Packit c32a2d
	copyright ?-2015 by the mpg123 project - free software under the terms of the LGPL 2.1
Packit c32a2d
	see COPYING and AUTHORS files in distribution or http://mpg123.org
Packit c32a2d
	initially written by Samuel Audet
Packit c32a2d
Packit c32a2d
	Geez, why are WAV RIFF headers are so secret?  I got something together,
Packit c32a2d
	but wow...  anyway, I hope someone will find this useful.
Packit c32a2d
	- Samuel Audet
Packit c32a2d
Packit c32a2d
	minor simplifications and ugly AU/CDR format stuff by MH
Packit c32a2d
Packit c32a2d
	It's not a very clean code ... Fix this!
Packit c32a2d
Packit c32a2d
	ThOr: The usage of stdio streams means we loose control over what data is actually written. On a full disk, fwrite() happily suceeds for ages, only a fflush fails.
Packit c32a2d
	Now: Do we want to fflush() after every write? That defeats the purpose of buffered I/O. So, switching to good old write() is an option (kernel doing disk buffering anyway).
Packit c32a2d
Packit c32a2d
	ThOr: Again reworked things for libout123, with non-static state.
Packit c32a2d
	This set of builtin "modules" is what we can use in automated tests
Packit c32a2d
	of libout123. Code still not very nice, but I tried to keep modification
Packit c32a2d
	of what stood the test of time minimal. One still can add a module to
Packit c32a2d
	libout123 that uses sndfile and similar libraries for more choice on writing
Packit c32a2d
	output files.
Packit c32a2d
*/
Packit c32a2d
Packit c32a2d
#include "out123_int.h"
Packit c32a2d
#include "wav.h"
Packit c32a2d
Packit c32a2d
#include <errno.h>
Packit c32a2d
#include "debug.h"
Packit c32a2d
Packit c32a2d
/* Create the two WAV headers. */
Packit c32a2d
Packit c32a2d
#define WAVE_FORMAT 1
Packit c32a2d
#define RIFF_NAME riff_template
Packit c32a2d
#define RIFF_STRUCT_NAME riff
Packit c32a2d
#include "wavhead.h"
Packit c32a2d
Packit c32a2d
#undef WAVE_FORMAT
Packit c32a2d
#undef RIFF_NAME
Packit c32a2d
#undef RIFF_STRUCT_NAME
Packit c32a2d
#define WAVE_FORMAT 3
Packit c32a2d
#define RIFF_NAME riff_float_template
Packit c32a2d
#define RIFF_STRUCT_NAME riff_float
Packit c32a2d
#define FLOATOUT
Packit c32a2d
#include "wavhead.h"
Packit c32a2d
Packit c32a2d
/* AU header struct... */
Packit c32a2d
Packit c32a2d
struct auhead {
Packit c32a2d
  byte magic[4];
Packit c32a2d
  byte headlen[4];
Packit c32a2d
  byte datalen[4];
Packit c32a2d
  byte encoding[4];
Packit c32a2d
  byte rate[4];
Packit c32a2d
  byte channels[4];
Packit c32a2d
  byte dummy[8];
Packit c32a2d
} const auhead_template = { 
Packit c32a2d
  { 0x2e,0x73,0x6e,0x64 } , { 0x00,0x00,0x00,0x20 } , 
Packit c32a2d
  { 0xff,0xff,0xff,0xff } , { 0,0,0,0 } , { 0,0,0,0 } , { 0,0,0,0 } , 
Packit c32a2d
  { 0,0,0,0,0,0,0,0 }};
Packit c32a2d
Packit c32a2d
struct wavdata
Packit c32a2d
{
Packit c32a2d
	FILE *wavfp;
Packit c32a2d
	long datalen;
Packit c32a2d
	int flipendian;
Packit c32a2d
	int bytes_per_sample;
Packit c32a2d
	int floatwav; /* If we write a floating point WAV file. */
Packit c32a2d
	/* 
Packit c32a2d
		Open routines only prepare a header, stored here and written on first
Packit c32a2d
		actual data write. If no data is written at all, proper files will
Packit c32a2d
		still get a header via the update at closing; non-seekable streams will
Packit c32a2d
		just have no no header if there is no data.
Packit c32a2d
	*/
Packit c32a2d
	void *the_header;
Packit c32a2d
	size_t the_header_size;
Packit c32a2d
};
Packit c32a2d
Packit c32a2d
static struct wavdata* wavdata_new(void)
Packit c32a2d
{
Packit c32a2d
	struct wavdata *wdat = malloc(sizeof(struct wavdata));
Packit c32a2d
	if(wdat)
Packit c32a2d
	{
Packit c32a2d
		wdat->wavfp = NULL;
Packit c32a2d
		wdat->datalen = 0;
Packit c32a2d
		wdat->flipendian = 0;
Packit c32a2d
		wdat->bytes_per_sample = -1;
Packit c32a2d
		wdat->floatwav = 0;
Packit c32a2d
		wdat->the_header = NULL;
Packit c32a2d
		wdat->the_header_size = 0;
Packit c32a2d
	}
Packit c32a2d
	return wdat;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static void wavdata_del(struct wavdata *wdat)
Packit c32a2d
{
Packit c32a2d
	if(!wdat) return;
Packit c32a2d
	if(wdat->wavfp && wdat->wavfp != stdout)
Packit c32a2d
		compat_fclose(wdat->wavfp);
Packit c32a2d
	if(wdat->the_header)
Packit c32a2d
		free(wdat->the_header);
Packit c32a2d
	free(wdat);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Pointer types are for pussies;-) */
Packit c32a2d
static void* wavhead_new(void const *template, size_t size)
Packit c32a2d
{
Packit c32a2d
	void *header = malloc(size);
Packit c32a2d
	if(header)
Packit c32a2d
		memcpy(header, template, size);
Packit c32a2d
	return header;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Convertfunctions: */
Packit c32a2d
/* always little endian */
Packit c32a2d
Packit c32a2d
static void long2littleendian(long inval,byte *outval,int b)
Packit c32a2d
{
Packit c32a2d
  int i;
Packit c32a2d
  for(i=0;i
Packit c32a2d
    outval[i] = (inval>>(i*8)) & 0xff;
Packit c32a2d
  } 
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* always big endian */
Packit c32a2d
static void long2bigendian(long inval,byte *outval,int b)
Packit c32a2d
{
Packit c32a2d
  int i;
Packit c32a2d
  for(i=0;i
Packit c32a2d
    outval[i] = (inval>>((b-i-1)*8)) & 0xff;
Packit c32a2d
  }
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static long from_little(byte *inval, int b)
Packit c32a2d
{
Packit c32a2d
	long ret = 0;
Packit c32a2d
	int i;
Packit c32a2d
	for(i=0;i
Packit c32a2d
Packit c32a2d
	return ret;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static int testEndian(void) 
Packit c32a2d
{
Packit c32a2d
  long i,a=0,b=0,c=0;
Packit c32a2d
  int ret = 0;
Packit c32a2d
Packit c32a2d
  for(i=0;i
Packit c32a2d
    ((byte *)&a)[i] = i;
Packit c32a2d
    b<<=8;
Packit c32a2d
    b |= i;
Packit c32a2d
    c |= i << (i*8);
Packit c32a2d
  }
Packit c32a2d
  if(a == b)
Packit c32a2d
      ret = 1;
Packit c32a2d
  else if(a != c) {
Packit c32a2d
      ret = -1;
Packit c32a2d
  }
Packit c32a2d
  return ret;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* return: 0 is good, -1 is bad */
Packit c32a2d
static int open_file(struct wavdata *wdat, char *filename)
Packit c32a2d
{
Packit c32a2d
	debug2("open_file(%p, %s)", (void*)wdat, filename ? filename : "<nil>");
Packit c32a2d
	if(!wdat)
Packit c32a2d
		return -1;
Packit c32a2d
#if defined(HAVE_SETUID) && defined(HAVE_GETUID)
Packit c32a2d
	/* TODO: get rid of that and settle that you rather not install mpg123
Packit c32a2d
	   setuid-root. Why should you?
Packit c32a2d
	   In case this program is setuid, create files owned by original user. */
Packit c32a2d
	setuid(getuid());
Packit c32a2d
#endif
Packit c32a2d
	if(!filename || !strcmp("-",filename) || !strcmp("", filename))
Packit c32a2d
	{
Packit c32a2d
		wdat->wavfp = stdout;
Packit c32a2d
#ifdef WIN32
Packit c32a2d
		_setmode(STDOUT_FILENO, _O_BINARY);
Packit c32a2d
#endif
Packit c32a2d
		/* If stdout is redirected to a file, seeks suddenly can work.
Packit c32a2d
		Doing one here to ensure that such a file has the same output
Packit c32a2d
		it had when opening directly as such. */
Packit c32a2d
		fseek(wdat->wavfp, 0L, SEEK_SET);
Packit c32a2d
		return 0;
Packit c32a2d
	}
Packit c32a2d
	else
Packit c32a2d
	{
Packit c32a2d
		wdat->wavfp = compat_fopen(filename, "wb");
Packit c32a2d
		if(!wdat->wavfp)
Packit c32a2d
			return -1;
Packit c32a2d
		else
Packit c32a2d
			return 0;
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* return: 0 is good, -1 is bad
Packit c32a2d
   Works for any partial state of setup, especially should not complain if
Packit c32a2d
   ao->userptr == NULL. */
Packit c32a2d
static int close_file(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	struct wavdata *wdat = ao->userptr;
Packit c32a2d
	int ret = 0;
Packit c32a2d
Packit c32a2d
	if(wdat->wavfp != NULL && wdat->wavfp != stdout)
Packit c32a2d
	{
Packit c32a2d
		if(compat_fclose(wdat->wavfp))
Packit c32a2d
		{
Packit c32a2d
			if(!AOQUIET)
Packit c32a2d
				error1("problem closing the audio file, probably because of flushing to disk: %s\n", strerror(errno));
Packit c32a2d
			ret = -1;
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	/* Always cleanup here. */
Packit c32a2d
	wdat->wavfp = NULL;
Packit c32a2d
	wavdata_del(wdat);
Packit c32a2d
	ao->userptr = NULL;
Packit c32a2d
	return ret;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* return: 0 is good, -1 is bad */
Packit c32a2d
static int write_header(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	struct wavdata *wdat = ao->userptr;
Packit c32a2d
Packit c32a2d
	if(!wdat)
Packit c32a2d
		return 0;
Packit c32a2d
Packit c32a2d
	if(
Packit c32a2d
		wdat->the_header_size > 0
Packit c32a2d
	&&	(
Packit c32a2d
			fwrite(wdat->the_header, wdat->the_header_size, 1, wdat->wavfp) != 1
Packit c32a2d
		|| fflush(wdat->wavfp)
Packit c32a2d
		)
Packit c32a2d
	)
Packit c32a2d
	{
Packit c32a2d
		if(!AOQUIET)
Packit c32a2d
			error1("cannot write header: %s", strerror(errno));
Packit c32a2d
		return -1;
Packit c32a2d
	}
Packit c32a2d
	else return 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int au_open(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	struct wavdata *wdat   = NULL;
Packit c32a2d
	struct auhead  *auhead = NULL;
Packit c32a2d
Packit c32a2d
	if(ao->format < 0)
Packit c32a2d
	{
Packit c32a2d
		ao->rate = 44100;
Packit c32a2d
		ao->channels = 2;
Packit c32a2d
		ao->format = MPG123_ENC_SIGNED_16;
Packit c32a2d
		return 0;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	if(ao->format & MPG123_ENC_FLOAT)
Packit c32a2d
	{
Packit c32a2d
		if(!AOQUIET)
Packit c32a2d
			error("AU file support for float values not there yet");
Packit c32a2d
		goto au_open_bad;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	if(
Packit c32a2d
		!(wdat   = wavdata_new())
Packit c32a2d
	||	!(auhead = wavhead_new(&auhead_template, sizeof(auhead_template)))
Packit c32a2d
	)
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_DOOM;
Packit c32a2d
		goto au_open_bad;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	wdat->the_header      = auhead;
Packit c32a2d
	wdat->the_header_size = sizeof(*auhead);
Packit c32a2d
Packit c32a2d
	wdat->flipendian = 0;
Packit c32a2d
Packit c32a2d
	switch(ao->format)
Packit c32a2d
	{
Packit c32a2d
		case MPG123_ENC_SIGNED_16:
Packit c32a2d
		{
Packit c32a2d
			int endiantest = testEndian();
Packit c32a2d
			if(endiantest == -1)
Packit c32a2d
				goto au_open_bad;
Packit c32a2d
			wdat->flipendian = !endiantest; /* big end */
Packit c32a2d
			long2bigendian(3,auhead->encoding,sizeof(auhead->encoding));
Packit c32a2d
		}
Packit c32a2d
		break;
Packit c32a2d
		case MPG123_ENC_UNSIGNED_8:
Packit c32a2d
			ao->format = MPG123_ENC_ULAW_8;
Packit c32a2d
		case MPG123_ENC_ULAW_8:
Packit c32a2d
			long2bigendian(1,auhead->encoding,sizeof(auhead->encoding));
Packit c32a2d
		break;
Packit c32a2d
		default:
Packit c32a2d
			if(!AOQUIET)
Packit c32a2d
				error("AU output is only a hack. This audio mode isn't supported yet.");
Packit c32a2d
			goto au_open_bad;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	long2bigendian(0xffffffff,auhead->datalen,sizeof(auhead->datalen));
Packit c32a2d
	long2bigendian(ao->rate,auhead->rate,sizeof(auhead->rate));
Packit c32a2d
	long2bigendian(ao->channels,auhead->channels,sizeof(auhead->channels));
Packit c32a2d
Packit c32a2d
	if(open_file(wdat, ao->device) < 0)
Packit c32a2d
		goto au_open_bad;
Packit c32a2d
Packit c32a2d
	wdat->datalen = 0;
Packit c32a2d
Packit c32a2d
	ao->userptr = wdat;
Packit c32a2d
	return 0;
Packit c32a2d
Packit c32a2d
au_open_bad:
Packit c32a2d
	if(auhead)
Packit c32a2d
		free(auhead);
Packit c32a2d
	if(wdat)
Packit c32a2d
	{
Packit c32a2d
		wdat->the_header = NULL;
Packit c32a2d
		wavdata_del(wdat);
Packit c32a2d
	}
Packit c32a2d
	return -1;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int cdr_open(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	struct wavdata *wdat   = NULL;
Packit c32a2d
Packit c32a2d
	if(ao->format < 0)
Packit c32a2d
	{
Packit c32a2d
		ao->rate = 44100;
Packit c32a2d
		ao->channels = 2;
Packit c32a2d
		ao->format = MPG123_ENC_SIGNED_16;
Packit c32a2d
		return 0;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	if(
Packit c32a2d
		ao->format != MPG123_ENC_SIGNED_16
Packit c32a2d
	||	ao->rate != 44100
Packit c32a2d
	||	ao->channels != 2
Packit c32a2d
	)
Packit c32a2d
	{
Packit c32a2d
		if(!AOQUIET)
Packit c32a2d
			error("Oops .. not forced to 16 bit, 44 kHz, stereo?");
Packit c32a2d
		goto cdr_open_bad;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	if(!(wdat = wavdata_new()))
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_DOOM;
Packit c32a2d
		goto cdr_open_bad;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	wdat->flipendian = !testEndian(); /* big end */
Packit c32a2d
Packit c32a2d
	if(open_file(wdat, ao->device) < 0)
Packit c32a2d
	{
Packit c32a2d
		if(!AOQUIET)
Packit c32a2d
			error("cannot open file for writing");
Packit c32a2d
		goto cdr_open_bad;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	ao->userptr = wdat;
Packit c32a2d
	return 0;
Packit c32a2d
cdr_open_bad:
Packit c32a2d
	if(wdat)
Packit c32a2d
		wavdata_del(wdat);
Packit c32a2d
	return -1;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* RAW files are headerless WAVs where the format does not matter. */
Packit c32a2d
int raw_open(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	struct wavdata *wdat;
Packit c32a2d
Packit c32a2d
	if(ao->format < 0)
Packit c32a2d
	{
Packit c32a2d
		ao->rate = 44100;
Packit c32a2d
		ao->channels = 2;
Packit c32a2d
		ao->format = MPG123_ENC_SIGNED_16;
Packit c32a2d
		return 0;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	if(!(wdat = wavdata_new()))
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_DOOM;
Packit c32a2d
		goto raw_open_bad;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	if(open_file(wdat, ao->device) < 0)
Packit c32a2d
		goto raw_open_bad;
Packit c32a2d
Packit c32a2d
	ao->userptr = wdat;
Packit c32a2d
	return 1;
Packit c32a2d
raw_open_bad:
Packit c32a2d
	if(wdat)
Packit c32a2d
		wavdata_del(wdat);
Packit c32a2d
	return -1;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int wav_open(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	int bps;
Packit c32a2d
	struct wavdata    *wdat      = NULL;
Packit c32a2d
	struct riff       *inthead   = NULL;
Packit c32a2d
	struct riff_float *floathead = NULL;
Packit c32a2d
Packit c32a2d
	if(ao->format < 0)
Packit c32a2d
	{
Packit c32a2d
		ao->rate = 44100;
Packit c32a2d
		ao->channels = 2;
Packit c32a2d
		ao->format = MPG123_ENC_SIGNED_16;
Packit c32a2d
		return 0;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	if(!(wdat = wavdata_new()))
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_DOOM;
Packit c32a2d
		goto wav_open_bad;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	wdat->floatwav = (ao->format & MPG123_ENC_FLOAT);
Packit c32a2d
	if(wdat->floatwav)
Packit c32a2d
	{
Packit c32a2d
		if(!(floathead = wavhead_new( &riff_float_template
Packit c32a2d
		,	sizeof(riff_float_template)) ))
Packit c32a2d
		{
Packit c32a2d
			ao->errcode = OUT123_DOOM;
Packit c32a2d
			goto wav_open_bad;
Packit c32a2d
		}
Packit c32a2d
		wdat->the_header = floathead;
Packit c32a2d
		wdat->the_header_size = sizeof(*floathead);
Packit c32a2d
	}
Packit c32a2d
	else
Packit c32a2d
	{
Packit c32a2d
		if(!(inthead = wavhead_new( &riff_template
Packit c32a2d
		,	sizeof(riff_template)) ))
Packit c32a2d
		{
Packit c32a2d
			ao->errcode = OUT123_DOOM;
Packit c32a2d
			goto wav_open_bad;
Packit c32a2d
		}
Packit c32a2d
		wdat->the_header = inthead;
Packit c32a2d
		wdat->the_header_size = sizeof(*inthead);
Packit c32a2d
Packit c32a2d
		/* standard MS PCM, and its format specific is BitsPerSample */
Packit c32a2d
		long2littleendian(1, inthead->WAVE.fmt.FormatTag
Packit c32a2d
		,	sizeof(inthead->WAVE.fmt.FormatTag));
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	if(ao->format == MPG123_ENC_FLOAT_32)
Packit c32a2d
	{
Packit c32a2d
		long2littleendian(3, floathead->WAVE.fmt.FormatTag
Packit c32a2d
		,	sizeof(floathead->WAVE.fmt.FormatTag));
Packit c32a2d
		long2littleendian(bps=32, floathead->WAVE.fmt.BitsPerSample
Packit c32a2d
		,	sizeof(floathead->WAVE.fmt.BitsPerSample));
Packit c32a2d
		wdat->flipendian = testEndian();
Packit c32a2d
	}
Packit c32a2d
	else if(ao->format == MPG123_ENC_SIGNED_32)
Packit c32a2d
	{
Packit c32a2d
		long2littleendian(bps=32, inthead->WAVE.fmt.BitsPerSample
Packit c32a2d
		,	sizeof(inthead->WAVE.fmt.BitsPerSample));
Packit c32a2d
		wdat->flipendian = testEndian();
Packit c32a2d
	}
Packit c32a2d
	else if(ao->format == MPG123_ENC_SIGNED_24)
Packit c32a2d
	{
Packit c32a2d
		long2littleendian(bps=24, inthead->WAVE.fmt.BitsPerSample
Packit c32a2d
		,	sizeof(inthead->WAVE.fmt.BitsPerSample));
Packit c32a2d
		wdat->flipendian = testEndian();
Packit c32a2d
	}
Packit c32a2d
	else if(ao->format == MPG123_ENC_SIGNED_16)
Packit c32a2d
	{
Packit c32a2d
		long2littleendian(bps=16, inthead->WAVE.fmt.BitsPerSample
Packit c32a2d
		,	sizeof(inthead->WAVE.fmt.BitsPerSample));
Packit c32a2d
		wdat->flipendian = testEndian();
Packit c32a2d
	}
Packit c32a2d
	else if(ao->format == MPG123_ENC_UNSIGNED_8)
Packit c32a2d
		long2littleendian(bps=8, inthead->WAVE.fmt.BitsPerSample
Packit c32a2d
		,	sizeof(inthead->WAVE.fmt.BitsPerSample));
Packit c32a2d
	else
Packit c32a2d
	{
Packit c32a2d
		if(!AOQUIET)
Packit c32a2d
			error("Format not supported.");
Packit c32a2d
		goto wav_open_bad;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	if(wdat->floatwav)
Packit c32a2d
	{
Packit c32a2d
		long2littleendian(ao->channels, floathead->WAVE.fmt.Channels
Packit c32a2d
		,	sizeof(floathead->WAVE.fmt.Channels));
Packit c32a2d
		long2littleendian(ao->rate, floathead->WAVE.fmt.SamplesPerSec
Packit c32a2d
		,	sizeof(floathead->WAVE.fmt.SamplesPerSec));
Packit c32a2d
		long2littleendian( (int)(ao->channels * ao->rate * bps)>>3
Packit c32a2d
		,	floathead->WAVE.fmt.AvgBytesPerSec
Packit c32a2d
		,	sizeof(floathead->WAVE.fmt.AvgBytesPerSec) );
Packit c32a2d
		long2littleendian( (int)(ao->channels * bps)>>3
Packit c32a2d
		,	floathead->WAVE.fmt.BlockAlign
Packit c32a2d
		,	sizeof(floathead->WAVE.fmt.BlockAlign) );
Packit c32a2d
	}
Packit c32a2d
	else
Packit c32a2d
	{
Packit c32a2d
		long2littleendian(ao->channels, inthead->WAVE.fmt.Channels
Packit c32a2d
		,	sizeof(inthead->WAVE.fmt.Channels));
Packit c32a2d
		long2littleendian(ao->rate, inthead->WAVE.fmt.SamplesPerSec
Packit c32a2d
		,	sizeof(inthead->WAVE.fmt.SamplesPerSec));
Packit c32a2d
		long2littleendian( (int)(ao->channels * ao->rate * bps)>>3
Packit c32a2d
		,	inthead->WAVE.fmt.AvgBytesPerSec
Packit c32a2d
		,sizeof(inthead->WAVE.fmt.AvgBytesPerSec) );
Packit c32a2d
		long2littleendian( (int)(ao->channels * bps)>>3
Packit c32a2d
		,	inthead->WAVE.fmt.BlockAlign
Packit c32a2d
		,	sizeof(inthead->WAVE.fmt.BlockAlign) );
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	if(open_file(wdat, ao->device) < 0)
Packit c32a2d
		goto wav_open_bad;
Packit c32a2d
Packit c32a2d
	if(wdat->floatwav)
Packit c32a2d
	{
Packit c32a2d
		long2littleendian(wdat->datalen, floathead->WAVE.data.datalen
Packit c32a2d
		,	sizeof(floathead->WAVE.data.datalen));
Packit c32a2d
		long2littleendian(wdat->datalen+sizeof(floathead->WAVE)
Packit c32a2d
		,	floathead->WAVElen, sizeof(floathead->WAVElen));
Packit c32a2d
	}
Packit c32a2d
	else
Packit c32a2d
	{
Packit c32a2d
		long2littleendian(wdat->datalen, inthead->WAVE.data.datalen
Packit c32a2d
		,	sizeof(inthead->WAVE.data.datalen));
Packit c32a2d
		long2littleendian( wdat->datalen+sizeof(inthead->WAVE)
Packit c32a2d
		,	inthead->WAVElen
Packit c32a2d
		,	sizeof(inthead->WAVElen) );
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	wdat->bytes_per_sample = bps>>3;
Packit c32a2d
Packit c32a2d
	ao->userptr = wdat;
Packit c32a2d
	return 0;
Packit c32a2d
Packit c32a2d
wav_open_bad:
Packit c32a2d
	if(inthead)
Packit c32a2d
		free(inthead);
Packit c32a2d
	if(floathead)
Packit c32a2d
		free(floathead);
Packit c32a2d
	if(wdat)
Packit c32a2d
	{
Packit c32a2d
		wdat->the_header = NULL;
Packit c32a2d
		wavdata_del(wdat);
Packit c32a2d
	}
Packit c32a2d
	return -1;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int wav_write(out123_handle *ao, unsigned char *buf, int len)
Packit c32a2d
{
Packit c32a2d
	struct wavdata *wdat = ao->userptr;
Packit c32a2d
	int temp;
Packit c32a2d
	int i;
Packit c32a2d
Packit c32a2d
	if(!wdat || !wdat->wavfp)
Packit c32a2d
		return 0; /* Really? Zero? */
Packit c32a2d
Packit c32a2d
	if(wdat->datalen == 0 && write_header(ao) < 0)
Packit c32a2d
		return -1;
Packit c32a2d
Packit c32a2d
	/* Endianess conversion. Not fancy / optimized. */
Packit c32a2d
	if(wdat->flipendian)
Packit c32a2d
	{
Packit c32a2d
		if(wdat->bytes_per_sample == 4) /* 32 bit */
Packit c32a2d
		{
Packit c32a2d
			if(len & 3)
Packit c32a2d
			{
Packit c32a2d
				if(!AOQUIET)
Packit c32a2d
					error("Number of bytes no multiple of 4 (32bit)!");
Packit c32a2d
				return -1;
Packit c32a2d
			}
Packit c32a2d
			for(i=0;i
Packit c32a2d
			{
Packit c32a2d
				int j;
Packit c32a2d
				unsigned char tmp[4];
Packit c32a2d
				for(j = 0; j<=3; ++j) tmp[j] = buf[i+j];
Packit c32a2d
				for(j = 0; j<=3; ++j) buf[i+j] = tmp[3-j];
Packit c32a2d
			}
Packit c32a2d
		}
Packit c32a2d
		else /* 16 bit */
Packit c32a2d
		{
Packit c32a2d
			if(len & 1)
Packit c32a2d
			{
Packit c32a2d
				error("Odd number of bytes!");
Packit c32a2d
				return -1;
Packit c32a2d
			}
Packit c32a2d
			for(i=0;i
Packit c32a2d
			{
Packit c32a2d
				unsigned char tmp;
Packit c32a2d
				tmp = buf[i+0];
Packit c32a2d
				buf[i+0] = buf[i+1];
Packit c32a2d
				buf[i+1] = tmp;
Packit c32a2d
			}
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	temp = fwrite(buf, 1, len, wdat->wavfp);
Packit c32a2d
	if(temp <= 0) return temp;
Packit c32a2d
/* That would kill it of early when running out of disk space. */
Packit c32a2d
#if 0
Packit c32a2d
if(fflush(wdat->wavfp))
Packit c32a2d
{
Packit c32a2d
	if(!AOQUIET)
Packit c32a2d
		error1("flushing failed: %s\n", strerror(errno));
Packit c32a2d
	return -1;
Packit c32a2d
}
Packit c32a2d
#endif
Packit c32a2d
	wdat->datalen += temp;
Packit c32a2d
Packit c32a2d
	return temp;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int wav_close(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	struct wavdata *wdat = ao->userptr;
Packit c32a2d
Packit c32a2d
	if(!wdat) /* Special case: Opened only for format query. */
Packit c32a2d
		return 0;
Packit c32a2d
Packit c32a2d
	if(!wdat || !wdat->wavfp)
Packit c32a2d
		return -1;
Packit c32a2d
Packit c32a2d
	/* flush before seeking to catch out-of-disk explicitly at least at the end */
Packit c32a2d
	if(fflush(wdat->wavfp))
Packit c32a2d
	{
Packit c32a2d
		if(!AOQUIET)
Packit c32a2d
			error1("cannot flush WAV stream: %s", strerror(errno));
Packit c32a2d
		return close_file(ao);
Packit c32a2d
	}
Packit c32a2d
	if(fseek(wdat->wavfp, 0L, SEEK_SET) >= 0)
Packit c32a2d
	{
Packit c32a2d
		if(wdat->floatwav)
Packit c32a2d
		{
Packit c32a2d
			struct riff_float *floathead = wdat->the_header;
Packit c32a2d
			long2littleendian(wdat->datalen
Packit c32a2d
			,	floathead->WAVE.data.datalen
Packit c32a2d
			,	sizeof(floathead->WAVE.data.datalen));
Packit c32a2d
			long2littleendian(wdat->datalen+sizeof(floathead->WAVE)
Packit c32a2d
			,	floathead->WAVElen
Packit c32a2d
			,	sizeof(floathead->WAVElen));
Packit c32a2d
			long2littleendian( wdat->datalen
Packit c32a2d
			/	(
Packit c32a2d
					from_little(floathead->WAVE.fmt.Channels,2)
Packit c32a2d
				*	from_little(floathead->WAVE.fmt.BitsPerSample,2)/8
Packit c32a2d
				)
Packit c32a2d
			,	floathead->WAVE.fact.samplelen
Packit c32a2d
			,	sizeof(floathead->WAVE.fact.samplelen) );
Packit c32a2d
		}
Packit c32a2d
		else
Packit c32a2d
		{
Packit c32a2d
			struct riff *inthead = wdat->the_header;
Packit c32a2d
			long2littleendian(wdat->datalen, inthead->WAVE.data.datalen
Packit c32a2d
			,	sizeof(inthead->WAVE.data.datalen));
Packit c32a2d
			long2littleendian(wdat->datalen+sizeof(inthead->WAVE), inthead->WAVElen
Packit c32a2d
			,	sizeof(inthead->WAVElen));
Packit c32a2d
		}
Packit c32a2d
		/* Always (over)writing the header here; also for stdout, when
Packit c32a2d
		   fseek worked, this overwrite works. */
Packit c32a2d
		write_header(ao);
Packit c32a2d
	}
Packit c32a2d
	else if(!AOQUIET)
Packit c32a2d
		warning("Cannot rewind WAV file. File-format isn't fully conform now.");
Packit c32a2d
Packit c32a2d
	return close_file(ao);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int au_close(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	struct wavdata *wdat = ao->userptr;
Packit c32a2d
Packit c32a2d
	if(!wdat) /* Special case: Opened only for format query. */
Packit c32a2d
		return 0;
Packit c32a2d
Packit c32a2d
	if(!wdat->wavfp)
Packit c32a2d
		return -1;
Packit c32a2d
Packit c32a2d
	/* flush before seeking to catch out-of-disk explicitly at least at the end */
Packit c32a2d
	if(fflush(wdat->wavfp))
Packit c32a2d
	{
Packit c32a2d
		if(!AOQUIET)
Packit c32a2d
			error1("cannot flush WAV stream: %s", strerror(errno));
Packit c32a2d
		return close_file(ao);
Packit c32a2d
	}
Packit c32a2d
	if(fseek(wdat->wavfp, 0L, SEEK_SET) >= 0)
Packit c32a2d
	{
Packit c32a2d
		struct auhead *auhead = wdat->the_header;
Packit c32a2d
		long2bigendian(wdat->datalen, auhead->datalen, sizeof(auhead->datalen));
Packit c32a2d
		/* Always (over)writing the header here; also for stdout, when
Packit c32a2d
		   fseek worked, this overwrite works. */
Packit c32a2d
		write_header(ao);
Packit c32a2d
	}
Packit c32a2d
	else if(!AOQUIET)
Packit c32a2d
		warning("Cannot rewind AU file. File-format isn't fully conform now.");
Packit c32a2d
Packit c32a2d
	return close_file(ao);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* CDR data also uses that. */
Packit c32a2d
int raw_close(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	struct wavdata *wdat = ao->userptr;
Packit c32a2d
Packit c32a2d
	if(!wdat) /* Special case: Opened only for format query. */
Packit c32a2d
		return 0;
Packit c32a2d
Packit c32a2d
	if(!wdat->wavfp)
Packit c32a2d
		return -1;
Packit c32a2d
Packit c32a2d
	return close_file(ao);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Some trivial functions to interface with out123's module architecture. */
Packit c32a2d
Packit c32a2d
int cdr_formats(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	if(ao->rate == 44100 && ao->channels == 2)
Packit c32a2d
		return MPG123_ENC_SIGNED_16;
Packit c32a2d
	else
Packit c32a2d
		return 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int au_formats(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	return MPG123_ENC_SIGNED_16|MPG123_ENC_UNSIGNED_8|MPG123_ENC_ULAW_8;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int raw_formats(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	return MPG123_ENC_ANY;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int wav_formats(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	return
Packit c32a2d
		MPG123_ENC_SIGNED_16
Packit c32a2d
	|	MPG123_ENC_UNSIGNED_8
Packit c32a2d
	|	MPG123_ENC_FLOAT_32
Packit c32a2d
	|	MPG123_ENC_SIGNED_24
Packit c32a2d
	|	MPG123_ENC_SIGNED_32;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Draining is flushing to disk. Words do suck at times.
Packit c32a2d
   One could call fsync(), too, but to be safe, that would need to
Packit c32a2d
   be called on the directory, too. Also, apps randomly calling
Packit c32a2d
   fsync() can cause annoying issues in a system. */
Packit c32a2d
void wav_drain(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	struct wavdata *wdat = ao->userptr;
Packit c32a2d
Packit c32a2d
	if(!wdat)
Packit c32a2d
		return;
Packit c32a2d
Packit c32a2d
	if(fflush(wdat->wavfp) && !AOQUIET)
Packit c32a2d
		error1("flushing failed: %s\n", strerror(errno));
Packit c32a2d
}