1 /*
2  *
3  *  ao_au.c
4  *
5  *      Copyright (C) Wil Mahan - May 2001
6  *
7  *  This file is part of libao, a cross-platform audio output library.  See
8  *  README for a history of this source code.
9  *
10  *  libao is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2, or (at your option)
13  *  any later version.
14  *
15  *  libao is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with GNU Make; see the file COPYING.  If not, write to
22  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  ********************************************************************
25 
26  last mod: $Id: ao_au.c 18200 2012-02-14 00:44:01Z ph3-der-loewe $
27 
28  ********************************************************************/
29 
30 #include <stdio.h>
31 #include <string.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #ifndef _MSC_VER
37 # include <unistd.h>
38 #endif
39 #include <ao/ao.h>
40 #include <ao/plugin.h>
41 
42 #define AUDIO_FILE_MAGIC ((uint_32)0x2e736e64)  /* ".snd" */
43 
44 #define AUDIO_UNKNOWN_SIZE (~0) /* (unsigned) -1 */
45 
46 /* Format codes (not comprehensive) */
47 #define AUDIO_FILE_ENCODING_LINEAR_8 (2) /* 8-bit linear PCM */
48 #define AUDIO_FILE_ENCODING_LINEAR_16 (3) /* 16-bit linear PCM */
49 
50 #define AU_HEADER_LEN (28)
51 
52 #define DEFAULT_SWAP_BUFFER_SIZE 2048
53 
54 /* Write a uint_32 in big-endian order. */
55 #define WRITE_U32(buf, x) \
56 	*(buf) = (unsigned char)(((x)>>24)&0xff);\
57 	*((buf)+1) = (unsigned char)(((x)>>16)&0xff);\
58 	*((buf)+2) = (unsigned char)(((x)>>8)&0xff);\
59 	*((buf)+3) = (unsigned char)((x)&0xff);
60 
61 typedef struct Audio_filehdr {
62 	uint_32 magic; /* magic number */
63 	uint_32 hdr_size; /* offset of the data */
64 	uint_32 data_size; /* length of data (optional) */
65 	uint_32 encoding; /* data format code */
66 	uint_32 sample_rate; /* samples per second */
67 	uint_32 channels; /* number of interleaved channels */
68 	char info[4]; /* optional text information */
69 } Audio_filehdr;
70 
71 static char *ao_au_options[] = {"matrix","verbose","quiet","debug"};
72 static ao_info ao_au_info =
73 {
74 	AO_TYPE_FILE,
75 	"AU file output",
76 	"au",
77 	"Wil Mahan <wtm2@duke.edu>",
78 	"Sends output to a .au file",
79 	AO_FMT_BIG,
80 	0,
81 	ao_au_options,
82         sizeof(ao_au_options)/sizeof(*ao_au_options)
83 };
84 
85 typedef struct ao_au_internal
86 {
87 	Audio_filehdr au;
88 } ao_au_internal;
89 
90 
ao_au_test(void)91 static int ao_au_test(void)
92 {
93 	return 1; /* File driver always works */
94 }
95 
96 
ao_au_driver_info(void)97 static ao_info *ao_au_driver_info(void)
98 {
99 	return &ao_au_info;
100 }
101 
102 
ao_au_device_init(ao_device * device)103 static int ao_au_device_init(ao_device *device)
104 {
105 	ao_au_internal *internal;
106 
107 	internal = (ao_au_internal *) malloc(sizeof(ao_au_internal));
108 
109 	if (internal == NULL)
110 		return 0; /* Could not initialize device memory */
111 
112 	memset(&(internal->au), 0, sizeof(internal->au));
113 
114 	device->internal = internal;
115         device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED;
116 
117 	return 1; /* Memory alloc successful */
118 }
119 
120 
ao_au_set_option(ao_device * device,const char * key,const char * value)121 static int ao_au_set_option(ao_device *device, const char *key,
122 			    const char *value)
123 {
124 	return 1; /* No options! */
125 }
126 
127 
ao_au_open(ao_device * device,ao_sample_format * format)128 static int ao_au_open(ao_device *device, ao_sample_format *format)
129 {
130 	ao_au_internal *internal = (ao_au_internal *) device->internal;
131 	unsigned char buf[AU_HEADER_LEN];
132 
133 	/* The AU format is big-endian */
134 	device->driver_byte_format = AO_FMT_BIG;
135 
136 	/* Fill out the header */
137 	internal->au.magic = AUDIO_FILE_MAGIC;
138 	internal->au.channels = device->output_channels;
139 	if (format->bits == 8)
140 		internal->au.encoding = AUDIO_FILE_ENCODING_LINEAR_8;
141 	else if (format->bits == 16)
142 		internal->au.encoding = AUDIO_FILE_ENCODING_LINEAR_16;
143 	else {
144 		/* Only 8 and 16 bits are supported at present. */
145 		return 0;
146 	}
147 	internal->au.sample_rate = format->rate;
148 	internal->au.hdr_size = AU_HEADER_LEN;
149 
150 	/* From the AU specification:  "When audio files are passed
151 	 * through pipes, the 'data_size' field may not be known in
152 	 * advance.  In such cases, the 'data_size' should be set
153 	 * to AUDIO_UNKNOWN_SIZE."
154 	 */
155 	internal->au.data_size = AUDIO_UNKNOWN_SIZE;
156 	/* strncpy(state->au.info, "OGG ", 4); */
157 
158 	/* Write the header in big-endian order */
159 	WRITE_U32(buf, internal->au.magic);
160 	WRITE_U32(buf + 4, internal->au.hdr_size);
161 	WRITE_U32(buf + 8, internal->au.data_size);
162 	WRITE_U32(buf + 12, internal->au.encoding);
163 	WRITE_U32(buf + 16, internal->au.sample_rate);
164 	WRITE_U32(buf + 20, internal->au.channels);
165 	strncpy (buf + 24, internal->au.info, 4);
166 
167 	if (fwrite(buf, sizeof(char), AU_HEADER_LEN, device->file)
168 	    != AU_HEADER_LEN) {
169 		return 0; /* Error writing header */
170 	}
171 
172         if(!device->inter_matrix){
173           /* set up matrix such that users are warned about > stereo playback */
174           if(device->output_channels<=2)
175             device->inter_matrix=strdup("L,R");
176           //else no matrix, which results in a warning
177         }
178 
179 
180 	return 1;
181 }
182 
183 
184 /*
185  * play the sample to the already opened file descriptor
186  */
ao_au_play(ao_device * device,const char * output_samples,uint_32 num_bytes)187 static int ao_au_play(ao_device *device, const char *output_samples,
188 		       uint_32 num_bytes)
189 {
190 	if (fwrite(output_samples, sizeof(char), num_bytes,
191 		   device->file) < num_bytes)
192 		return 0;
193 	else
194 		return 1;
195 }
196 
ao_au_close(ao_device * device)197 static int ao_au_close(ao_device *device)
198 {
199 	ao_au_internal *internal = (ao_au_internal *) device->internal;
200 
201         off_t size;
202 	unsigned char buf[4];
203 
204 	/* Try to find the total file length, including header */
205 	size = ftell(device->file);
206 
207 	/* It's not a problem if the lseek() fails; the AU
208 	 * format does not require a file length.  This is
209 	 * useful for writing to non-seekable files (e.g.
210 	 * pipes).
211 	 */
212 	if (size > 0) {
213 		internal->au.data_size = size - AU_HEADER_LEN;
214 
215 		/* Rewind the file */
216 		if (fseek(device->file, 8 /* offset of data_size */,
217 					SEEK_SET) < 0)
218 		{
219 			return 1; /* Seek failed; that's okay  */
220 		}
221 
222 		/* Fill in the file length */
223 		WRITE_U32 (buf, internal->au.data_size);
224 		if (fwrite(buf, sizeof(char), 4, device->file) < 4) {
225 			return 1; /* Header write failed; that's okay */
226 		}
227 	}
228 
229 	return 1;
230 }
231 
232 
ao_au_device_clear(ao_device * device)233 static void ao_au_device_clear(ao_device *device)
234 {
235 	ao_au_internal *internal = (ao_au_internal *) device->internal;
236 
237 	free(internal);
238         device->internal=NULL;
239 }
240 
ao_au_file_extension(void)241 const char *ao_au_file_extension(void)
242 {
243 	return "au";
244 }
245 
246 
247 ao_functions ao_au = {
248 	ao_au_test,
249 	ao_au_driver_info,
250 	ao_au_device_init,
251 	ao_au_set_option,
252 	ao_au_open,
253 	ao_au_play,
254 	ao_au_close,
255 	ao_au_device_clear,
256 	ao_au_file_extension
257 };
258