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