1 /*
2 TiMidity++ -- MIDI to WAVE converter and player
3 Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
4 Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; nclude <sys/stat.h>if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 au_a.c
21
22 Functions to output Sun audio file (*.au).
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif /* HAVE_CONFIG_H */
28 #include <stdio.h>
29
30 #ifdef __POCC__
31 #include <sys/types.h>
32 #endif //for off_t
33
34 #ifdef __W32__
35 #include <stdlib.h>
36 #include <io.h>
37 #endif
38
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif /* HAVE_UNISTD_H */
42
43 #ifdef STDC_HEADERS
44 #include <string.h>
45 #include <stdlib.h>
46 #include <ctype.h>
47 #elif HAVE_STRINGS_H
48 #include <strings.h>
49 #endif
50 #ifdef HAVE_FCNTL_H
51 #include <fcntl.h>
52 #endif
53
54 #ifdef __FreeBSD__
55 #include <stdio.h>
56 #endif
57
58 #include "timidity.h"
59 #include "common.h"
60 #include "output.h"
61 #include "controls.h"
62 #include "instrum.h"
63 #include "playmidi.h"
64 #include "readmidi.h"
65
66 static int open_output(void); /* 0=success, 1=warning, -1=fatal error */
67 static void close_output(void);
68 static int output_data(char *buf, int32 bytes);
69 static int acntl(int request, void *arg);
70 static int write_u32(uint32 value);
71
72 /* export the playback mode */
73
74 #define dpm au_play_mode
75
76 PlayMode dpm = {
77 8000, PE_MONO|PE_ULAW, PF_PCM_STREAM|PF_FILE_OUTPUT,
78 -1,
79 {0,0,0,0,0},
80 "Sun audio file", 'u',
81 NULL,
82 open_output,
83 close_output,
84 output_data,
85 acntl
86 };
87
88 /*************************************************************************/
89
90 #define UPDATE_HEADER_STEP (128*1024) /* 128KB */
91 static uint32 bytes_output, next_bytes;
92 static int already_warning_lseek;
93
write_u32(uint32 value)94 static int write_u32(uint32 value)
95 {
96 int n;
97 value = BE_LONG(value);
98 if((n = std_write(dpm.fd, (char *)&value, 4)) == -1)
99 {
100 ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: write: %s",
101 dpm.name, strerror(errno));
102 close_output();
103 return -1;
104 }
105 return n;
106 }
107
write_str(const char * s)108 static int write_str(const char *s)
109 {
110 int n;
111 if((n = std_write(dpm.fd, s, strlen(s))) == -1)
112 {
113 ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: write: %s",
114 dpm.name, strerror(errno));
115 close_output();
116 return -1;
117 }
118 return n;
119 }
120
121 /* Sun Audio File Encoding Tags */
122 #define AUDIO_FILE_ENCODING_MULAW_8 1 /* 8-bit ISDN u-law */
123 #define AUDIO_FILE_ENCODING_LINEAR_8 2 /* 8-bit linear PCM */
124 #define AUDIO_FILE_ENCODING_LINEAR_16 3 /* 16-bit linear PCM */
125 #define AUDIO_FILE_ENCODING_LINEAR_24 4 /* 24-bit linear PCM */
126 #define AUDIO_FILE_ENCODING_ALAW_8 27 /* 8-bit ISDN A-law */
127
au_output_open(const char * fname,const char * comment)128 static int au_output_open(const char *fname, const char *comment)
129 {
130 int t;
131
132 if(strcmp(fname, "-") == 0) {
133 dpm.fd = 1; /* data to stdout */
134 if(comment == NULL)
135 comment = "(stdout)";
136 } else {
137 /* Open the audio file */
138 dpm.fd = open(fname, FILE_OUTPUT_MODE);
139 if(dpm.fd < 0) {
140 ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
141 fname, strerror(errno));
142 return -1;
143 }
144 if(comment == NULL)
145 comment = fname;
146 }
147
148 /* magic */
149 if(write_str(".snd") == -1) return -1;
150
151 /* header size */
152 if(write_u32((uint32)(24 + strlen(comment))) == -1) return -1;
153
154 /* sample data size */
155 if(write_u32((uint32)0xffffffff) == -1) return -1;
156
157 /* audio file encoding */
158 if(dpm.encoding & PE_ULAW)
159 t = AUDIO_FILE_ENCODING_MULAW_8;
160 else if(dpm.encoding & PE_ALAW)
161 t = AUDIO_FILE_ENCODING_ALAW_8;
162 else if(dpm.encoding & PE_24BIT)
163 t = AUDIO_FILE_ENCODING_LINEAR_24;
164 else if(dpm.encoding & PE_16BIT)
165 t = AUDIO_FILE_ENCODING_LINEAR_16;
166 else
167 t = AUDIO_FILE_ENCODING_LINEAR_8;
168 if(write_u32((uint32)t) == -1) return -1;
169
170 /* sample rate */
171 if(write_u32((uint32)dpm.rate) == -1) return -1;
172
173 /* number of channels */
174 if(dpm.encoding & PE_MONO) {
175 if(write_u32((uint32)1) == -1) return -1;
176 } else {
177 if(write_u32((uint32)2) == -1) return -1;
178 }
179
180 /* comment */
181 if(write_str(comment) == -1) return -1;
182
183 bytes_output = 0;
184 next_bytes = bytes_output + UPDATE_HEADER_STEP;
185 already_warning_lseek = 0;
186
187 return 0;
188 }
189
auto_au_output_open(const char * input_filename)190 static int auto_au_output_open(const char *input_filename)
191 {
192 char *output_filename = (char *)safe_malloc(strlen(input_filename) + 5);
193 char *ext, *p;
194
195 strcpy(output_filename, input_filename);
196 if((ext = strrchr(output_filename, '.')) == NULL)
197 ext = output_filename + strlen(output_filename);
198 else {
199 /* strip ".gz" */
200 if(strcasecmp(ext, ".gz") == 0) {
201 *ext = '\0';
202 if((ext = strrchr(output_filename, '.')) == NULL)
203 ext = output_filename + strlen(output_filename);
204 }
205 }
206
207 /* replace '.' and '#' before ext */
208 for(p = output_filename; p < ext; p++)
209 if(*p == '.' || *p == '#')
210 *p = '_';
211
212 if(*ext && isupper(*(ext + 1)))
213 strcpy(ext, ".AU");
214 else
215 strcpy(ext, ".au");
216 if(au_output_open(output_filename, input_filename) == -1) {
217 free(output_filename);
218 return -1;
219 }
220 if(dpm.name != NULL)
221 free(dpm.name);
222 dpm.name = output_filename;
223 ctl->cmsg(CMSG_INFO, VERB_NORMAL, "Output %s", dpm.name);
224 return 0;
225 }
226
open_output(void)227 static int open_output(void)
228 {
229 int include_enc, exclude_enc;
230
231 include_enc = exclude_enc = 0;
232 if(dpm.encoding & (PE_16BIT | PE_24BIT))
233 {
234 #ifdef LITTLE_ENDIAN
235 include_enc = PE_BYTESWAP;
236 #else
237 exclude_enc = PE_BYTESWAP;
238 #endif /* LITTLE_ENDIAN */
239 include_enc |= PE_SIGNED;
240 }
241 else if(!(dpm.encoding & (PE_ULAW|PE_ALAW)))
242 {
243 /* is 8 bit au unsigned ? */
244 exclude_enc = PE_SIGNED;
245 }
246
247 dpm.encoding = validate_encoding(dpm.encoding, include_enc, exclude_enc);
248
249 if(dpm.name == NULL) {
250 if (!current_file_info || !current_file_info->filename)
251 return -1;
252 dpm.flag |= PF_AUTO_SPLIT_FILE;
253 } else {
254 dpm.flag &= ~PF_AUTO_SPLIT_FILE;
255 if(au_output_open(dpm.name, NULL) == -1)
256 return -1;
257 }
258
259 return 0;
260 }
261
262
update_header(void)263 static int update_header(void)
264 {
265 off_t save_point;
266
267 if(already_warning_lseek)
268 return 0;
269
270 save_point = lseek(dpm.fd, 0, SEEK_CUR);
271 if(save_point == -1 || lseek(dpm.fd, 8, SEEK_SET) == -1)
272 {
273 ctl->cmsg(CMSG_WARNING, VERB_VERBOSE,
274 "Warning: %s: %s: Can't make valid header",
275 dpm.name, strerror(errno));
276 already_warning_lseek = 1;
277 return 0;
278 }
279
280 if(write_u32(bytes_output) == -1) return -1;
281 lseek(dpm.fd, save_point, SEEK_SET);
282 ctl->cmsg(CMSG_INFO, VERB_DEBUG,
283 "%s: Update au header (size=%d)", dpm.name, bytes_output);
284 return 0;
285 }
286
287
output_data(char * buf,int32 bytes)288 static int output_data(char *buf, int32 bytes)
289 {
290 int n;
291
292 if(dpm.fd == -1)
293 return -1;
294
295 while(((n = std_write(dpm.fd, buf, bytes)) == -1) && errno == EINTR)
296 ;
297 if(n == -1)
298 {
299 ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
300 dpm.name, strerror(errno));
301 return -1;
302 }
303
304 bytes_output += bytes;
305
306 #if UPDATE_HEADER_STEP > 0
307 if(bytes_output >= next_bytes)
308 {
309 if(update_header() == -1) return -1;
310 next_bytes = bytes_output + UPDATE_HEADER_STEP;
311 }
312 #endif /* UPDATE_HEADER_STEP */
313 return n;
314 }
315
close_output(void)316 static void close_output(void)
317 {
318 if(dpm.fd != 1 && /* We don't close stdout */
319 dpm.fd != -1) {
320 update_header();
321 close(dpm.fd);
322 dpm.fd = -1;
323 }
324 }
325
acntl(int request,void * arg)326 static int acntl(int request, void *arg)
327 {
328 switch(request) {
329 case PM_REQ_PLAY_START:
330 if(dpm.flag & PF_AUTO_SPLIT_FILE)
331 return auto_au_output_open(current_file_info->filename);
332 return 0;
333 case PM_REQ_PLAY_END:
334 if(dpm.flag & PF_AUTO_SPLIT_FILE)
335 close_output();
336 return 0;
337 case PM_REQ_DISCARD:
338 return 0;
339 }
340 return -1;
341 }
342