1 /*
2  *
3  *  ao_wav.c
4  *
5  *      Original Copyright (C) Aaron Holtzman - May 1999
6  *      Modifications Copyright (C) Stan Seibert - July 2000, July 2001
7  *                    Copyright (C) Monty - January 2010
8  *
9  *  This file is part of libao, a cross-platform audio output library.  See
10  *  README for a history of this source code.
11  *
12  *  libao is free software; you can redistribute it and/or modify
13  *  it under the terms of the GNU General Public License as published by
14  *  the Free Software Foundation; either version 2, or (at your option)
15  *  any later version.
16  *
17  *  libao is distributed in the hope that it will be useful,
18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *  GNU General Public License for more details.
21  *
22  *  You should have received a copy of the GNU General Public License
23  *  along with GNU Make; see the file COPYING.  If not, write to
24  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
25  *
26  ********************************************************************
27 
28  last mod: $Id: ao_wav.c 18200 2012-02-14 00:44:01Z ph3-der-loewe $
29 
30  ********************************************************************/
31 
32 
33 #include <stdio.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <signal.h>
37 #include <ao/ao.h>
38 
39 #define WAVE_FORMAT_PCM         0x0001
40 #define FORMAT_MULAW            0x0101
41 #define IBM_FORMAT_ALAW         0x0102
42 #define IBM_FORMAT_ADPCM        0x0103
43 #define WAVE_FORMAT_EXTENSIBLE  0xfffe
44 
45 #define WAV_HEADER_LEN 68
46 
47 #define WRITE_U32(buf, x) *(buf)     = (unsigned char)(x&0xff);\
48 						  *((buf)+1) = (unsigned char)((x>>8)&0xff);\
49 						  *((buf)+2) = (unsigned char)((x>>16)&0xff);\
50 						  *((buf)+3) = (unsigned char)((x>>24)&0xff);
51 
52 #define WRITE_U16(buf, x) *(buf)     = (unsigned char)(x&0xff);\
53 						  *((buf)+1) = (unsigned char)((x>>8)&0xff);
54 
55 #define DEFAULT_SWAP_BUFFER_SIZE 2048
56 
57 struct riff_struct {
58 	unsigned char id[4];   /* RIFF */
59 	unsigned int len;
60 	unsigned char wave_id[4]; /* WAVE */
61 };
62 
63 
64 struct chunk_struct
65 {
66 	unsigned char id[4];
67 	unsigned int len;
68 };
69 
70 struct common_struct
71 {
72 	unsigned short wFormatTag;
73 	unsigned short wChannels;
74 	unsigned int dwSamplesPerSec;
75 	unsigned int dwAvgBytesPerSec;
76 	unsigned short wBlockAlign;
77 	unsigned short wBitsPerSample;
78         unsigned short cbSize;
79 	unsigned short wValidBitsPerSample;
80         unsigned int   dwChannelMask;
81         unsigned short subFormat;
82 };
83 
84 struct wave_header
85 {
86 	struct riff_struct   riff;
87 	struct chunk_struct  format;
88 	struct common_struct common;
89 	struct chunk_struct  data;
90 };
91 
92 
93 static char *ao_wav_options[] = {"matrix","verbose","quiet","debug"};
94 static ao_info ao_wav_info =
95 {
96 	AO_TYPE_FILE,
97 	"WAV file output",
98 	"wav",
99 	"Aaron Holtzman <aholtzma@ess.engr.uvic.ca>",
100 	"Sends output to a .wav file",
101 	AO_FMT_LITTLE,
102 	0,
103 	ao_wav_options,
104         sizeof(ao_wav_options)/sizeof(*ao_wav_options)
105 };
106 
107 typedef struct ao_wav_internal
108 {
109 	struct wave_header wave;
110 } ao_wav_internal;
111 
112 
ao_wav_test(void)113 static int ao_wav_test(void)
114 {
115 	return 1; /* File driver always works */
116 }
117 
118 
ao_wav_driver_info(void)119 static ao_info *ao_wav_driver_info(void)
120 {
121 	return &ao_wav_info;
122 }
123 
124 
ao_wav_device_init(ao_device * device)125 static int ao_wav_device_init(ao_device *device)
126 {
127 	ao_wav_internal *internal;
128 
129 	internal = (ao_wav_internal *) malloc(sizeof(ao_wav_internal));
130 
131 	if (internal == NULL)
132 		return 0; /* Could not initialize device memory */
133 
134 	memset(&(internal->wave), 0, sizeof(internal->wave));
135 
136 	device->internal = internal;
137         device->output_matrix = strdup("L,R,C,LFE,BL,BR,CL,CR,BC,SL,SR");
138         device->output_matrix_order = AO_OUTPUT_MATRIX_COLLAPSIBLE;
139 
140 	return 1; /* Memory alloc successful */
141 }
142 
143 
ao_wav_set_option(ao_device * device,const char * key,const char * value)144 static int ao_wav_set_option(ao_device *device, const char *key,
145 			     const char *value)
146 {
147 	return 1; /* No options! */
148 }
149 
ao_wav_open(ao_device * device,ao_sample_format * format)150 static int ao_wav_open(ao_device *device, ao_sample_format *format)
151 {
152 	ao_wav_internal *internal = (ao_wav_internal *) device->internal;
153 	unsigned char buf[WAV_HEADER_LEN];
154 	int size = 0x7fffffff; /* Use a bogus size initially */
155 
156 	/* Store information */
157 	internal->wave.common.wChannels = device->output_channels;
158 	internal->wave.common.wBitsPerSample = ((format->bits+7)>>3)<<3;
159 	internal->wave.common.wValidBitsPerSample = format->bits;
160 	internal->wave.common.dwSamplesPerSec = format->rate;
161 
162 	memset(buf, 0, WAV_HEADER_LEN);
163 
164 	/* Fill out our wav-header with some information. */
165 	strncpy(internal->wave.riff.id, "RIFF",4);
166 	internal->wave.riff.len = size - 8;
167 	strncpy(internal->wave.riff.wave_id, "WAVE",4);
168 
169 	strncpy(internal->wave.format.id, "fmt ",4);
170 	internal->wave.format.len = 40;
171 
172 	internal->wave.common.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
173 	internal->wave.common.dwAvgBytesPerSec =
174 		internal->wave.common.wChannels *
175 		internal->wave.common.dwSamplesPerSec *
176 		(internal->wave.common.wBitsPerSample >> 3);
177 
178 	internal->wave.common.wBlockAlign =
179 		internal->wave.common.wChannels *
180 		(internal->wave.common.wBitsPerSample >> 3);
181 	internal->wave.common.cbSize = 22;
182 	internal->wave.common.subFormat = WAVE_FORMAT_PCM;
183         internal->wave.common.dwChannelMask=device->output_mask;
184 
185 	strncpy(internal->wave.data.id, "data",4);
186 
187 	internal->wave.data.len = size - WAV_HEADER_LEN;
188 
189 	strncpy(buf, internal->wave.riff.id, 4);
190 	WRITE_U32(buf+4, internal->wave.riff.len);
191 	strncpy(buf+8, internal->wave.riff.wave_id, 4);
192 	strncpy(buf+12, internal->wave.format.id,4);
193 	WRITE_U32(buf+16, internal->wave.format.len);
194 	WRITE_U16(buf+20, internal->wave.common.wFormatTag);
195 	WRITE_U16(buf+22, internal->wave.common.wChannels);
196 	WRITE_U32(buf+24, internal->wave.common.dwSamplesPerSec);
197 	WRITE_U32(buf+28, internal->wave.common.dwAvgBytesPerSec);
198 	WRITE_U16(buf+32, internal->wave.common.wBlockAlign);
199 	WRITE_U16(buf+34, internal->wave.common.wBitsPerSample);
200 	WRITE_U16(buf+36, internal->wave.common.cbSize);
201 	WRITE_U16(buf+38, internal->wave.common.wValidBitsPerSample);
202 	WRITE_U32(buf+40, internal->wave.common.dwChannelMask);
203 	WRITE_U16(buf+44, internal->wave.common.subFormat);
204         memcpy(buf+46,"\x00\x00\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71",14);
205 	strncpy(buf+60, internal->wave.data.id, 4);
206 	WRITE_U32(buf+64, internal->wave.data.len);
207 
208 	if (fwrite(buf, sizeof(char), WAV_HEADER_LEN, device->file)
209 	    != WAV_HEADER_LEN) {
210 		return 0; /* Could not write wav header */
211 	}
212 
213 	device->driver_byte_format = AO_FMT_LITTLE;
214 
215 	return 1;
216 }
217 
218 
219 /*
220  * play the sample to the already opened file descriptor
221  */
ao_wav_play(ao_device * device,const char * output_samples,uint_32 num_bytes)222 static int ao_wav_play(ao_device *device, const char *output_samples,
223 			uint_32 num_bytes)
224 {
225 	if (fwrite(output_samples, sizeof(char), num_bytes,
226 		   device->file) < num_bytes)
227 		return 0;
228 	else
229 		return 1;
230 
231 }
232 
ao_wav_close(ao_device * device)233 static int ao_wav_close(ao_device *device)
234 {
235 	ao_wav_internal *internal = (ao_wav_internal *) device->internal;
236 	unsigned char buf[4];  /* For holding length values */
237 
238 	long size;
239 
240 	/* Find how long our file is in total, including header */
241 	size = ftell(device->file);
242 
243 	if (size < 0) {
244 		return 0;  /* Wav header corrupt */
245 	}
246 
247 	/* Go back and set correct length info */
248 
249 	internal->wave.riff.len = size - 8;
250 	internal->wave.data.len = size - WAV_HEADER_LEN;
251 
252 	/* Rewind to riff len and write it */
253 	if (fseek(device->file, 4, SEEK_SET) < 0)
254 		return 0; /* Wav header corrupt */
255 
256 	WRITE_U32(buf, internal->wave.riff.len);
257 	if (fwrite(buf, sizeof(char), 4, device->file) < 4)
258 	  return 0; /* Wav header corrupt */
259 
260 
261 	/* Rewind to data len and write it */
262 	if (fseek(device->file, 64, SEEK_SET) < 0)
263 		return 0; /* Wav header corrupt */
264 
265 	WRITE_U32(buf, internal->wave.data.len);
266 	if (fwrite(buf, sizeof(char), 4, device->file) < 4)
267 	  return 0; /* Wav header corrupt */
268 
269 
270 	return 1; /* Wav header correct */
271 }
272 
ao_wav_device_clear(ao_device * device)273 static void ao_wav_device_clear(ao_device *device)
274 {
275 	ao_wav_internal *internal = (ao_wav_internal *) device->internal;
276 
277 	free(internal);
278         device->internal=NULL;
279 }
280 
ao_wav_file_extension(void)281 const char *ao_wav_file_extension(void)
282 {
283 	return "wav";
284 }
285 
286 
287 ao_functions ao_wav = {
288 	ao_wav_test,
289 	ao_wav_driver_info,
290 	ao_wav_device_init,
291 	ao_wav_set_option,
292 	ao_wav_open,
293 	ao_wav_play,
294 	ao_wav_close,
295 	ao_wav_device_clear,
296 	ao_wav_file_extension
297 };
298