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