Recognizes if input is ogg or not.
[xiph.git] / postfish / main.c
blobdf30dee9103770e32f56388be9a01af5784fa38f
1 /*
3 * postfish
4 *
5 * Copyright (C) 2002-2005 Monty
7 * Postfish is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
12 * Postfish is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Postfish; see the file COPYING. If not, write to the
19 * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24 /* This project is a small, tightly tailored application. it is not
25 designed to be nigh-infinitely extensible, nor is it designed to be
26 reusable code. It's monolithic, inflexible, and designed that way
27 on purpose. */
29 /* sound playback code is OSS-specific for now */
30 #include "postfish.h"
31 #include <signal.h>
32 #include <getopt.h>
33 #include <fenv.h> // Thank you C99!
34 #include <fftw3.h>
35 #include <gtk/gtk.h>
36 #include "input.h"
37 #include "output.h"
38 #include "declip.h"
39 #include "eq.h"
40 #include "deverb.h"
41 #include "multicompand.h"
42 #include "singlecomp.h"
43 #include "limit.h"
44 #include "mute.h"
45 #include "mix.h"
46 #include "freeverb.h"
47 #include "version.h"
48 #include "config.h"
49 #include "mainpanel.h"
51 int eventpipe[2];
52 sig_atomic_t main_looping;
53 char *configfile="postfish-staterc";
54 char *version;
56 void clean_exit(int sig){
57 signal(sig,SIG_IGN);
58 if(sig!=SIGINT){
59 fprintf(stderr,
60 "\nTrapped signal %d; saving state and exiting!\n"
61 "This signal almost certainly indicates a bug in the Postfish;\n"
62 "If this version of Postfish is newer than a few months old,\n"
63 "please email a detailed report of the crash along with\n"
64 "processor type, OS version, FFTW3 version, and as much\n"
65 "information as possible about what caused the crash. The best\n"
66 "possible report will outline the exact steps needed to\n"
67 "reproduce the error, ensuring that we at Xiph can fix the\n"
68 "bug as quickly as possible.\n\n"
69 "-- monty@xiph.org, Postfish revision %s\n\n",sig,version);
70 configfile="postfish-staterc-crashsave";
72 save_state();
74 if(main_looping){
75 main_looping=0;
76 gtk_main_quit();
78 exit(0);
82 /* otherwise inform the UI thread that we've requested shutdown */
83 write(eventpipe[1],"\001",1);
87 const char *optstring = "-c:gh";
89 struct option options [] = {
90 {"configuration-file",required_argument,NULL,'c'},
91 {"group",no_argument,NULL,'g'},
92 {"help",no_argument,NULL,'h'},
94 {NULL,0,NULL,0}
97 static void usage(FILE *f){
98 fprintf( f,
99 "\nthe Postfish, revision %s\n\n"
101 "USAGE:\n"
102 " postfish [options] infile [infile]+ [-g infile [infile]+]+ > output\n\n"
104 "OPTIONS:\n"
105 " -c --configuration-file : load state from alternate configuration file\n"
106 " -g --group : place following input files in a new channel\n"
107 " grouping\n"
108 " -h --help : print this help\n\n"
109 "INPUT:\n\n"
111 " Postfish takes WAV/AIFF input either from stdin or from a list of files\n"
112 " specified on the command line. A list of input files is handled as\n"
113 " time-continguous entries, each holding audio data that continues at\n"
114 " the instant the previous file ends. Files may also be arranged into\n"
115 " groups with -g; each group represents additional input channels\n"
116 " parallel to preceeding groups. All input files must be the same\n"
117 " sampling rate. Files in a group must have the same number of\n"
118 " channels.\n\n"
120 " Examples:\n\n"
121 " Files a.wav, b.wav, c.wav and d.wav are all four channels and\n"
122 " ten minutes each.\n\n"
124 " postfish a.wav b.wav c.wav d.wav \n"
125 " This command line treats the input as forty minutes of four channel\n"
126 " audio in the order a.wav, b.wav, c.wav, d.wav.\n\n"
128 " postfish a.wav b.wav -g c.wav d.wav \n"
129 " This command line treats the input as twenty minutes of eight channel\n"
130 " audio. Channels 1-4 are taken from files a.wav and b.wav while channels\n"
131 " 5-8 are taken from files c.wav and d.wav.\n\n"
133 " cat a.wav | postfish \n"
134 " This command line sends a.wav to Postfish as a non-seekable stream\n"
135 " of four-channel data. If the WAV (or AIFF) header is complete, Postfish\n"
136 " obeys the length encoded in the header and halts after processing to\n"
137 " that length. If the data length in the header is unset (0 or -1),\n"
138 " Postfish will continue processing data until EOF on stdin.\n\n"
140 "OUTPUT:\n\n"
142 " Postfish writes output to stdout.\n\n"
144 " If stdout is piped, the output is nonseekable and Postfish marks the\n"
145 " produced header incomplete (length of -1). Stopping and re-starting\n"
146 " processing writes a fresh stream to stdout.\n\n"
148 " If stdout is redirected to a file, Postfish will write a complete header\n"
149 " upon processing halt or program exit. If processing halts and restarts,\n"
150 " the file is re-written from scratch.\n\n"
152 " If stdout is a pipe or redirected to a file, the user may specify\n"
153 " parallel audio monitor through the audio device using the 'mOn' activator\n"
154 " button in the main panel's 'master' section, or on the output config\n"
155 " panel. The audio device selected for playback is configurable on the\n"
156 " output config panel.\n\n"
158 " If stdout is redirected to an audio device, output is sent to that audio\n"
159 " device exclusively and the 'mOn' activator on the main panel will not\n"
160 " be available.\n\n"
162 "STATE/CONFIG:\n\n"
164 " By default, persistent panel state is loaded from the file \n"
165 " 'postfish-staterc' in the current working directory. Postfish rewrites\n"
166 " this file with all current panel state upon exit. -c specifies loading\n"
167 " from and saving to an alternate configuration file name.\n\n",version);
171 void parse_command_line(int argc, char **argv){
172 int c,long_option_index;
173 int newgroup=1;
175 while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
176 switch(c){
177 case 1:
178 /* file name that belongs to current group */
179 input_parse(optarg,newgroup);
180 newgroup=0;
181 break;
182 case 'c':
183 /* alternate configuration file */
184 configfile=strdup(optarg);
185 break;
186 case 'g':
187 /* start a new file/channel group */
188 newgroup=1;
189 break;
190 case 'h':
191 usage(stdout);
192 exit(0);
193 default:
194 usage(stderr);
195 exit(0);
200 int look_for_wisdom(char *filename){
201 int ret;
202 FILE *f=fopen(filename,"r");
203 if(!f)return 0;
204 ret=fftwf_import_wisdom_from_file(f);
205 fclose(f);
207 if(ret)
208 fprintf(stderr,"Found valid postfish-wisdomrc file at %s\n",filename);
209 else
210 fprintf(stderr,"WARNING: corrupt, invalid or obsolete postfish-wisdomrc file at %s\n",filename);
211 return(ret);
214 static int sigill=0;
215 void sigill_handler(int sig){
216 /* make sure */
217 if(sig==SIGILL)sigill=1;
220 int main(int argc, char **argv){
221 int wisdom=0;
223 version=strstr(VERSION,"version.h");
224 if(version){
225 char *versionend=strchr(version,' ');
226 if(versionend)versionend=strchr(versionend+1,' ');
227 if(versionend)versionend=strchr(versionend+1,' ');
228 if(versionend)versionend=strchr(versionend+1,' ');
229 if(versionend){
230 int len=versionend-version-9;
231 version=strdup(version+10);
232 version[len-1]=0;
234 }else{
235 version="";
238 /* parse command line and open all the input files */
239 parse_command_line(argc, argv);
241 /* We do not care about FPEs; rather, underflow is nominal case, and
242 its better to ignore other traps in production than to crash the
243 app. Please inform the FPU of this. */
245 #ifndef DEBUG
246 fedisableexcept(FE_INVALID);
247 fedisableexcept(FE_INEXACT);
248 fedisableexcept(FE_UNDERFLOW);
249 fedisableexcept(FE_OVERFLOW);
250 #else
251 feenableexcept(FE_INVALID);
252 feenableexcept(FE_INEXACT);
253 feenableexcept(FE_UNDERFLOW);
254 feenableexcept(FE_OVERFLOW);
255 #endif
257 /* Linux Altivec support has a very annoying problem; by default,
258 math on denormalized floats will simply crash the program. FFTW3
259 uses Altivec, so boom, but only random booms.
261 By the C99 spec, the above exception configuration is also
262 supposed to handle Altivec config, but doesn't. So we use the
263 below ugliness to both handle altivec and non-alitvec PPC. */
265 #ifdef __PPC
266 #include <altivec.h>
267 signal(SIGILL,sigill_handler);
269 #if (defined __GNUC__) && (__GNUC__ == 3) && ! (defined __APPLE_CC__)
270 __vector unsigned short noTrap =
271 (__vector unsigned short){0,0,0,0,0,0,0x1,0};
272 #else
273 vector unsigned short noTrap =
274 (vector unsigned short)(0,0,0,0,0,0,0x1,0);
275 #endif
277 vec_mtvscr(noTrap);
278 #endif
280 /* check for fftw wisdom file in order:
281 ./postfish-wisdomrc
282 $(POSTFISHDIR)/postfish-wisdomrc
283 ~/.postfish/postfish-wisdomrc
284 ETCDIR/postfish-wisdomrc
285 system wisdom */
288 wisdom=look_for_wisdom("./postfish-wisdomrc");
289 if(!wisdom){
290 char *rcdir=getenv("POSTFISH_RCDIR");
291 if(rcdir){
292 char *rcfile="/postfish-wisdomrc";
293 char *homerc=calloc(1,strlen(rcdir)+strlen(rcfile)+1);
294 strcat(homerc,rcdir);
295 strcat(homerc,rcfile);
296 wisdom=look_for_wisdom(homerc);
299 if(!wisdom){
300 char *rcdir=getenv("HOME");
301 if(rcdir){
302 char *rcfile="/.postfish/postfish-wisdomrc";
303 char *homerc=calloc(1,strlen(rcdir)+strlen(rcfile)+1);
304 strcat(homerc,rcdir);
305 strcat(homerc,rcfile);
306 wisdom=look_for_wisdom(homerc);
309 if(!wisdom)wisdom=look_for_wisdom(ETCDIR"/postfish-wisdomrc");
310 if(!wisdom){
311 fftwf_import_system_wisdom();
313 fprintf(stderr,"Postfish could not find the postfish-wisdom configuration file normally built\n"
314 "or installed with Postfish and located in one of the following places:\n"
316 "\t./postfish-wisdomrc\n"
317 "\t$(POSTFISHDIR)/postfish-wisdomrc\n"
318 "\t~/.postfish/postfish-wisdomrc\n\t"
319 ETCDIR"/postfish-wisdomrc\n"
320 "This configuration file is used to reduce the startup time Postfish uses to \n"
321 "pre-calculate Fourier transform tables for the FFTW3 library. Postfish will start\n"
322 "and operate normally, but it will take additional time before popping the main\n"
323 "window because this information must be regenerated each time Postfish runs.\n");
326 /* probe outputs */
327 if(setvbuf(stdout, NULL, _IONBF , 0))
328 fprintf(stderr,"Unable to remove block buffering on stdout; continuing\n");
330 output_probe_stdout(STDOUT_FILENO);
331 output_probe_monitor();
333 /* open all the input files */
334 if(input_load())exit(1);
336 /* load config file */
337 if(config_load(configfile))exit(1);
339 /* set up filter chains */
340 if(declip_load())exit(1);
341 if(eq_load(OUTPUT_CHANNELS))exit(1);
342 if(deverb_load())exit(1);
343 if(multicompand_load(OUTPUT_CHANNELS))exit(1);
344 if(singlecomp_load(OUTPUT_CHANNELS))exit(1);
345 if(limit_load(OUTPUT_CHANNELS))exit(1);
346 if(mute_load())exit(1);
347 if(mix_load(OUTPUT_CHANNELS))exit(1);
348 if(p_reverb_load())exit(1);
350 /* easiest way to inform gtk of changes and not deal with locking
351 issues around the UI */
352 if(pipe(eventpipe)){
353 fprintf(stderr,"Unable to open event pipe:\n"
354 " %s\n",strerror(errno));
356 exit(1);
359 input_seek(0);
361 main_looping=0;
363 signal(SIGINT,clean_exit);
364 signal(SIGSEGV,clean_exit);
366 mainpanel_go(argc,argv,input_ch);
368 return(0);