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