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