1 /* @(#)audiosize.c	1.23 09/07/05 Copyright 1998-2009 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)audiosize.c	1.23 09/07/05 Copyright 1998-2009 J. Schilling";
6 #endif
7 /*
8  *	Copyright (c) 1998-2009 J. Schilling
9  *
10  *	First .vaw implementation made by Dave Platt <dplatt@iq.nc.com>
11  *	Current .wav implementation with additional help from Heiko Ei�feld.
12  */
13 /*
14  * The contents of this file are subject to the terms of the
15  * Common Development and Distribution License, Version 1.0 only
16  * (the "License").  You may not use this file except in compliance
17  * with the License.
18  *
19  * See the file CDDL.Schily.txt in this distribution for details.
20  * A copy of the CDDL is also available via the Internet at
21  * http://www.opensource.org/licenses/cddl1.txt
22  *
23  * When distributing Covered Code, include this CDDL HEADER in each
24  * file and include the License file CDDL.Schily.txt from this distribution.
25  */
26 
27 #include <schily/mconfig.h>
28 #include <schily/stat.h>
29 #include <schily/unistd.h>
30 #include <schily/standard.h>
31 #include <schily/utypes.h>
32 #include <schily/string.h>
33 #include <schily/intcvt.h>
34 #include <schily/schily.h>
35 
36 #include <scg/scgcmd.h>
37 #include "auheader.h"
38 
39 typedef struct {
40 	Uchar	magic[4];
41 	Uchar	hdr_size[4];
42 	Uchar	data_size[4];
43 	Uchar	encoding[4];
44 	Uchar	sample_rate[4];
45 	Uchar	channels[4];
46 } sun_au_t;
47 
48 #define	SUN_AU_MAGIC		".snd"
49 #define	SUN_AU_UNKNOWN_LEN	((Uint)~0)
50 #define	SUN_AU_ULAW8		1		/* American ISDN Telephonie */
51 #define	SUN_AU_LINEAR8		2		/* Linear PCM 8 bit/channel  */
52 #define	SUN_AU_LINEAR16		3		/* Linear PCM 16 bit/channel */
53 #define	SUN_AU_LINEAR24		4		/* Linear PCM 24 bit/channel */
54 #define	SUN_AU_LINEAR32		5		/* Linear PCM 32 bit/channel */
55 #define	SUN_AU_FLOAT		6		/* 32 bit IEEE floatingpoint */
56 #define	SUN_AU_DOUBLE		7		/* 64 bit IEEE floatingpoint */
57 #define	SUN_AU_G721		23		/* 4 bit CCITT G.721 ADPCM  */
58 #define	SUN_AU_G722		24		/* CCITT G.722 ADPCM	    */
59 #define	SUN_AU_G723_3		25		/* 3 bit CCITT G.723 ADPCM  */
60 #define	SUN_AU_G723_5		26		/* 5 bit CCITT G.723 ADPCM  */
61 #define	SUN_AU_ALAW8		27		/* International ISDN Tel.  */
62 
63 typedef struct {
64 	Uchar	ckid[4];
65 	Uchar	cksize[4];
66 } chunk_t;
67 
68 typedef struct {
69 	Uchar	wave[4];
70 } riff_chunk;
71 
72 typedef struct {
73 	Uchar	fmt_tag[2];
74 	Uchar	channels[2];
75 	Uchar	sample_rate[4];
76 	Uchar	av_byte_rate[4];
77 	Uchar	block_size[2];
78 	Uchar	bits_per_sample[2];
79 } fmt_chunk;
80 
81 #define	WAV_RIFF_MAGIC		"RIFF"		/* Magic for file format    */
82 #define	WAV_WAVE_MAGIC		"WAVE"		/* Magic for Waveform Audio */
83 #define	WAV_FMT_MAGIC		"fmt "		/* Start of Waveform format */
84 #define	WAV_DATA_MAGIC		"data"		/* Start of data chunk	    */
85 #define	WAV_FORMAT_PCM		0x0001		/* Linear PCM format	    */
86 #define	WAV_FORMAT_ULAW		0x0101		/* American ISDN Telephonie */
87 #define	WAV_FORMAT_ALAW		0x0102		/* International ISDN Tel.  */
88 #define	WAV_FORMAT_ADPCM	0x0103		/* ADPCM format		    */
89 
90 #define	le_a_to_u_short(a)	((unsigned short) \
91 				((((unsigned char *)a)[0]	& 0xFF) | \
92 				(((unsigned char *)a)[1] << 8	& 0xFF00)))
93 
94 #ifdef	__STDC__
95 #define	le_a_to_u_long(a)	((unsigned long) \
96 				((((unsigned char *)a)[0]	& 0xFF) | \
97 				(((unsigned  char *)a)[1] << 8	& 0xFF00) | \
98 				(((unsigned  char *)a)[2] << 16	& 0xFF0000) | \
99 				(((unsigned  char *)a)[3] << 24	& 0xFF000000UL)))
100 #else
101 #define	le_a_to_u_long(a)	((unsigned long) \
102 				((((unsigned char *)a)[0]	& 0xFF) | \
103 				(((unsigned  char *)a)[1] << 8	& 0xFF00) | \
104 				(((unsigned  char *)a)[2] << 16	& 0xFF0000) | \
105 				(((unsigned  char *)a)[3] << 24	& 0xFF000000)))
106 #endif
107 
108 EXPORT	BOOL	is_auname	__PR((const char *name));
109 EXPORT	off_t	ausize		__PR((int f));
110 EXPORT	BOOL	is_wavname	__PR((const char *name));
111 EXPORT	off_t	wavsize		__PR((int f));
112 
113 EXPORT	BOOL
is_auname(name)114 is_auname(name)
115 	const	char	*name;
116 {
117 	const	char	*p;
118 
119 	if ((p = strrchr(name, '.')) == NULL)
120 		return (FALSE);
121 	return (streql(p, ".au"));
122 }
123 
124 /*
125  * Read Sun audio header, leave file seek pointer past auheader.
126  */
127 EXPORT off_t
ausize(f)128 ausize(f)
129 	int	f;
130 {
131 	sun_au_t	hdr;
132 	struct stat	sb;
133 	mode_t		mode;
134 	off_t		size;
135 	Int32_t		val;
136 	long		ret = AU_BAD_HEADER;
137 
138 	/*
139 	 * First check if a bad guy tries to call ausize()
140 	 * with an unappropriate file descriptor.
141 	 * return -1 in this case.
142 	 */
143 	if (isatty(f))
144 		return (-1L);
145 	if (fstat(f, &sb) < 0)
146 		return (-1L);
147 	mode = sb.st_mode & S_IFMT;
148 	if (!S_ISREG(mode) && !S_ISBLK(mode) && !S_ISCHR(mode))
149 		return (-1L);
150 	lseek(f, (off_t)0L, SEEK_SET);
151 
152 	if (read(f, &hdr, sizeof (hdr)) != sizeof (hdr))
153 		goto err;
154 
155 	if (strncmp((char *)hdr.magic, SUN_AU_MAGIC, 4) != 0)
156 		goto err;
157 
158 	ret = AU_BAD_CODING;
159 
160 	val = a_to_u_4_byte(hdr.encoding);
161 	if (val != SUN_AU_LINEAR16)
162 		goto err;
163 
164 	val = a_to_u_4_byte(hdr.channels);
165 	if (val != 2)
166 		goto err;
167 
168 	val = a_to_u_4_byte(hdr.sample_rate);
169 	if (val != 44100)
170 		goto err;
171 
172 	size = (off_t)a_to_u_4_byte(hdr.hdr_size);
173 	if (size < (off_t)sizeof (hdr) || size > 512)
174 		goto err;
175 	lseek(f, size, SEEK_SET);
176 
177 	/*
178 	 * Most .au files don't seem to honor the data_size field,
179 	 * so we use the whole file size without the header.
180 	 */
181 	size = sb.st_size - size;
182 	return (size);
183 
184 err:
185 	lseek(f, (off_t)0L, SEEK_SET);
186 	return ((off_t)ret);
187 }
188 
189 EXPORT	BOOL
is_wavname(name)190 is_wavname(name)
191 	const	char	*name;
192 {
193 	const	char	*p;
194 
195 	if ((p = strrchr(name, '.')) == NULL)
196 		return (FALSE);
197 	return (streql(p, ".wav") || streql(p, ".WAV"));
198 }
199 
200 /*
201  * Read WAV header, leave file seek pointer past WAV header.
202  */
203 EXPORT off_t
wavsize(f)204 wavsize(f)
205 	int	f;
206 {
207 	chunk_t		chunk;
208 	riff_chunk	riff;
209 	fmt_chunk	fmt;
210 	struct stat	sb;
211 	off_t		cursor;
212 	BOOL		gotFormat;
213 	mode_t		mode;
214 	off_t		size;
215 	long		ret = AU_BAD_HEADER;
216 
217 	/*
218 	 * First check if a bad guy tries to call wavsize()
219 	 * with an unappropriate file descriptor.
220 	 * return -1 in this case.
221 	 */
222 
223 	if (isatty(f))
224 		return (-1L);
225 	if (fstat(f, &sb) < 0)
226 		return (-1L);
227 	mode = sb.st_mode & S_IFMT;
228 	if (!S_ISREG(mode) && !S_ISBLK(mode) && !S_ISCHR(mode))
229 		return (-1L);
230 	lseek(f, (off_t)0L, SEEK_SET);
231 
232 	cursor = (off_t)0;
233 	gotFormat = FALSE;
234 
235 	for (;;) {
236 		if (read(f, &chunk, sizeof (chunk)) != sizeof (chunk))
237 			goto err;
238 		size = (off_t)le_a_to_u_long(chunk.cksize);
239 
240 		if (strncmp((char *)chunk.ckid, WAV_RIFF_MAGIC, 4) == 0) {
241 			/*
242 			 * We found (first) RIFF header. Check if a WAVE
243 			 * magic follows. Set up size to be able to skip
244 			 * past this header.
245 			 */
246 			if (read(f, &riff, sizeof (riff)) != sizeof (riff))
247 				goto err;
248 			if (strncmp((char *)riff.wave, WAV_WAVE_MAGIC, 4) != 0)
249 				goto err;
250 			size = (off_t)sizeof (riff);
251 
252 		} else if (strncmp((char *)chunk.ckid, WAV_FMT_MAGIC, 4) == 0) {
253 			/*
254 			 * We found WAVE "fmt " header. Check size (if it is
255 			 * valid for a WAVE file) and coding whether it is
256 			 * useable for a CD.
257 			 */
258 			if (size < (off_t)sizeof (fmt)) goto err;
259 			if (sizeof (fmt) != read(f, &fmt, sizeof (fmt))) goto err;
260 			if (le_a_to_u_short(fmt.channels) != 2 ||
261 			    le_a_to_u_long(fmt.sample_rate) != 44100 ||
262 			    le_a_to_u_short(fmt.bits_per_sample) != 16) {
263 				ret = AU_BAD_CODING;
264 				goto err;
265 			}
266 			gotFormat = TRUE;
267 
268 		} else if (strncmp((char *)chunk.ckid, WAV_DATA_MAGIC, 4) == 0) {
269 			/*
270 			 * We found WAVE "data" header. This contains the
271 			 * size value of the audio part.
272 			 */
273 			if (!gotFormat) {
274 				ret = AU_BAD_CODING;
275 				goto err;
276 			}
277 			if ((cursor + size + sizeof (chunk)) > sb.st_size)
278 				size = sb.st_size - (cursor  + sizeof (chunk));
279 			return (size);
280 		}
281 		cursor += size + sizeof (chunk);
282 		lseek(f, cursor, SEEK_SET);	/* Skip over current chunk */
283 	}
284 err:
285 	lseek(f, (off_t)0L, SEEK_SET);
286 	return (ret);
287 }
288