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