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_snd.c,v 1.15 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 # define AUDIO_FILE_MAGIC	(('.' << 24) | ('s' << 16) | ('n' << 8) | 'd')
35 # define AUDIO_UNKNOWN_SIZE	(~0)		/* (unsigned) -1 */
36 # define AUDIO_ENCODING_MULAW_8	1		/* 8-bit ISDN mu-law */
37 
38 static FILE *outfile;
39 
40 static unsigned long data_len;
41 
42 static unsigned int config_channels;
43 static unsigned int config_speed;
44 static unsigned int config_precision;
45 
46 /*
47  * NAME:	int32()
48  * DESCRIPTION:	store 32-bit big-endian integer
49  */
50 static
int32(unsigned char * ptr,unsigned long num)51 void int32(unsigned char *ptr, unsigned long num)
52 {
53   ptr[0] = num >> 24;
54   ptr[1] = num >> 16;
55   ptr[2] = num >>  8;
56   ptr[3] = num >>  0;
57 }
58 
59 static
init(struct audio_init * init)60 int init(struct audio_init *init)
61 {
62   if (init->path && strcmp(init->path, "-") != 0) {
63     outfile = fopen(init->path, "wb");
64     if (outfile == 0) {
65       audio_error = ":";
66       return -1;
67     }
68   }
69   else
70     outfile = stdout;
71 
72   data_len = 0;
73 
74   config_channels  = 0;
75   config_speed     = 0;
76   config_precision = 0;
77 
78   return 0;
79 }
80 
81 static
config(struct audio_config * config)82 int config(struct audio_config *config)
83 {
84   unsigned char header[24];
85 
86   if (config_precision) {
87     /* it's not possible to change the format once set */
88 
89     config->channels  = config_channels;
90     config->speed     = config_speed;
91     config->precision = config_precision;
92 
93     return 0;
94   }
95 
96   /* Sun/NeXT audio file header */
97 
98   int32(&header[0],  AUDIO_FILE_MAGIC);		/* magic */
99   int32(&header[4],  sizeof(header));		/* hdr_size */
100   int32(&header[8],  AUDIO_UNKNOWN_SIZE);	/* data_size */
101   int32(&header[12], AUDIO_ENCODING_MULAW_8);	/* encoding */
102   int32(&header[16], config->speed);		/* sample_rate */
103   int32(&header[20], config->channels);		/* channels */
104 
105   if (fwrite(header, sizeof(header), 1, outfile) != 1) {
106     audio_error = ":fwrite";
107     return -1;
108   }
109 
110   data_len = 0;
111 
112   /* require 8-bit depth for now */
113 
114   config->precision = 8;
115 
116   config_channels  = config->channels;
117   config_speed     = config->speed;
118   config_precision = config->precision;
119 
120   return 0;
121 }
122 
123 static
play(struct audio_play * play)124 int play(struct audio_play *play)
125 {
126   unsigned char data[MAX_NSAMPLES * 2];
127   unsigned int len;
128 
129   len = audio_pcm_mulaw(data, play->nsamples,
130 			play->samples[0], play->samples[1],
131 			play->mode, play->stats);
132 
133   if (fwrite(data, play->samples[1] ? 2 : 1,
134 	     play->nsamples, outfile) != play->nsamples) {
135     audio_error = ":fwrite";
136     return -1;
137   }
138 
139   data_len += len;
140 
141   return 0;
142 }
143 
144 static
stop(struct audio_stop * stop)145 int stop(struct audio_stop *stop)
146 {
147   return 0;
148 }
149 
150 static
patch_int32(long address,unsigned long num)151 int patch_int32(long address, unsigned long num)
152 {
153   unsigned char dword[4];
154 
155   if (fseek(outfile, address, SEEK_SET) == -1) {
156     audio_error = ":fseek";
157     return -1;
158   }
159 
160   int32(dword, num);
161 
162   if (fwrite(dword, sizeof(dword), 1, outfile) != 1) {
163     audio_error = ":fwrite";
164     return -1;
165   }
166 
167   if (fseek(outfile, 0, SEEK_END) == -1) {
168     audio_error = ":fseek";
169     return -1;
170   }
171 
172   return 0;
173 }
174 
175 static
finish(struct audio_finish * finish)176 int finish(struct audio_finish *finish)
177 {
178   int result = 0;
179 
180   if (config_precision == 0) {
181     struct audio_config dummy;
182 
183     /* write empty chunks */
184 
185     dummy.command   = AUDIO_COMMAND_CONFIG;
186     dummy.channels  = 2;
187     dummy.speed     = 44100;
188     dummy.precision = 0;
189 
190     result = config(&dummy);
191   }
192 
193   if (result == 0)
194     patch_int32(8, data_len);
195 
196   if (outfile != stdout &&
197       fclose(outfile) == EOF &&
198       result == 0) {
199     audio_error = ":fclose";
200     result = -1;
201   }
202 
203   return result;
204 }
205 
audio_snd(union audio_control * control)206 int audio_snd(union audio_control *control)
207 {
208   audio_error = 0;
209 
210   switch (control->command) {
211   case AUDIO_COMMAND_INIT:
212     return init(&control->init);
213 
214   case AUDIO_COMMAND_CONFIG:
215     return config(&control->config);
216 
217   case AUDIO_COMMAND_PLAY:
218     return play(&control->play);
219 
220   case AUDIO_COMMAND_STOP:
221     return stop(&control->stop);
222 
223   case AUDIO_COMMAND_FINISH:
224     return finish(&control->finish);
225   }
226 
227   return 0;
228 }
229