1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * This file contains a set of Very Paranoid routines to convert
31 * audio file headers to in-core audio headers and vice versa.
32 *
33 * They are robust enough to handle any random file input without
34 * crashing miserably.  Of course, bad audio headers coming from
35 * the calling program can cause significant problems.
36 */
37
38#include <stdlib.h>
39#include <memory.h>
40#include <fcntl.h>
41#include <errno.h>	/* needed for large file error checking */
42#include <stdio.h>
43#include <sys/types.h>
44#include <sys/file.h>
45#include <sys/stat.h>
46#include <libintl.h>
47#include <math.h>
48
49#include <libaudio_impl.h>	/* include other audio hdr's */
50
51/* Round up to a double boundary */
52#define	ROUND_DBL(x)	(((x) + 7) & ~7)
53
54#define	HEADER_BUFFER		100
55
56#define	_MGET_(str)	(char *)dgettext(TEXT_DOMAIN, str)
57
58static int audio_encode_aiff(Audio_hdr *, unsigned char *, unsigned int *);
59static int audio_encode_au(Audio_hdr *, char *, unsigned int,
60	unsigned char *, unsigned int *);
61static int audio_encode_wav(Audio_hdr *, unsigned char *, unsigned int *);
62static double convert_from_ieee_extended(unsigned char *);
63static void convert_to_ieee_extended(double, unsigned char *);
64
65/*
66 * Write an audio file header to an output stream.
67 *
68 * The file header is encoded from the supplied Audio_hdr structure.
69 * If 'infop' is not NULL, it is the address of a buffer containing 'info'
70 * data.  'ilen' specifies the size of this buffer.
71 * The entire file header will be zero-padded to a double-word boundary.
72 *
73 * Note that the file header is stored on-disk in big-endian format,
74 * regardless of the machine type.
75 *
76 * Note also that the output file descriptor must not have been set up
77 * non-blocking i/o.  If non-blocking behavior is desired, set this
78 * flag after writing the file header.
79 */
80int
81audio_write_filehdr(int fd, Audio_hdr *hdrp, int file_type, char *infop,
82	unsigned int ilen)
83					/* file descriptor */
84					/* audio header */
85					/* audio header type */
86					/* info buffer pointer */
87					/* buffer size */
88{
89	int		err;
90	unsigned	blen;
91	unsigned char	*buf;		/* temporary buffer */
92
93	/* create tmp buf for the encoding routines to work with */
94	blen = HEADER_BUFFER + (infop ? ilen : 0) + 4;
95	blen = ROUND_DBL(blen);
96
97	if (!(buf = (unsigned char *)calloc(1, blen))) {
98		return (AUDIO_UNIXERROR);
99	}
100
101	switch (file_type) {
102	case FILE_AU:
103		err = audio_encode_au(hdrp, infop, ilen, buf, &blen);
104		break;
105	case FILE_WAV:
106		err = audio_encode_wav(hdrp, buf, &blen);
107		break;
108	case FILE_AIFF:
109		err = audio_encode_aiff(hdrp, buf, &blen);
110		break;
111	default:
112		return (AUDIO_ERR_BADFILETYPE);
113	}
114
115	if (err != AUDIO_SUCCESS) {
116		return (err);
117	}
118
119	/* Write and free the holding buffer */
120	err = write(fd, (char *)buf, (int)blen);
121	(void) free((char *)buf);
122
123	if (err != blen)
124		return ((err < 0) ? AUDIO_UNIXERROR : AUDIO_ERR_BADFILEHDR);
125
126	return (AUDIO_SUCCESS);
127
128}
129
130/*
131 * Rewrite the aiff header chunk length and the data chunk length fields.
132 */
133static int
134audio_rewrite_aiff_filesize(int fd, unsigned int size, unsigned int channels,
135	unsigned int bytes_per_sample)
136{
137	unsigned int	offset;
138	unsigned int	tmp_uint;
139	unsigned int	tmp_uint2;
140	unsigned int	total_size;
141
142	/* first fix aiff_hdr_size */
143	total_size = size + sizeof (aiff_hdr_chunk_t) +
144	    AUDIO_AIFF_COMM_CHUNK_SIZE + sizeof (aiff_ssnd_chunk_t);
145	tmp_uint = total_size - (2 * sizeof (int));
146	AUDIO_AIFF_HOST2FILE_INT(&tmp_uint, &tmp_uint2);
147	offset = sizeof (int);
148	if (lseek(fd, offset, SEEK_SET) < 0) {
149		return (AUDIO_ERR_NOEFFECT);
150	}
151	if (write(fd, &tmp_uint2, sizeof (tmp_uint2)) != sizeof (tmp_uint2)) {
152		return (AUDIO_ERR_NOEFFECT);
153	}
154
155	/* fix the frame count */
156	tmp_uint = size / channels / bytes_per_sample;
157	AUDIO_AIFF_HOST2FILE_INT(&tmp_uint, &tmp_uint2);
158	offset = sizeof (aiff_hdr_chunk_t) + (2 * sizeof (int)) +
159	    sizeof (short);
160	if (lseek(fd, offset, SEEK_SET) < 0) {
161		return (AUDIO_ERR_NOEFFECT);
162	}
163	if (write(fd, &tmp_uint2, sizeof (tmp_uint2)) != sizeof (tmp_uint2)) {
164		return (AUDIO_ERR_NOEFFECT);
165	}
166
167	/* fix the data size */
168	tmp_uint = size + sizeof (aiff_ssnd_chunk_t) - (2 * sizeof (int));
169	AUDIO_AIFF_HOST2FILE_INT(&tmp_uint, &tmp_uint2);
170	offset = sizeof (aiff_hdr_chunk_t) + AUDIO_AIFF_COMM_CHUNK_SIZE +
171	    sizeof (int);
172	if (lseek(fd, offset, SEEK_SET) < 0) {
173		return (AUDIO_ERR_NOEFFECT);
174	}
175	if (write(fd, &tmp_uint2, sizeof (tmp_uint2)) != sizeof (tmp_uint2)) {
176		return (AUDIO_ERR_NOEFFECT);
177	}
178
179	return (AUDIO_SUCCESS);
180
181}
182
183/*
184 * Rewrite the data size field for the .au file format. Rewrite the audio
185 * file header au_data_size field with the supplied value. Otherwise,
186 * return AUDIO_ERR_NOEFFECT.
187 */
188static int
189audio_rewrite_au_filesize(int fd, unsigned int size)
190{
191	au_filehdr_t	fhdr;
192	int		err;
193	int		data;
194	int		offset;
195
196	/* seek to the position of the au_data_size member */
197	offset = (char *)&fhdr.au_data_size - (char *)&fhdr;
198	if (lseek(fd, offset, SEEK_SET) < 0) {
199		return (AUDIO_ERR_NOEFFECT);
200	}
201
202	/* Encode the 32-bit integer header field */
203	AUDIO_AU_HOST2FILE(&size, &data);
204
205	/* Write the data */
206	err = write(fd, (char *)&data, sizeof (fhdr.au_data_size));
207	if (err != sizeof (fhdr.au_data_size))
208		return ((err < 0) ? AUDIO_UNIXERROR : AUDIO_ERR_BADFILEHDR);
209
210	return (AUDIO_SUCCESS);
211
212}
213
214/*
215 * Rewrite the riff header chunk length and the data chunk length fields.
216 */
217static int
218audio_rewrite_wav_filesize(int fd, unsigned int size)
219{
220	wav_filehdr_t	fhdr;
221	int		calc_size;
222	int		err;
223	int		data;
224	int		offset;
225
226	/* seek to the position of the riff header chunk length */
227	calc_size = size + sizeof (fhdr) - sizeof (fhdr.wav_riff_ID) -
228	    sizeof (fhdr.wav_riff_size);
229	AUDIO_WAV_HOST2FILE_INT(&calc_size, &data);
230	offset = (char *)&fhdr.wav_riff_size - (char *)&fhdr;
231	if (lseek(fd, offset, SEEK_SET) < 0) {
232		return (AUDIO_ERR_NOEFFECT);
233	}
234
235	/* Write the data */
236	err = write(fd, (char *)&data, sizeof (fhdr.wav_riff_size));
237	if (err != sizeof (fhdr.wav_riff_size))
238		return ((err < 0) ? AUDIO_UNIXERROR : AUDIO_ERR_BADFILEHDR);
239
240	/* now seek to the position of the data chunk length */
241	AUDIO_WAV_HOST2FILE_INT(&size, &data);
242	offset = (char *)&fhdr.wav_data_size - (char *)&fhdr;
243	if (lseek(fd, offset, SEEK_SET) < 0) {
244		return (AUDIO_ERR_NOEFFECT);
245	}
246
247	/* Write the data */
248	err = write(fd, (char *)&data, sizeof (fhdr.wav_data_size));
249	if (err != sizeof (fhdr.wav_data_size))
250		return ((err < 0) ? AUDIO_UNIXERROR : AUDIO_ERR_BADFILEHDR);
251
252	return (AUDIO_SUCCESS);
253
254}
255
256/*
257 * Rewrite the data size field of an audio header to the output stream if
258 * the output file is capable of seeking.
259 */
260int
261audio_rewrite_filesize(int fd, int file_type, unsigned int size,
262	unsigned int channels, unsigned int bytes_per_sample)
263					/* file descriptor */
264					/* audio file type */
265					/* new data size */
266					/* number of channels */
267					/* number of bytes per sample */
268{
269	int		fcntl_err;
270
271	/* Can we seek back in this file and write without appending? */
272	fcntl_err = fcntl(fd, F_GETFL, 0);
273	if ((fcntl_err < 0) && ((errno == EOVERFLOW) || (errno == EINVAL))) {
274		/* Large file encountered (probably) */
275		perror("fcntl");
276		exit(1);
277	} else if ((lseek(fd, (off_t)0, SEEK_SET) < 0) ||
278		    (fcntl_err & FAPPEND)) {
279		return (AUDIO_ERR_NOEFFECT);
280	}
281
282	switch (file_type) {
283	case FILE_AU:
284		return (audio_rewrite_au_filesize(fd, size));
285	case FILE_WAV:
286		return (audio_rewrite_wav_filesize(fd, size));
287	case FILE_AIFF:
288		return (audio_rewrite_aiff_filesize(fd, size, channels,
289		    bytes_per_sample));
290	default:
291		return (AUDIO_ERR_BADFILETYPE);
292	}
293}
294
295
296/*
297 * Decode an audio file header from an input stream.
298 *
299 * The file header is decoded into the supplied Audio_hdr structure, regardless
300 * of the file format. Thus .wav and .aiff files look like .au files once the
301 * header is decoded.
302 *
303 * If 'infop' is not NULL, it is the address of a buffer to which the
304 * 'info' portion of the file header will be copied.  'ilen' specifies
305 * the maximum number of bytes to copy.  The buffer will be NULL-terminated,
306 * even if it means over-writing the last byte.
307 *
308 * Note that the .au file header is stored on-disk in big-endian format,
309 * regardless of the machine type.  This may not have been true if
310 * the file was written on a non-Sun machine.  For now, such
311 * files will appear invalid.
312 *
313 * Note also that the input file descriptor must not have been set up
314 * non-blocking i/o.  If non-blocking behavior is desired, set this
315 * flag after reading the file header.
316 */
317int
318audio_read_filehdr(int fd, Audio_hdr *hdrp, int *file_type, char *infop,
319	unsigned int ilen)
320					/* input file descriptor */
321					/* output audio header */
322					/* audio file type */
323					/* info buffer pointer */
324					/* buffer size */
325{
326	int		err;
327	int		dsize;
328	int		isize;
329	unsigned	resid;
330	unsigned char	buf[HEADER_BUFFER];
331	struct stat	st;
332
333	/* decode the file header and fill in the hdrp structure */
334	if ((err = audio_decode_filehdr(fd, buf, file_type, hdrp, &isize)) !=
335	    AUDIO_SUCCESS) {
336		goto checkerror;
337	}
338
339	/* Stat the file, to determine if it is a regular file. */
340	err = fstat(fd, &st);
341	if (err < 0) {
342		return (AUDIO_UNIXERROR);
343	}
344
345	/*
346	 * If au_data_size is not indeterminate (i.e., this isn't a pipe),
347	 * try to validate the au_offset and au_data_size.
348	 */
349	if (*file_type == FILE_AU && hdrp->data_size != AUDIO_UNKNOWN_SIZE) {
350		/* Only trust the size for regular files */
351		if (S_ISREG(st.st_mode)) {
352			dsize = isize + hdrp->data_size + sizeof (au_filehdr_t);
353			if (st.st_size < dsize) {
354				(void) fprintf(stderr,
355				    _MGET_("Warning: More audio data "
356				    "than the file header specifies\n"));
357			} else if (st.st_size > dsize) {
358				(void) fprintf(stderr,
359				    _MGET_("Warning: Less audio data "
360				    "than the file header specifies\n"));
361			}
362		}
363	}
364
365	resid = isize;
366	/*
367	 * Deal with extra header data.
368	 */
369	if ((infop != NULL) && (ilen != 0)) {
370		/*
371		 * If infop is non-NULL, try to read in the info data
372		 */
373		if (isize > ilen)
374			isize = ilen;
375		err = read(fd, infop, (int)isize);
376		if (err != isize)
377			goto checkerror;
378
379		/* Zero any residual bytes in the text buffer */
380		if (isize < ilen)
381			(void) memset(&infop[isize], '\0',
382				    (int)(ilen - isize));
383		else
384			infop[ilen - 1] = '\0';	/* zero-terminate */
385
386		resid -= err;		/* subtract the amount read */
387	}
388
389	/*
390	 * If we truncated the info, seek or read data until info size
391	 * is satisfied.  If regular file, seek nearly to end and check
392	 * for eof.
393	 */
394	if (resid != 0) {
395		if (S_ISREG(st.st_mode)) {
396			err = lseek(fd, (off_t)(resid - 1), SEEK_CUR);
397			if ((err < 0) ||
398			    ((err = read(fd, (char *)buf, 1)) != 1))
399				goto checkerror;
400		} else while (resid != 0) {
401			char	junk[8192];	/* temporary buffer */
402
403			isize = (resid > sizeof (junk)) ?
404			    sizeof (junk) : resid;
405			err = read(fd, junk, isize);
406			if (err != isize)
407				goto checkerror;
408			resid -= err;
409		}
410	}
411
412	return (AUDIO_SUCCESS);
413
414checkerror:
415	if ((err < 0) && (errno == EOVERFLOW)) {
416		perror("read");
417		exit(1);
418	} else {
419		return ((err < 0) ? AUDIO_UNIXERROR : AUDIO_ERR_BADFILEHDR);
420	}
421	return (AUDIO_SUCCESS);
422}
423
424/*
425 * Return TRUE if the named file is an audio file.  Else, return FALSE.
426 */
427int
428audio_isaudiofile(char *name)
429{
430	int		fd;
431	int		err;
432	int		file_type;	/* ignored */
433	int		isize;
434	Audio_hdr	hdr;
435	unsigned char	buf[sizeof (au_filehdr_t)];
436
437	/* Open the file (set O_NONBLOCK in case the name refers to a device) */
438	fd = open(name, O_RDONLY | O_NONBLOCK);
439	if (fd < 0) {
440		if (errno == EOVERFLOW) {
441			perror("open");
442			exit(1);
443		} else {
444			return (FALSE);
445		}
446	}
447
448	/* Read the header (but not the text info). */
449	err = read(fd, (char *)buf, sizeof (buf));
450	if (err < 0) {
451		if (errno == EOVERFLOW) {
452			perror("open");
453			exit(1);
454		} else {
455			return (FALSE);
456		}
457	}
458	(void) close(fd);
459
460	if ((err == sizeof (buf)) &&
461	    (audio_decode_filehdr(fd, buf, &file_type, &hdr, &isize) ==
462	    AUDIO_SUCCESS)) {
463		return (hdr.encoding);
464	} else {
465		return (FALSE);
466	}
467}
468
469/*
470 * audio_endian()
471 *
472 * This routine tests the magic number at the head of a buffer
473 * containing the file header.  The first thing in the header
474 * should be the magic number.
475 */
476static int
477audio_endian(unsigned char *buf, int *file_type)
478{
479	unsigned int	magic1;
480	unsigned int	magic2;
481
482	/* put the buffer into an int that is aligned properly */
483	(void) memcpy(&magic1, buf, sizeof (magic1));
484
485	magic2 = magic1;
486	SWABI(magic2);
487
488	if (magic1 == AUDIO_AU_FILE_MAGIC || magic2 == AUDIO_AU_FILE_MAGIC) {
489		*file_type = FILE_AU;
490		return (AUDIO_ENDIAN_BIG);
491	} else if (magic1 == AUDIO_WAV_RIFF_ID || magic2 == AUDIO_WAV_RIFF_ID) {
492		*file_type = FILE_WAV;
493		return (AUDIO_ENDIAN_SMALL);
494	} else if (magic1 == AUDIO_AIFF_HDR_CHUNK_ID ||
495	    magic2 == AUDIO_AIFF_HDR_CHUNK_ID) {
496		*file_type = FILE_AIFF;
497		return (AUDIO_ENDIAN_BIG);
498	}
499
500	return (AUDIO_ENDIAN_UNKNOWN);
501}
502
503/*
504 * Decode an aiff file header. Unlike .au and .wav, we have to process
505 * by chunk.
506 */
507static int
508decode_aiff(int fd, unsigned char *buf, Audio_hdr *hdrp, int *isize)
509{
510	aiff_hdr_chunk_t	hdr_chunk;
511	aiff_comm_chunk_t	comm_chunk;
512	aiff_ssnd_chunk_t	ssnd_chunk;
513	uint32_t		ID;
514	uint32_t		size;
515	uint32_t		tmp;
516	int			data_type;
517	int			hdr_sizes;
518	int			sr;
519	short			bits_per_sample;
520	short			channels;
521
522	/* we've read in 4 bytes, read in the rest of the wav header */
523	size = sizeof (hdr_chunk) - sizeof (hdr_chunk.aiff_hdr_ID);
524
525	/* read in the rest of the header */
526	if (read(fd, &hdr_chunk.aiff_hdr_size, size) != size) {
527		return (AUDIO_UNIXERROR);
528	}
529
530	/* see which kind of audio file we have */
531	AUDIO_AIFF_FILE2HOST_INT(&hdr_chunk.aiff_hdr_data_type, &data_type);
532	if (data_type != AUDIO_AIFF_HDR_FORM_AIFF) {
533		/* we can't play this version of a .aiff file */
534		return (AUDIO_ERR_BADFILEHDR);
535	}
536
537	hdr_sizes = sizeof (hdr_chunk);
538
539	/*
540	 * We don't know what the chunk order will be, so read each, getting
541	 * the data we need from each. Eventually we'll get to the end of
542	 * the file, in which case we should have all of the info on the
543	 * file that we need. We then lseek() back to the data to play.
544	 *
545	 * We start each loop by reading the chunk ID.
546	 */
547	while (read(fd, &tmp, sizeof (tmp)) == sizeof (tmp)) {
548		AUDIO_AIFF_FILE2HOST_INT(&tmp, &ID);
549		switch (ID) {
550		case AUDIO_AIFF_COMM_ID:
551			/* read in the rest of the COMM chunk */
552			size = AUDIO_AIFF_COMM_CHUNK_SIZE -
553			    sizeof (comm_chunk.aiff_comm_ID);
554			if (read(fd, &comm_chunk.aiff_comm_size, size) !=
555			    size) {
556				return (AUDIO_UNIXERROR);
557			}
558
559			sr = convert_from_ieee_extended(
560			    comm_chunk.aiff_comm_sample_rate);
561
562			hdr_sizes += AUDIO_AIFF_COMM_CHUNK_SIZE;
563
564			break;
565		case AUDIO_AIFF_SSND_ID:
566			/* read in the rest of the INST chunk */
567			size = sizeof (ssnd_chunk) -
568			    sizeof (ssnd_chunk.aiff_ssnd_ID);
569			if (read(fd, &ssnd_chunk.aiff_ssnd_size, size) !=
570			    size) {
571				return (AUDIO_UNIXERROR);
572			}
573
574			/*
575			 * This has to be the last chunk because the audio data
576			 * follows. So we should have all we need to tell the
577			 * app the format information.
578			 */
579			hdrp->sample_rate = sr;
580
581			AUDIO_AIFF_FILE2HOST_SHORT(
582			    &comm_chunk.aiff_comm_channels,
583			    &channels);
584			/* use channels to convert from short to int */
585			hdrp->channels = channels;
586
587			AUDIO_AIFF_FILE2HOST_SHORT(
588			    &comm_chunk.aiff_comm_sample_size,
589			    &bits_per_sample);
590			switch (bits_per_sample) {
591			case AUDIO_AIFF_COMM_8_BIT_SAMPLE_SIZE:
592				hdrp->encoding = AUDIO_AU_ENCODING_LINEAR_8;
593				break;
594			case AUDIO_AIFF_COMM_16_BIT_SAMPLE_SIZE:
595				hdrp->encoding = AUDIO_AU_ENCODING_LINEAR_16;
596				break;
597			default:
598				return (AUDIO_ERR_BADFILEHDR);
599			}
600
601			AUDIO_AIFF_FILE2HOST_INT(&ssnd_chunk.aiff_ssnd_size,
602			    &size);
603			size -= sizeof (ssnd_chunk.aiff_ssnd_offset) +
604			    sizeof (ssnd_chunk.aiff_ssnd_block_size);
605			hdrp->data_size = size;
606
607			hdr_sizes += sizeof (ssnd_chunk);
608
609			*isize = hdr_sizes - sizeof (au_filehdr_t);
610
611			return (AUDIO_SUCCESS);
612		default:
613			/*
614			 * Unknown chunk. Read the size, which is right after
615			 * the ID. Then seek past it to get to the next chunk.
616			 */
617			if (read(fd, &size, sizeof (size)) != sizeof (size)) {
618				return (AUDIO_UNIXERROR);
619			}
620
621			if (lseek(fd, size, SEEK_CUR) < 0) {
622				return (AUDIO_UNIXERROR);
623			}
624			break;
625		}
626	}
627
628	return (AUDIO_SUCCESS);
629
630}	/* decode_aiff() */
631
632/*
633 * Decode an au file header.
634 */
635static int
636decode_au(int fd, unsigned char *buf, Audio_hdr *hdrp, int *isize,
637    boolean_t read_info)
638{
639	au_filehdr_t	fhdr;
640	int		offset;
641	int		size;
642
643	if (read_info) {
644		/* read in the rest of the au header */
645		size = sizeof (fhdr) - sizeof (int);
646		(void) lseek(fd, (off_t)4, SEEK_SET);
647		if (read(fd, &buf[sizeof (int)], size) != size) {
648
649			return (AUDIO_UNIXERROR);
650		}
651	}
652
653	/* put the buffer into a structure that is aligned properly */
654	(void) memcpy(&fhdr, buf, sizeof (fhdr));
655
656	/* Decode the 32-bit integer header fields. */
657	AUDIO_AU_FILE2HOST(&fhdr.au_offset, &offset);
658	AUDIO_AU_FILE2HOST(&fhdr.au_data_size, &hdrp->data_size);
659	AUDIO_AU_FILE2HOST(&fhdr.au_encoding, &hdrp->encoding);
660	AUDIO_AU_FILE2HOST(&fhdr.au_sample_rate, &hdrp->sample_rate);
661	AUDIO_AU_FILE2HOST(&fhdr.au_channels, &hdrp->channels);
662
663	/* Set the info field size (ie, number of bytes left before data). */
664	*isize = offset - sizeof (au_filehdr_t);
665
666	return (AUDIO_SUCCESS);
667
668}	/* decode_au() */
669
670/*
671 * Decode a wav file header.
672 *
673 * .wav files are stored on-disk in little-endian format.
674 */
675static int
676decode_wav(int fd, unsigned char *buf, Audio_hdr *hdrp, int *isize)
677{
678	wav_filehdr_t	fhdr;
679	uint32_t	ID;
680	uint32_t	size;
681	short		bits_per_sample;
682	short		encoding;
683
684	/* we've read in 4 bytes, read in the rest of the wav header */
685	size = sizeof (fhdr) - sizeof (int);
686
687	/* read in the rest of the header */
688	if (read(fd, &buf[sizeof (int)], size) != size) {
689		return (AUDIO_UNIXERROR);
690	}
691
692	/* put the buffer into a structure that is aligned properly */
693	(void) memcpy(&fhdr, buf, sizeof (fhdr));
694
695	/* make sure we have the correct RIFF type */
696	AUDIO_WAV_FILE2HOST_INT(&fhdr.wav_type_ID, &ID);
697	if (ID != AUDIO_WAV_TYPE_ID) {
698		/* not a wave file */
699		return (AUDIO_ERR_BADFILEHDR);
700	}
701
702	/* decode the fields */
703	AUDIO_WAV_FILE2HOST_INT(&fhdr.wav_fmt_ID, &ID);
704	if (ID != AUDIO_WAV_FORMAT_ID) {
705		/* mangled format */
706		return (AUDIO_ERR_BADFILEHDR);
707	}
708
709	AUDIO_WAV_FILE2HOST_SHORT(&fhdr.wav_fmt_encoding, &encoding);
710	AUDIO_WAV_FILE2HOST_SHORT(&fhdr.wav_fmt_channels, &hdrp->channels);
711	AUDIO_WAV_FILE2HOST_INT(&fhdr.wav_fmt_sample_rate, &hdrp->sample_rate);
712	AUDIO_WAV_FILE2HOST_SHORT(&fhdr.wav_fmt_bits_per_sample,
713	    &bits_per_sample);
714
715	/* convert .wav encodings to .au encodings */
716	switch (encoding) {
717	case AUDIO_WAV_FMT_ENCODING_PCM:
718		switch (bits_per_sample) {
719		case AUDIO_WAV_FMT_BITS_PER_SAMPLE_8_BITS:
720			hdrp->encoding = AUDIO_AU_ENCODING_LINEAR_8;
721			break;
722		case AUDIO_WAV_FMT_BITS_PER_SAMPLE_16_BITS:
723			hdrp->encoding = AUDIO_AU_ENCODING_LINEAR_16;
724			break;
725		default:
726			return (AUDIO_ERR_BADFILEHDR);
727		}
728		break;
729	case AUDIO_WAV_FMT_ENCODING_ALAW:
730		hdrp->encoding = AUDIO_AU_ENCODING_ALAW;
731		break;
732	case AUDIO_WAV_FMT_ENCODING_MULAW:
733		hdrp->encoding = AUDIO_AU_ENCODING_ULAW;
734		break;
735	default:
736		return (AUDIO_ERR_BADFILEHDR);
737	}
738
739	AUDIO_WAV_FILE2HOST_INT(&fhdr.wav_data_size, &hdrp->data_size);
740
741	*isize = sizeof (wav_filehdr_t) - sizeof (au_filehdr_t);
742
743	return (AUDIO_SUCCESS);
744
745}	/* decode_wav() */
746
747/*
748 * Try to decode buffer containing an audio file header into an audio header.
749 */
750int
751audio_decode_filehdr(int fd, unsigned char *buf, int *file_type,
752	Audio_hdr *hdrp, int *isize)
753					/* file descriptor */
754					/* buffer address */
755					/* audio file type */
756					/* output audio header */
757					/* output size of info */
758{
759	int		err;
760	struct stat	fd_stat;
761	boolean_t	read_info;
762
763	/* Test for .au first */
764	hdrp->endian = audio_endian(buf, file_type);
765
766	/*
767	 * When cat'ing a file, audioconvert will read the whole header
768	 * trying to figure out the file. audioplay however, does not.
769	 * Hence we check if this is a pipe and do not attempt to read
770	 * any more header info if the file type is already known.
771	 * Otherwise we overwrite the header data already in the buffer.
772	 */
773	if (fstat(fd, &fd_stat) < 0) {
774		return (AUDIO_ERR_BADFILEHDR);
775	}
776	if (S_ISFIFO(fd_stat.st_mode) && (*file_type == FILE_AU)) {
777		read_info = B_FALSE;
778	} else {
779		/*
780		 * Not an au file, or file type unknown. Reread the header's
781		 * magic number. Fortunately this is always an int.
782		 */
783		(void) lseek(fd, (off_t)0, SEEK_SET);
784		err = read(fd, (char *)buf, sizeof (int));
785		read_info = B_TRUE;
786
787		/* test the magic number to determine the endian */
788		if ((hdrp->endian = audio_endian(buf, file_type)) ==
789		    AUDIO_ENDIAN_UNKNOWN) {
790
791			return (AUDIO_ERR_BADFILEHDR);
792		}
793	}
794
795	/* decode the different file types, putting the data into hdrp */
796	switch (*file_type) {
797	case FILE_AU:
798		if ((err = decode_au(fd, buf, hdrp, isize, read_info)) !=
799		    AUDIO_SUCCESS) {
800			return (err);
801		}
802		break;
803	case FILE_WAV:
804		if ((err = decode_wav(fd, buf, hdrp, isize)) != AUDIO_SUCCESS) {
805			return (err);
806		}
807		break;
808	case FILE_AIFF:
809		if ((err = decode_aiff(fd, buf, hdrp, isize)) !=
810		    AUDIO_SUCCESS) {
811			return (err);
812		}
813		break;
814	default:
815		return (AUDIO_ERR_BADFILEHDR);
816	}
817
818	/* Convert from file format info to audio format info */
819	switch (hdrp->encoding) {
820	case AUDIO_AU_ENCODING_ULAW:
821		hdrp->encoding = AUDIO_ENCODING_ULAW;
822		hdrp->bytes_per_unit = 1;
823		hdrp->samples_per_unit = 1;
824		break;
825	case AUDIO_AU_ENCODING_ALAW:
826		hdrp->encoding = AUDIO_ENCODING_ALAW;
827		hdrp->bytes_per_unit = 1;
828		hdrp->samples_per_unit = 1;
829		break;
830	case AUDIO_AU_ENCODING_LINEAR_8:
831		if (*file_type == FILE_WAV) {
832			hdrp->encoding = AUDIO_ENCODING_LINEAR8;
833		} else {
834			hdrp->encoding = AUDIO_ENCODING_LINEAR;
835		}
836		hdrp->bytes_per_unit = 1;
837		hdrp->samples_per_unit = 1;
838		break;
839	case AUDIO_AU_ENCODING_LINEAR_16:
840		hdrp->encoding = AUDIO_ENCODING_LINEAR;
841		hdrp->bytes_per_unit = 2;
842		hdrp->samples_per_unit = 1;
843		break;
844	case AUDIO_AU_ENCODING_LINEAR_24:
845		hdrp->encoding = AUDIO_ENCODING_LINEAR;
846		hdrp->bytes_per_unit = 3;
847		hdrp->samples_per_unit = 1;
848		break;
849	case AUDIO_AU_ENCODING_LINEAR_32:
850		hdrp->encoding = AUDIO_ENCODING_LINEAR;
851		hdrp->bytes_per_unit = 4;
852		hdrp->samples_per_unit = 1;
853		break;
854	case AUDIO_AU_ENCODING_FLOAT:
855		hdrp->encoding = AUDIO_ENCODING_FLOAT;
856		hdrp->bytes_per_unit = 4;
857		hdrp->samples_per_unit = 1;
858		break;
859	case AUDIO_AU_ENCODING_DOUBLE:
860		hdrp->encoding = AUDIO_ENCODING_FLOAT;
861		hdrp->bytes_per_unit = 8;
862		hdrp->samples_per_unit = 1;
863		break;
864	case AUDIO_AU_ENCODING_ADPCM_G721:
865		hdrp->encoding = AUDIO_ENCODING_G721;
866		hdrp->bytes_per_unit = 1;
867		hdrp->samples_per_unit = 2;
868		break;
869	case AUDIO_AU_ENCODING_ADPCM_G723_3:
870		hdrp->encoding = AUDIO_ENCODING_G723;
871		hdrp->bytes_per_unit = 3;
872		hdrp->samples_per_unit = 8;
873		break;
874	case AUDIO_AU_ENCODING_ADPCM_G723_5:
875		hdrp->encoding = AUDIO_ENCODING_G723;
876		hdrp->bytes_per_unit = 5;
877		hdrp->samples_per_unit = 8;
878		break;
879
880	default:
881		return (AUDIO_ERR_BADFILEHDR);
882	}
883	return (AUDIO_SUCCESS);
884}
885
886/*
887 * Encode a .aiff file header from the supplied Audio_hdr structure and
888 * store in the supplied char* buffer. blen is the size of the buffer to
889 * store the header in. Unlike .au and .wav we can't cast to a data structure.
890 * We have to build it one chunk at a time.
891 *
892 * NOTE: .aiff doesn't support unsigned 8-bit linear PCM.
893 */
894static int
895audio_encode_aiff(Audio_hdr *hdrp, unsigned char *buf, unsigned int *blen)
896					/* audio header */
897					/* output buffer */
898					/* output buffer size */
899{
900	aiff_comm_chunk_t	comm_chunk;
901	aiff_hdr_chunk_t	hdr_chunk;
902	aiff_ssnd_chunk_t	ssnd_chunk;
903	uint32_t		tmp_uint;
904	uint32_t		tmp_uint2;
905	int			buf_size = 0;
906	int			encoding;
907	uint16_t		tmp_ushort;
908
909	/* the only encoding we support for .aiff is signed linear PCM */
910	if (hdrp->encoding != AUDIO_ENCODING_LINEAR) {
911		return (AUDIO_ERR_ENCODING);
912	}
913
914	/* build the header chunk */
915	tmp_uint = AUDIO_AIFF_HDR_CHUNK_ID;
916	AUDIO_AIFF_HOST2FILE_INT(&tmp_uint, &hdr_chunk.aiff_hdr_ID);
917	/* needs to be fixed when closed */
918	tmp_uint = AUDIO_AIFF_UNKNOWN_SIZE;
919	AUDIO_AIFF_HOST2FILE_INT(&tmp_uint, &hdr_chunk.aiff_hdr_size);
920	tmp_uint = AUDIO_AIFF_HDR_FORM_AIFF;
921	AUDIO_AIFF_HOST2FILE_INT(&tmp_uint, &hdr_chunk.aiff_hdr_data_type);
922	(void) memcpy(&buf[buf_size], &hdr_chunk, sizeof (hdr_chunk));
923	buf_size += sizeof (hdr_chunk);
924
925	/* build the COMM chunk */
926	tmp_uint = AUDIO_AIFF_COMM_ID;
927	AUDIO_AIFF_HOST2FILE_INT(&tmp_uint, &comm_chunk.aiff_comm_ID);
928	tmp_uint = AUDIO_AIFF_COMM_SIZE;
929	AUDIO_AIFF_HOST2FILE_INT(&tmp_uint, &comm_chunk.aiff_comm_size);
930	tmp_ushort = hdrp->channels;
931	AUDIO_AIFF_HOST2FILE_SHORT(&tmp_ushort, &comm_chunk.aiff_comm_channels);
932	/* needs to be fixed when closed */
933	tmp_uint = AUDIO_AIFF_UNKNOWN_SIZE;
934	AUDIO_AIFF_HOST2FILE_INT(&tmp_uint, &tmp_uint2);
935	AUDIO_AIFF_COMM_INT2FRAMES(comm_chunk.aiff_comm_frames, tmp_uint2);
936	tmp_ushort = hdrp->bytes_per_unit * 8;
937	AUDIO_AIFF_HOST2FILE_SHORT(&tmp_ushort,
938	    &comm_chunk.aiff_comm_sample_size);
939	convert_to_ieee_extended((double)hdrp->sample_rate,
940	    comm_chunk.aiff_comm_sample_rate);
941	(void) memcpy(&buf[buf_size], &comm_chunk, AUDIO_AIFF_COMM_CHUNK_SIZE);
942	buf_size += AUDIO_AIFF_COMM_CHUNK_SIZE;
943
944	/* build the SSND chunk */
945	tmp_uint = AUDIO_AIFF_SSND_ID;
946	AUDIO_AIFF_HOST2FILE_INT(&tmp_uint, &ssnd_chunk.aiff_ssnd_ID);
947	/* needs to be fixed when closed */
948	tmp_uint = AUDIO_AIFF_UNKNOWN_SIZE;
949	AUDIO_AIFF_HOST2FILE_INT(&tmp_uint, &ssnd_chunk.aiff_ssnd_size);
950	ssnd_chunk.aiff_ssnd_offset = 0;
951	ssnd_chunk.aiff_ssnd_block_size = 0;
952	(void) memcpy(&buf[buf_size], &ssnd_chunk, sizeof (ssnd_chunk));
953	buf_size += sizeof (ssnd_chunk);
954
955	*blen = buf_size;
956
957	return (AUDIO_SUCCESS);
958
959}	/* audio_encode_aiff() */
960
961/*
962 * Encode a .au file header from the supplied Audio_hdr structure and
963 * store in the supplied char* buffer. blen is the size of the buffer to
964 * store the header in. If 'infop' is not NULL, it is the address of a
965 * buffer containing 'info' data. 'ilen' specifies the size of this buffer.
966 * The entire file header will be zero-padded to a double-word boundary.
967 *
968 * NOTE: .au doesn't support unsigned 8-bit linear PCM.
969 */
970static int
971audio_encode_au(Audio_hdr *hdrp, char *infop, unsigned int ilen,
972	unsigned char *buf, unsigned int *blen)
973					/* audio header */
974					/* info buffer pointer */
975					/* info buffer size */
976					/* output buffer */
977					/* output buffer size */
978{
979	au_filehdr_t	fhdr;
980	int		encoding;
981	int		hdrsize;
982	int		magic;
983	int		offset;
984
985	/*
986	 * Set the size of the real header (hdr size + info size).
987	 * If no supplied info, make sure a minimum size is accounted for.
988	 * Also, round the whole thing up to double-word alignment.
989	 */
990	if ((infop == NULL) || (ilen == 0)) {
991		infop = NULL;
992		ilen = 4;
993	}
994	hdrsize = sizeof (fhdr) + ilen;
995	offset = ROUND_DBL(hdrsize);
996
997	/* Check the data encoding. */
998	switch (hdrp->encoding) {
999	case AUDIO_ENCODING_LINEAR8:
1000		return (AUDIO_ERR_ENCODING);	/* we don't support ulinear */
1001	case AUDIO_ENCODING_ULAW:
1002		if (hdrp->samples_per_unit != 1)
1003			return (AUDIO_ERR_BADHDR);
1004
1005		switch (hdrp->bytes_per_unit) {
1006		case 1:
1007			encoding = AUDIO_AU_ENCODING_ULAW;
1008			break;
1009		default:
1010			return (AUDIO_ERR_BADHDR);
1011		}
1012		break;
1013	case AUDIO_ENCODING_ALAW:
1014		if (hdrp->samples_per_unit != 1)
1015			return (AUDIO_ERR_BADHDR);
1016
1017		switch (hdrp->bytes_per_unit) {
1018		case 1:
1019			encoding = AUDIO_AU_ENCODING_ALAW;
1020			break;
1021		default:
1022			return (AUDIO_ERR_BADHDR);
1023		}
1024		break;
1025	case AUDIO_ENCODING_LINEAR:
1026		if (hdrp->samples_per_unit != 1)
1027			return (AUDIO_ERR_BADHDR);
1028
1029		switch (hdrp->bytes_per_unit) {
1030		case 1:
1031			encoding = AUDIO_AU_ENCODING_LINEAR_8;
1032			break;
1033		case 2:
1034			encoding = AUDIO_AU_ENCODING_LINEAR_16;
1035			break;
1036		case 3:
1037			encoding = AUDIO_AU_ENCODING_LINEAR_24;
1038			break;
1039		case 4:
1040			encoding = AUDIO_AU_ENCODING_LINEAR_32;
1041			break;
1042		default:
1043			return (AUDIO_ERR_BADHDR);
1044		}
1045		break;
1046	case AUDIO_ENCODING_FLOAT:
1047		if (hdrp->samples_per_unit != 1)
1048			return (AUDIO_ERR_BADHDR);
1049
1050		switch (hdrp->bytes_per_unit) {
1051		case 4:
1052			encoding = AUDIO_AU_ENCODING_FLOAT;
1053			break;
1054		case 8:
1055			encoding = AUDIO_AU_ENCODING_DOUBLE;
1056			break;
1057		default:
1058			return (AUDIO_ERR_BADHDR);
1059		}
1060		break;
1061	case AUDIO_ENCODING_G721:
1062		if (hdrp->bytes_per_unit != 1)
1063			return (AUDIO_ERR_BADHDR);
1064		else if (hdrp->samples_per_unit != 2)
1065			return (AUDIO_ERR_BADHDR);
1066		else
1067			encoding = AUDIO_AU_ENCODING_ADPCM_G721;
1068		break;
1069	case AUDIO_ENCODING_G723:
1070		if (hdrp->samples_per_unit != 8)
1071			return (AUDIO_ERR_BADHDR);
1072		else if (hdrp->bytes_per_unit == 3)
1073			encoding = AUDIO_AU_ENCODING_ADPCM_G723_3;
1074		else if (hdrp->bytes_per_unit == 5)
1075			encoding = AUDIO_AU_ENCODING_ADPCM_G723_5;
1076		else
1077			return (AUDIO_ERR_BADHDR);
1078		break;
1079	default:
1080		return (AUDIO_ERR_BADHDR);
1081	}
1082
1083	/* copy the fhdr into the supplied buffer - make sure it'll fit */
1084	if (*blen < offset) {
1085		/* XXX - is this apropriate? */
1086		return (AUDIO_EOF);
1087	}
1088
1089	/* reset blen to actual size of hdr data */
1090	*blen = (unsigned)offset;
1091
1092	magic = AUDIO_AU_FILE_MAGIC;	/* set the magic number */
1093
1094	/* Encode the audio header structure. */
1095	AUDIO_AU_HOST2FILE(&magic, &fhdr.au_magic);
1096	AUDIO_AU_HOST2FILE(&offset, &fhdr.au_offset);
1097	AUDIO_AU_HOST2FILE(&hdrp->data_size, &fhdr.au_data_size);
1098	AUDIO_AU_HOST2FILE(&encoding, &fhdr.au_encoding);
1099	AUDIO_AU_HOST2FILE(&hdrp->sample_rate, &fhdr.au_sample_rate);
1100	AUDIO_AU_HOST2FILE(&hdrp->channels, &fhdr.au_channels);
1101
1102	/* Copy to the buffer */
1103	(void) memcpy(buf, &fhdr, sizeof (fhdr));
1104
1105	/* Copy the info data, if present */
1106	if (infop != NULL) {
1107		(void) memcpy(&buf[sizeof (fhdr)], infop, (int)ilen);
1108		buf += ilen;
1109	}
1110
1111	if (offset > hdrsize) {
1112		(void) memset(&buf[hdrsize], '\0', (size_t)(offset - hdrsize));
1113	}
1114
1115	/* buf now has the data, just return ... */
1116
1117	return (AUDIO_SUCCESS);
1118
1119}	/* audio_encode_au() */
1120
1121/*
1122 * Encode a .wav file header from the supplied Audio_hdr structure and
1123 * store in the supplied char* buffer. blen is the size of the buffer to
1124 * store the header in. .wav doesn't support an information string like
1125 * .au does.
1126 *
1127 * NOTE: .wav only supports a few encoding methods.
1128 */
1129static int
1130audio_encode_wav(Audio_hdr *hdrp, unsigned char *buf, unsigned int *blen)
1131					/* audio header */
1132					/* output buffer */
1133					/* output buffer size */
1134{
1135	wav_filehdr_t	fhdr;
1136	int		bytes_per_second;
1137	int		bytes_per_sample;
1138	int		bits_per_sample;
1139	int		id;
1140	int		length;
1141	int		type;
1142	short		encoding;
1143
1144	/* make sure we've got valid encoding and precision settings for .wav */
1145	switch (hdrp->encoding) {
1146	case AUDIO_ENCODING_LINEAR8:
1147		if (hdrp->bytes_per_unit != 1) {
1148			return (AUDIO_ERR_ENCODING);
1149		}
1150		encoding = AUDIO_WAV_FMT_ENCODING_PCM;
1151		break;
1152	case AUDIO_ENCODING_ULAW:
1153		if (hdrp->bytes_per_unit != 1) {
1154			return (AUDIO_ERR_ENCODING);
1155		}
1156		encoding = AUDIO_WAV_FMT_ENCODING_MULAW;
1157		break;
1158	case AUDIO_ENCODING_ALAW:
1159		if (hdrp->bytes_per_unit != 1) {
1160			return (AUDIO_ERR_ENCODING);
1161		}
1162		encoding = AUDIO_WAV_FMT_ENCODING_ALAW;
1163		break;
1164	case AUDIO_ENCODING_LINEAR:
1165		if (hdrp->bytes_per_unit != 2) {
1166			return (AUDIO_ERR_ENCODING);
1167		}
1168		encoding = AUDIO_WAV_FMT_ENCODING_PCM;
1169		break;
1170	default:
1171		return (AUDIO_ERR_ENCODING);
1172	}
1173
1174	/* fill in the riff chunk */
1175	id = AUDIO_WAV_RIFF_ID;
1176	length = AUDIO_WAV_UNKNOWN_SIZE;
1177	AUDIO_WAV_HOST2FILE_INT(&id, &fhdr.wav_riff_ID);
1178	AUDIO_WAV_HOST2FILE_INT(&length, &fhdr.wav_riff_size);
1179
1180	/* fill in the type chunk */
1181	type = AUDIO_WAV_TYPE_ID;
1182	AUDIO_WAV_HOST2FILE_INT(&type, &fhdr.wav_type_ID);
1183
1184
1185	/* fill in the format chunk */
1186	id = AUDIO_WAV_FORMAT_ID;
1187	length = AUDIO_WAV_FORMAT_SIZE;
1188	bytes_per_second = hdrp->sample_rate * hdrp->channels *
1189	    hdrp->bytes_per_unit;
1190	bytes_per_sample = hdrp->channels * hdrp->bytes_per_unit;
1191	bits_per_sample = hdrp->bytes_per_unit * 8;
1192
1193	AUDIO_WAV_HOST2FILE_INT(&id, &fhdr.wav_fmt_ID);
1194	AUDIO_WAV_HOST2FILE_INT(&length, &fhdr.wav_fmt_size);
1195	AUDIO_WAV_HOST2FILE_SHORT(&encoding, &fhdr.wav_fmt_encoding);
1196	AUDIO_WAV_HOST2FILE_SHORT(&hdrp->channels, &fhdr.wav_fmt_channels);
1197	AUDIO_WAV_HOST2FILE_INT(&hdrp->sample_rate, &fhdr.wav_fmt_sample_rate);
1198	AUDIO_WAV_HOST2FILE_INT(&bytes_per_second,
1199	    &fhdr.wav_fmt_bytes_per_second);
1200	AUDIO_WAV_HOST2FILE_SHORT(&bytes_per_sample,
1201	    &fhdr.wav_fmt_bytes_per_sample);
1202	AUDIO_WAV_HOST2FILE_SHORT(&bits_per_sample,
1203	    &fhdr.wav_fmt_bits_per_sample);
1204
1205	/* fill in the data chunk */
1206	id = AUDIO_WAV_DATA_ID_LC;
1207	length = AUDIO_WAV_UNKNOWN_SIZE;
1208	AUDIO_WAV_HOST2FILE_INT(&id, &fhdr.wav_data_ID);
1209	AUDIO_WAV_HOST2FILE_INT(&length, &fhdr.wav_data_size);
1210
1211	*blen = sizeof (fhdr);
1212
1213	/* copy to the buffer */
1214	(void) memcpy(buf, &fhdr, sizeof (fhdr));
1215
1216	return (AUDIO_SUCCESS);
1217
1218}	/* audio_encode_wav() */
1219
1220/*
1221 * Utility routine used to convert 10 byte IEEE extended float into
1222 * a regular double. Raw data arrives in an unsigned char array. Because
1223 * this is for sample rate, which is always positive, we don't worry
1224 * about the sign.
1225 */
1226static double
1227convert_from_ieee_extended(unsigned char *data)
1228{
1229	double		value = 0.0;
1230	unsigned long	high_mantissa;
1231	unsigned long	low_mantissa;
1232	int		exponent;
1233
1234	/* first 2 bytes are the exponent */
1235	exponent = ((data[0] & 0x7f) << 8) | data[1];
1236
1237	high_mantissa = ((unsigned long)data[2] << 24) |
1238	    ((unsigned long)data[3] << 16) |
1239	    ((unsigned long)data[4] << 8) |
1240	    (unsigned long)data[5];
1241	low_mantissa = ((unsigned long)data[6] << 24) |
1242	    ((unsigned long)data[7] << 16) |
1243	    ((unsigned long)data[8] << 8) |
1244	    (unsigned long)data[9];
1245
1246	/* convert exponent and mantissas into a real double */
1247	if (exponent == 0 && high_mantissa == 0 && low_mantissa == 0) {
1248		/* everything is 0, so we're done */
1249		value = 0.0;
1250	} else {
1251		if (exponent == 0x7fff) {	/* infinity */
1252			value = MAXFLOAT;
1253		} else {
1254			/* convert exponent from being unsigned to signed */
1255			exponent -= 0x3fff;
1256
1257			exponent -= 31;
1258			value = ldexp((double)high_mantissa, exponent);
1259
1260			exponent -= 32;
1261			value += ldexp((double)low_mantissa, exponent);
1262		}
1263	}
1264
1265	return (value);
1266
1267}
1268
1269/*
1270 * Utility routine to convert a double into 10 byte IEEE extended floating
1271 * point. The new number is placed into the unsigned char array. This is a
1272 * very brain dead convesion routine. It only supports integers, but then
1273 * that should be all we need for sample rate.
1274 */
1275static void
1276convert_to_ieee_extended(double value, unsigned char *data)
1277{
1278	double		fmantissa;
1279	int		exponent;
1280	int		mantissa;
1281
1282	exponent = 16398;
1283	fmantissa = value;
1284
1285	while (fmantissa < 44000) {
1286		fmantissa *= 2;
1287		exponent--;
1288	}
1289
1290	mantissa = (int)fmantissa << 16;
1291
1292	data[0] = exponent >> 8;
1293	data[1] = exponent;
1294	data[2] = mantissa >> 24;
1295	data[3] = mantissa >> 16;
1296	data[4] = mantissa >> 8;
1297	data[5] = mantissa;
1298	data[6] = 0;
1299	data[7] = 0;
1300	data[8] = 0;
1301	data[9] = 0;
1302
1303}
1304