1 /*
2 	wav.c: write wav/au/cdr files (and headerless raw
3 
4 	copyright ?-2015 by the mpg123 project - free software under the terms of the LGPL 2.1
5 	see COPYING and AUTHORS files in distribution or http://mpg123.org
6 	initially written by Samuel Audet
7 
8 	Geez, why are WAV RIFF headers are so secret?  I got something together,
9 	but wow...  anyway, I hope someone will find this useful.
10 	- Samuel Audet
11 
12 	minor simplifications and ugly AU/CDR format stuff by MH
13 
14 	It's not a very clean code ... Fix this!
15 
16 	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.
17 	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).
18 
19 	ThOr: Again reworked things for libout123, with non-static state.
20 	This set of builtin "modules" is what we can use in automated tests
21 	of libout123. Code still not very nice, but I tried to keep modification
22 	of what stood the test of time minimal. One still can add a module to
23 	libout123 that uses sndfile and similar libraries for more choice on writing
24 	output files.
25 */
26 
27 #include "out123_int.h"
28 #include "wav.h"
29 
30 #include <errno.h>
31 #include "debug.h"
32 
33 /* Create the two WAV headers. */
34 
35 #define WAVE_FORMAT 1
36 #define RIFF_NAME riff_template
37 #define RIFF_STRUCT_NAME riff
38 #include "wavhead.h"
39 
40 #undef WAVE_FORMAT
41 #undef RIFF_NAME
42 #undef RIFF_STRUCT_NAME
43 #define WAVE_FORMAT 3
44 #define RIFF_NAME riff_float_template
45 #define RIFF_STRUCT_NAME riff_float
46 #define FLOATOUT
47 #include "wavhead.h"
48 
49 /* AU header struct... */
50 
51 struct auhead {
52   byte magic[4];
53   byte headlen[4];
54   byte datalen[4];
55   byte encoding[4];
56   byte rate[4];
57   byte channels[4];
58   byte dummy[8];
59 } const auhead_template = {
60   { 0x2e,0x73,0x6e,0x64 } , { 0x00,0x00,0x00,0x20 } ,
61   { 0xff,0xff,0xff,0xff } , { 0,0,0,0 } , { 0,0,0,0 } , { 0,0,0,0 } ,
62   { 0,0,0,0,0,0,0,0 }};
63 
64 struct wavdata
65 {
66 	FILE *wavfp;
67 	long datalen;
68 	int flipendian;
69 	int bytes_per_sample;
70 	int floatwav; /* If we write a floating point WAV file. */
71 	/*
72 		Open routines only prepare a header, stored here and written on first
73 		actual data write. If no data is written at all, proper files will
74 		still get a header via the update at closing; non-seekable streams will
75 		just have no no header if there is no data.
76 	*/
77 	void *the_header;
78 	size_t the_header_size;
79 };
80 
wavdata_new(void)81 static struct wavdata* wavdata_new(void)
82 {
83 	struct wavdata *wdat = malloc(sizeof(struct wavdata));
84 	if(wdat)
85 	{
86 		wdat->wavfp = NULL;
87 		wdat->datalen = 0;
88 		wdat->flipendian = 0;
89 		wdat->bytes_per_sample = -1;
90 		wdat->floatwav = 0;
91 		wdat->the_header = NULL;
92 		wdat->the_header_size = 0;
93 	}
94 	return wdat;
95 }
96 
wavdata_del(struct wavdata * wdat)97 static void wavdata_del(struct wavdata *wdat)
98 {
99 	if(!wdat) return;
100 	if(wdat->wavfp && wdat->wavfp != stdout)
101 		compat_fclose(wdat->wavfp);
102 	if(wdat->the_header)
103 		free(wdat->the_header);
104 	free(wdat);
105 }
106 
107 /* Pointer types are for pussies;-) */
wavhead_new(void const * template,size_t size)108 static void* wavhead_new(void const *template, size_t size)
109 {
110 	void *header = malloc(size);
111 	if(header)
112 		memcpy(header, template, size);
113 	return header;
114 }
115 
116 /* Convertfunctions: */
117 /* always little endian */
118 
long2littleendian(long inval,byte * outval,int b)119 static void long2littleendian(long inval,byte *outval,int b)
120 {
121   int i;
122   for(i=0;i<b;i++) {
123     outval[i] = (inval>>(i*8)) & 0xff;
124   }
125 }
126 
127 /* always big endian */
long2bigendian(long inval,byte * outval,int b)128 static void long2bigendian(long inval,byte *outval,int b)
129 {
130   int i;
131   for(i=0;i<b;i++) {
132     outval[i] = (inval>>((b-i-1)*8)) & 0xff;
133   }
134 }
135 
from_little(byte * inval,int b)136 static long from_little(byte *inval, int b)
137 {
138 	long ret = 0;
139 	int i;
140 	for(i=0;i<b;++i) ret += ((long)inval[i])<<(i*8);
141 
142 	return ret;
143 }
144 
testEndian(void)145 static int testEndian(void)
146 {
147   long i,a=0,b=0,c=0;
148   int ret = 0;
149 
150   for(i=0;i<sizeof(long);i++) {
151     ((byte *)&a)[i] = i;
152     b<<=8;
153     b |= i;
154     c |= i << (i*8);
155   }
156   if(a == b)
157       ret = 1;
158   else if(a != c) {
159       ret = -1;
160   }
161   return ret;
162 }
163 
164 /* return: 0 is good, -1 is bad */
open_file(struct wavdata * wdat,char * filename)165 static int open_file(struct wavdata *wdat, char *filename)
166 {
167 	debug2("open_file(%p, %s)", (void*)wdat, filename ? filename : "<nil>");
168 	if(!wdat)
169 		return -1;
170 #if defined(HAVE_SETUID) && defined(HAVE_GETUID)
171 	/* TODO: get rid of that and settle that you rather not install mpg123
172 	   setuid-root. Why should you?
173 	   In case this program is setuid, create files owned by original user. */
174 	setuid(getuid());
175 #endif
176 	if(!filename || !strcmp("-",filename) || !strcmp("", filename))
177 	{
178 		wdat->wavfp = stdout;
179 #ifdef WIN32
180 		_setmode(STDOUT_FILENO, _O_BINARY);
181 #endif
182 		/* If stdout is redirected to a file, seeks suddenly can work.
183 		Doing one here to ensure that such a file has the same output
184 		it had when opening directly as such. */
185 		fseek(wdat->wavfp, 0L, SEEK_SET);
186 		return 0;
187 	}
188 	else
189 	{
190 		wdat->wavfp = compat_fopen(filename, "wb");
191 		if(!wdat->wavfp)
192 			return -1;
193 		else
194 			return 0;
195 	}
196 }
197 
198 /* return: 0 is good, -1 is bad
199    Works for any partial state of setup, especially should not complain if
200    ao->userptr == NULL. */
close_file(out123_handle * ao)201 static int close_file(out123_handle *ao)
202 {
203 	struct wavdata *wdat = ao->userptr;
204 	int ret = 0;
205 
206 	if(wdat->wavfp != NULL && wdat->wavfp != stdout)
207 	{
208 		if(compat_fclose(wdat->wavfp))
209 		{
210 			if(!AOQUIET)
211 				error1("problem closing the audio file, probably because of flushing to disk: %s\n", strerror(errno));
212 			ret = -1;
213 		}
214 	}
215 
216 	/* Always cleanup here. */
217 	wdat->wavfp = NULL;
218 	wavdata_del(wdat);
219 	ao->userptr = NULL;
220 	return ret;
221 }
222 
223 /* return: 0 is good, -1 is bad */
write_header(out123_handle * ao)224 static int write_header(out123_handle *ao)
225 {
226 	struct wavdata *wdat = ao->userptr;
227 
228 	if(!wdat)
229 		return 0;
230 
231 	if(
232 		wdat->the_header_size > 0
233 	&&	(
234 			fwrite(wdat->the_header, wdat->the_header_size, 1, wdat->wavfp) != 1
235 		|| fflush(wdat->wavfp)
236 		)
237 	)
238 	{
239 		if(!AOQUIET)
240 			error1("cannot write header: %s", strerror(errno));
241 		return -1;
242 	}
243 	else return 0;
244 }
245 
au_open(out123_handle * ao)246 int au_open(out123_handle *ao)
247 {
248 	struct wavdata *wdat   = NULL;
249 	struct auhead  *auhead = NULL;
250 
251 	if(ao->format < 0)
252 	{
253 		ao->rate = 44100;
254 		ao->channels = 2;
255 		ao->format = MPG123_ENC_SIGNED_16;
256 		return 0;
257 	}
258 
259 	if(ao->format & MPG123_ENC_FLOAT)
260 	{
261 		if(!AOQUIET)
262 			error("AU file support for float values not there yet");
263 		goto au_open_bad;
264 	}
265 
266 	if(
267 		!(wdat   = wavdata_new())
268 	||	!(auhead = wavhead_new(&auhead_template, sizeof(auhead_template)))
269 	)
270 	{
271 		ao->errcode = OUT123_DOOM;
272 		goto au_open_bad;
273 	}
274 
275 	wdat->the_header      = auhead;
276 	wdat->the_header_size = sizeof(*auhead);
277 
278 	wdat->flipendian = 0;
279 
280 	switch(ao->format)
281 	{
282 		case MPG123_ENC_SIGNED_16:
283 		{
284 			int endiantest = testEndian();
285 			if(endiantest == -1)
286 				goto au_open_bad;
287 			wdat->flipendian = !endiantest; /* big end */
288 			long2bigendian(3,auhead->encoding,sizeof(auhead->encoding));
289 		}
290 		break;
291 		case MPG123_ENC_UNSIGNED_8:
292 			ao->format = MPG123_ENC_ULAW_8;
293 		case MPG123_ENC_ULAW_8:
294 			long2bigendian(1,auhead->encoding,sizeof(auhead->encoding));
295 		break;
296 		default:
297 			if(!AOQUIET)
298 				error("AU output is only a hack. This audio mode isn't supported yet.");
299 			goto au_open_bad;
300 	}
301 
302 	long2bigendian(0xffffffff,auhead->datalen,sizeof(auhead->datalen));
303 	long2bigendian(ao->rate,auhead->rate,sizeof(auhead->rate));
304 	long2bigendian(ao->channels,auhead->channels,sizeof(auhead->channels));
305 
306 	if(open_file(wdat, ao->device) < 0)
307 		goto au_open_bad;
308 
309 	wdat->datalen = 0;
310 
311 	ao->userptr = wdat;
312 	return 0;
313 
314 au_open_bad:
315 	if(auhead)
316 		free(auhead);
317 	if(wdat)
318 	{
319 		wdat->the_header = NULL;
320 		wavdata_del(wdat);
321 	}
322 	return -1;
323 }
324 
cdr_open(out123_handle * ao)325 int cdr_open(out123_handle *ao)
326 {
327 	struct wavdata *wdat   = NULL;
328 
329 	if(ao->format < 0)
330 	{
331 		ao->rate = 44100;
332 		ao->channels = 2;
333 		ao->format = MPG123_ENC_SIGNED_16;
334 		return 0;
335 	}
336 
337 	if(
338 		ao->format != MPG123_ENC_SIGNED_16
339 	||	ao->rate != 44100
340 	||	ao->channels != 2
341 	)
342 	{
343 		if(!AOQUIET)
344 			error("Oops .. not forced to 16 bit, 44 kHz, stereo?");
345 		goto cdr_open_bad;
346 	}
347 
348 	if(!(wdat = wavdata_new()))
349 	{
350 		ao->errcode = OUT123_DOOM;
351 		goto cdr_open_bad;
352 	}
353 
354 	wdat->flipendian = !testEndian(); /* big end */
355 
356 	if(open_file(wdat, ao->device) < 0)
357 	{
358 		if(!AOQUIET)
359 			error("cannot open file for writing");
360 		goto cdr_open_bad;
361 	}
362 
363 	ao->userptr = wdat;
364 	return 0;
365 cdr_open_bad:
366 	if(wdat)
367 		wavdata_del(wdat);
368 	return -1;
369 }
370 
371 /* RAW files are headerless WAVs where the format does not matter. */
raw_open(out123_handle * ao)372 int raw_open(out123_handle *ao)
373 {
374 	struct wavdata *wdat;
375 
376 	if(ao->format < 0)
377 	{
378 		ao->rate = 44100;
379 		ao->channels = 2;
380 		ao->format = MPG123_ENC_SIGNED_16;
381 		return 0;
382 	}
383 
384 	if(!(wdat = wavdata_new()))
385 	{
386 		ao->errcode = OUT123_DOOM;
387 		goto raw_open_bad;
388 	}
389 
390 	if(open_file(wdat, ao->device) < 0)
391 		goto raw_open_bad;
392 
393 	ao->userptr = wdat;
394 	return 1;
395 raw_open_bad:
396 	if(wdat)
397 		wavdata_del(wdat);
398 	return -1;
399 }
400 
wav_open(out123_handle * ao)401 int wav_open(out123_handle *ao)
402 {
403 	int bps;
404 	struct wavdata    *wdat      = NULL;
405 	struct riff       *inthead   = NULL;
406 	struct riff_float *floathead = NULL;
407 
408 	if(ao->format < 0)
409 	{
410 		ao->rate = 44100;
411 		ao->channels = 2;
412 		ao->format = MPG123_ENC_SIGNED_16;
413 		return 0;
414 	}
415 
416 	if(!(wdat = wavdata_new()))
417 	{
418 		ao->errcode = OUT123_DOOM;
419 		goto wav_open_bad;
420 	}
421 
422 	wdat->floatwav = (ao->format & MPG123_ENC_FLOAT);
423 	if(wdat->floatwav)
424 	{
425 		if(!(floathead = wavhead_new( &riff_float_template
426 		,	sizeof(riff_float_template)) ))
427 		{
428 			ao->errcode = OUT123_DOOM;
429 			goto wav_open_bad;
430 		}
431 		wdat->the_header = floathead;
432 		wdat->the_header_size = sizeof(*floathead);
433 	}
434 	else
435 	{
436 		if(!(inthead = wavhead_new( &riff_template
437 		,	sizeof(riff_template)) ))
438 		{
439 			ao->errcode = OUT123_DOOM;
440 			goto wav_open_bad;
441 		}
442 		wdat->the_header = inthead;
443 		wdat->the_header_size = sizeof(*inthead);
444 
445 		/* standard MS PCM, and its format specific is BitsPerSample */
446 		long2littleendian(1, inthead->WAVE.fmt.FormatTag
447 		,	sizeof(inthead->WAVE.fmt.FormatTag));
448 	}
449 
450 	if(ao->format == MPG123_ENC_FLOAT_32)
451 	{
452 		long2littleendian(3, floathead->WAVE.fmt.FormatTag
453 		,	sizeof(floathead->WAVE.fmt.FormatTag));
454 		long2littleendian(bps=32, floathead->WAVE.fmt.BitsPerSample
455 		,	sizeof(floathead->WAVE.fmt.BitsPerSample));
456 		wdat->flipendian = testEndian();
457 	}
458 	else if(ao->format == MPG123_ENC_SIGNED_32)
459 	{
460 		long2littleendian(bps=32, inthead->WAVE.fmt.BitsPerSample
461 		,	sizeof(inthead->WAVE.fmt.BitsPerSample));
462 		wdat->flipendian = testEndian();
463 	}
464 	else if(ao->format == MPG123_ENC_SIGNED_24)
465 	{
466 		long2littleendian(bps=24, inthead->WAVE.fmt.BitsPerSample
467 		,	sizeof(inthead->WAVE.fmt.BitsPerSample));
468 		wdat->flipendian = testEndian();
469 	}
470 	else if(ao->format == MPG123_ENC_SIGNED_16)
471 	{
472 		long2littleendian(bps=16, inthead->WAVE.fmt.BitsPerSample
473 		,	sizeof(inthead->WAVE.fmt.BitsPerSample));
474 		wdat->flipendian = testEndian();
475 	}
476 	else if(ao->format == MPG123_ENC_UNSIGNED_8)
477 		long2littleendian(bps=8, inthead->WAVE.fmt.BitsPerSample
478 		,	sizeof(inthead->WAVE.fmt.BitsPerSample));
479 	else
480 	{
481 		if(!AOQUIET)
482 			error("Format not supported.");
483 		goto wav_open_bad;
484 	}
485 
486 	if(wdat->floatwav)
487 	{
488 		long2littleendian(ao->channels, floathead->WAVE.fmt.Channels
489 		,	sizeof(floathead->WAVE.fmt.Channels));
490 		long2littleendian(ao->rate, floathead->WAVE.fmt.SamplesPerSec
491 		,	sizeof(floathead->WAVE.fmt.SamplesPerSec));
492 		long2littleendian( (int)(ao->channels * ao->rate * bps)>>3
493 		,	floathead->WAVE.fmt.AvgBytesPerSec
494 		,	sizeof(floathead->WAVE.fmt.AvgBytesPerSec) );
495 		long2littleendian( (int)(ao->channels * bps)>>3
496 		,	floathead->WAVE.fmt.BlockAlign
497 		,	sizeof(floathead->WAVE.fmt.BlockAlign) );
498 	}
499 	else
500 	{
501 		long2littleendian(ao->channels, inthead->WAVE.fmt.Channels
502 		,	sizeof(inthead->WAVE.fmt.Channels));
503 		long2littleendian(ao->rate, inthead->WAVE.fmt.SamplesPerSec
504 		,	sizeof(inthead->WAVE.fmt.SamplesPerSec));
505 		long2littleendian( (int)(ao->channels * ao->rate * bps)>>3
506 		,	inthead->WAVE.fmt.AvgBytesPerSec
507 		,sizeof(inthead->WAVE.fmt.AvgBytesPerSec) );
508 		long2littleendian( (int)(ao->channels * bps)>>3
509 		,	inthead->WAVE.fmt.BlockAlign
510 		,	sizeof(inthead->WAVE.fmt.BlockAlign) );
511 	}
512 
513 	if(open_file(wdat, ao->device) < 0)
514 		goto wav_open_bad;
515 
516 	if(wdat->floatwav)
517 	{
518 		long2littleendian(wdat->datalen, floathead->WAVE.data.datalen
519 		,	sizeof(floathead->WAVE.data.datalen));
520 		long2littleendian(wdat->datalen+sizeof(floathead->WAVE)
521 		,	floathead->WAVElen, sizeof(floathead->WAVElen));
522 	}
523 	else
524 	{
525 		long2littleendian(wdat->datalen, inthead->WAVE.data.datalen
526 		,	sizeof(inthead->WAVE.data.datalen));
527 		long2littleendian( wdat->datalen+sizeof(inthead->WAVE)
528 		,	inthead->WAVElen
529 		,	sizeof(inthead->WAVElen) );
530 	}
531 
532 	wdat->bytes_per_sample = bps>>3;
533 
534 	ao->userptr = wdat;
535 	return 0;
536 
537 wav_open_bad:
538 	if(inthead)
539 		free(inthead);
540 	if(floathead)
541 		free(floathead);
542 	if(wdat)
543 	{
544 		wdat->the_header = NULL;
545 		wavdata_del(wdat);
546 	}
547 	return -1;
548 }
549 
wav_write(out123_handle * ao,unsigned char * buf,int len)550 int wav_write(out123_handle *ao, unsigned char *buf, int len)
551 {
552 	struct wavdata *wdat = ao->userptr;
553 	int temp;
554 	int i;
555 
556 	if(!wdat || !wdat->wavfp)
557 		return 0; /* Really? Zero? */
558 
559 	if(wdat->datalen == 0 && write_header(ao) < 0)
560 		return -1;
561 
562 	/* Endianess conversion. Not fancy / optimized. */
563 	if(wdat->flipendian)
564 	{
565 		if(wdat->bytes_per_sample == 4) /* 32 bit */
566 		{
567 			if(len & 3)
568 			{
569 				if(!AOQUIET)
570 					error("Number of bytes no multiple of 4 (32bit)!");
571 				return -1;
572 			}
573 			for(i=0;i<len;i+=4)
574 			{
575 				int j;
576 				unsigned char tmp[4];
577 				for(j = 0; j<=3; ++j) tmp[j] = buf[i+j];
578 				for(j = 0; j<=3; ++j) buf[i+j] = tmp[3-j];
579 			}
580 		}
581 		else /* 16 bit */
582 		{
583 			if(len & 1)
584 			{
585 				error("Odd number of bytes!");
586 				return -1;
587 			}
588 			for(i=0;i<len;i+=2)
589 			{
590 				unsigned char tmp;
591 				tmp = buf[i+0];
592 				buf[i+0] = buf[i+1];
593 				buf[i+1] = tmp;
594 			}
595 		}
596 	}
597 
598 	temp = fwrite(buf, 1, len, wdat->wavfp);
599 	if(temp <= 0) return temp;
600 /* That would kill it of early when running out of disk space. */
601 #if 0
602 if(fflush(wdat->wavfp))
603 {
604 	if(!AOQUIET)
605 		error1("flushing failed: %s\n", strerror(errno));
606 	return -1;
607 }
608 #endif
609 	wdat->datalen += temp;
610 
611 	return temp;
612 }
613 
wav_close(out123_handle * ao)614 int wav_close(out123_handle *ao)
615 {
616 	struct wavdata *wdat = ao->userptr;
617 
618 	if(!wdat) /* Special case: Opened only for format query. */
619 		return 0;
620 
621 	if(!wdat || !wdat->wavfp)
622 		return -1;
623 
624 	/* flush before seeking to catch out-of-disk explicitly at least at the end */
625 	if(fflush(wdat->wavfp))
626 	{
627 		if(!AOQUIET)
628 			error1("cannot flush WAV stream: %s", strerror(errno));
629 		return close_file(ao);
630 	}
631 	if(fseek(wdat->wavfp, 0L, SEEK_SET) >= 0)
632 	{
633 		if(wdat->floatwav)
634 		{
635 			struct riff_float *floathead = wdat->the_header;
636 			long2littleendian(wdat->datalen
637 			,	floathead->WAVE.data.datalen
638 			,	sizeof(floathead->WAVE.data.datalen));
639 			long2littleendian(wdat->datalen+sizeof(floathead->WAVE)
640 			,	floathead->WAVElen
641 			,	sizeof(floathead->WAVElen));
642 			long2littleendian( wdat->datalen
643 			/	(
644 					from_little(floathead->WAVE.fmt.Channels,2)
645 				*	from_little(floathead->WAVE.fmt.BitsPerSample,2)/8
646 				)
647 			,	floathead->WAVE.fact.samplelen
648 			,	sizeof(floathead->WAVE.fact.samplelen) );
649 		}
650 		else
651 		{
652 			struct riff *inthead = wdat->the_header;
653 			long2littleendian(wdat->datalen, inthead->WAVE.data.datalen
654 			,	sizeof(inthead->WAVE.data.datalen));
655 			long2littleendian(wdat->datalen+sizeof(inthead->WAVE), inthead->WAVElen
656 			,	sizeof(inthead->WAVElen));
657 		}
658 		/* Always (over)writing the header here; also for stdout, when
659 		   fseek worked, this overwrite works. */
660 		write_header(ao);
661 	}
662 	else if(!AOQUIET)
663 		warning("Cannot rewind WAV file. File-format isn't fully conform now.");
664 
665 	return close_file(ao);
666 }
667 
au_close(out123_handle * ao)668 int au_close(out123_handle *ao)
669 {
670 	struct wavdata *wdat = ao->userptr;
671 
672 	if(!wdat) /* Special case: Opened only for format query. */
673 		return 0;
674 
675 	if(!wdat->wavfp)
676 		return -1;
677 
678 	/* flush before seeking to catch out-of-disk explicitly at least at the end */
679 	if(fflush(wdat->wavfp))
680 	{
681 		if(!AOQUIET)
682 			error1("cannot flush WAV stream: %s", strerror(errno));
683 		return close_file(ao);
684 	}
685 	if(fseek(wdat->wavfp, 0L, SEEK_SET) >= 0)
686 	{
687 		struct auhead *auhead = wdat->the_header;
688 		long2bigendian(wdat->datalen, auhead->datalen, sizeof(auhead->datalen));
689 		/* Always (over)writing the header here; also for stdout, when
690 		   fseek worked, this overwrite works. */
691 		write_header(ao);
692 	}
693 	else if(!AOQUIET)
694 		warning("Cannot rewind AU file. File-format isn't fully conform now.");
695 
696 	return close_file(ao);
697 }
698 
699 /* CDR data also uses that. */
raw_close(out123_handle * ao)700 int raw_close(out123_handle *ao)
701 {
702 	struct wavdata *wdat = ao->userptr;
703 
704 	if(!wdat) /* Special case: Opened only for format query. */
705 		return 0;
706 
707 	if(!wdat->wavfp)
708 		return -1;
709 
710 	return close_file(ao);
711 }
712 
713 /* Some trivial functions to interface with out123's module architecture. */
714 
cdr_formats(out123_handle * ao)715 int cdr_formats(out123_handle *ao)
716 {
717 	if(ao->rate == 44100 && ao->channels == 2)
718 		return MPG123_ENC_SIGNED_16;
719 	else
720 		return 0;
721 }
722 
au_formats(out123_handle * ao)723 int au_formats(out123_handle *ao)
724 {
725 	return MPG123_ENC_SIGNED_16|MPG123_ENC_UNSIGNED_8|MPG123_ENC_ULAW_8;
726 }
727 
raw_formats(out123_handle * ao)728 int raw_formats(out123_handle *ao)
729 {
730 	return MPG123_ENC_ANY;
731 }
732 
wav_formats(out123_handle * ao)733 int wav_formats(out123_handle *ao)
734 {
735 	return
736 		MPG123_ENC_SIGNED_16
737 	|	MPG123_ENC_UNSIGNED_8
738 	|	MPG123_ENC_FLOAT_32
739 	|	MPG123_ENC_SIGNED_24
740 	|	MPG123_ENC_SIGNED_32;
741 }
742 
743 /* Draining is flushing to disk. Words do suck at times.
744    One could call fsync(), too, but to be safe, that would need to
745    be called on the directory, too. Also, apps randomly calling
746    fsync() can cause annoying issues in a system. */
wav_drain(out123_handle * ao)747 void wav_drain(out123_handle *ao)
748 {
749 	struct wavdata *wdat = ao->userptr;
750 
751 	if(!wdat)
752 		return;
753 
754 	if(fflush(wdat->wavfp) && !AOQUIET)
755 		error1("flushing failed: %s\n", strerror(errno));
756 }
757