1 /*
2 * madplay - MPEG audio decoder and player
3 * Copyright (C) 2000-2004 Robert Leslie
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * $Id: audio_wave.c,v 1.18 2004/01/23 09:41:31 rob Exp $
20 */
21
22 # ifdef HAVE_CONFIG_H
23 # include "config.h"
24 # endif
25
26 # include "global.h"
27
28 # include <stdio.h>
29 # include <string.h>
30 # include <mad.h>
31
32 # include "audio.h"
33
34 static FILE *outfile;
35 static audio_pcmfunc_t *audio_pcm;
36
37 static unsigned long riff_len, data_len;
38 static long data_chunk;
39
40 static unsigned int config_channels;
41 static unsigned int config_speed;
42 static unsigned int config_precision;
43
44 # define WAVE_FORMAT_PCM 0x0001
45 # define UNKNOWN_LENGTH "\xff\xff\xff\xff"
46
47 static
init(struct audio_init * init)48 int init(struct audio_init *init)
49 {
50 if (init->path && strcmp(init->path, "-") != 0) {
51 outfile = fopen(init->path, "wb");
52 if (outfile == 0) {
53 audio_error = ":";
54 return -1;
55 }
56 }
57 else
58 outfile = stdout;
59
60 /* RIFF header and (WAVE) data type identifier */
61
62 if (fwrite("RIFF" UNKNOWN_LENGTH "WAVE", 4 + 4 + 4, 1, outfile) != 1) {
63 audio_error = ":fwrite";
64 return -1;
65 }
66
67 riff_len = 4;
68 data_chunk = -1;
69
70 config_channels = 0;
71 config_speed = 0;
72 config_precision = 0;
73
74 return 0;
75 }
76
77 /*
78 * NAME: int32()
79 * DESCRIPTION: store 32-bit little-endian integer
80 */
81 static
int32(unsigned char * ptr,unsigned long num)82 void int32(unsigned char *ptr, unsigned long num)
83 {
84 ptr[0] = num >> 0;
85 ptr[1] = num >> 8;
86 ptr[2] = num >> 16;
87 ptr[3] = num >> 24;
88 }
89
90 /*
91 * NAME: int16()
92 * DESCRIPTION: store 16-bit little-endian integer
93 */
94 static
int16(unsigned char * ptr,unsigned int num)95 void int16(unsigned char *ptr, unsigned int num)
96 {
97 ptr[0] = num >> 0;
98 ptr[1] = num >> 8;
99 }
100
101 static
config(struct audio_config * config)102 int config(struct audio_config *config)
103 {
104 unsigned char chunk[8 + 16];
105 unsigned int block_al, bitdepth;
106 unsigned long bytes_ps;
107
108 if (config_precision) {
109 /* it's not possible to change the format once set */
110
111 config->channels = config_channels;
112 config->speed = config_speed;
113 config->precision = config_precision;
114
115 return 0;
116 }
117
118 bitdepth = config->precision;
119 if (bitdepth == 0)
120 bitdepth = 16;
121 else if (bitdepth > 32)
122 bitdepth = 32;
123
124 /* Format chunk */
125
126 block_al = config->channels * ((bitdepth + 7) / 8);
127 bytes_ps = config->speed * block_al;
128
129 memcpy(&chunk[0], "fmt ", 4); /* chunkID */
130 int32(&chunk[4], 16); /* chunkSize */
131
132 int16(&chunk[8], WAVE_FORMAT_PCM); /* wFormatTag */
133 int16(&chunk[10], config->channels); /* wChannels */
134 int32(&chunk[12], config->speed); /* dwSamplesPerSec */
135 int32(&chunk[16], bytes_ps); /* dwAvgBytesPerSec */
136 int16(&chunk[20], block_al); /* wBlockAlign */
137
138 /* PCM-format-specific */
139
140 int16(&chunk[22], bitdepth); /* wBitsPerSample */
141
142 if (fwrite(chunk, 8 + 16, 1, outfile) != 1) {
143 audio_error = ":fwrite";
144 return -1;
145 }
146
147 riff_len += 8 + 16;
148
149 /* Data chunk */
150
151 data_chunk = ftell(outfile);
152
153 if (fwrite("data" UNKNOWN_LENGTH, 8 + 0, 1, outfile) != 1) {
154 audio_error = ":fwrite";
155 return -1;
156 }
157
158 riff_len += 8 + 0;
159 data_len = 0;
160
161 switch (config->precision = bitdepth) {
162 case 1: case 2: case 3: case 4:
163 case 5: case 6: case 7: case 8:
164 audio_pcm = audio_pcm_u8;
165 break;
166
167 case 9: case 10: case 11: case 12:
168 case 13: case 14: case 15: case 16:
169 audio_pcm = audio_pcm_s16le;
170 break;
171
172 case 17: case 18: case 19: case 20:
173 case 21: case 22: case 23: case 24:
174 audio_pcm = audio_pcm_s24le;
175 break;
176
177 case 25: case 26: case 27: case 28:
178 case 29: case 30: case 31: case 32:
179 audio_pcm = audio_pcm_s32le;
180 break;
181 }
182
183 config_channels = config->channels;
184 config_speed = config->speed;
185 config_precision = config->precision;
186
187 return 0;
188 }
189
190 static
play(struct audio_play * play)191 int play(struct audio_play *play)
192 {
193 unsigned char data[MAX_NSAMPLES * 4 * 2];
194 unsigned int len;
195
196 len = audio_pcm(data, play->nsamples, play->samples[0], play->samples[1],
197 play->mode, play->stats);
198
199 if (fwrite(data, len, 1, outfile) != 1) {
200 audio_error = ":fwrite";
201 return -1;
202 }
203
204 data_len += len;
205 riff_len += len;
206
207 return 0;
208 }
209
210 static
stop(struct audio_stop * stop)211 int stop(struct audio_stop *stop)
212 {
213 return 0;
214 }
215
216 static
patch_int32(long address,unsigned long num)217 int patch_int32(long address, unsigned long num)
218 {
219 unsigned char dword[4];
220
221 if (fseek(outfile, address, SEEK_SET) == -1) {
222 audio_error = ":fseek";
223 return -1;
224 }
225
226 int32(dword, num);
227
228 if (fwrite(dword, sizeof(dword), 1, outfile) != 1) {
229 audio_error = ":fwrite";
230 return -1;
231 }
232
233 if (fseek(outfile, 0, SEEK_END) == -1) {
234 audio_error = ":fseek";
235 return -1;
236 }
237
238 return 0;
239 }
240
241 static
finish(struct audio_finish * finish)242 int finish(struct audio_finish *finish)
243 {
244 int result = 0;
245
246 if (config_precision == 0) {
247 struct audio_config dummy;
248
249 /* write empty chunks */
250
251 dummy.command = AUDIO_COMMAND_CONFIG;
252 dummy.channels = 2;
253 dummy.speed = 44100;
254 dummy.precision = 0;
255
256 result = config(&dummy);
257 }
258
259 if (data_len & 1) {
260 if (fputc(0, outfile) == EOF && result == 0) {
261 audio_error = ":fputc";
262 result = -1;
263 }
264
265 ++riff_len;
266 }
267
268 if (result == 0 &&
269 data_chunk != -1 && patch_int32(data_chunk + 4, data_len) == -1)
270 result = -1;
271
272 if (result == 0)
273 patch_int32(4, riff_len);
274
275 if (outfile != stdout &&
276 fclose(outfile) == EOF &&
277 result == 0) {
278 audio_error = ":fclose";
279 result = -1;
280 }
281
282 return result;
283 }
284
audio_wave(union audio_control * control)285 int audio_wave(union audio_control *control)
286 {
287 audio_error = 0;
288
289 switch (control->command) {
290 case AUDIO_COMMAND_INIT:
291 return init(&control->init);
292
293 case AUDIO_COMMAND_CONFIG:
294 return config(&control->config);
295
296 case AUDIO_COMMAND_PLAY:
297 return play(&control->play);
298
299 case AUDIO_COMMAND_STOP:
300 return stop(&control->stop);
301
302 case AUDIO_COMMAND_FINISH:
303 return finish(&control->finish);
304 }
305
306 return 0;
307 }
308