main.c 11.9 KB
Newer Older
Monty Montgomery's avatar
Monty Montgomery committed
1 2 3 4
/*
 *
 *  postfish
 *    
Monty Montgomery's avatar
Monty Montgomery committed
5
 *      Copyright (C) 2002-2005 Monty
Monty Montgomery's avatar
Monty Montgomery committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *
 *  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. */

Monty Montgomery's avatar
Monty Montgomery committed
29
#include "postfish.h"
30 31
#include <signal.h>
#include <getopt.h>
32
#include <fenv.h>  // Thank you C99!
Monty Montgomery's avatar
Monty Montgomery committed
33
#include <fftw3.h>
34
#include <gtk/gtk.h>
35
#include <ao/ao.h>
Monty Montgomery's avatar
Monty Montgomery committed
36
#include "input.h"
Monty Montgomery's avatar
Monty Montgomery committed
37
#include "output.h"
38 39
#include "declip.h"
#include "eq.h"
40
#include "deverb.h"
41 42 43
#include "multicompand.h"
#include "singlecomp.h"
#include "limit.h"
44
#include "mute.h"
45
#include "mix.h"
Monty Montgomery's avatar
Monty Montgomery committed
46
#include "freeverb.h"
47 48 49
#include "version.h"
#include "config.h"
#include "mainpanel.h"
Monty Montgomery's avatar
Monty Montgomery committed
50

51
int eventpipe[2]={-1,-1};
52 53 54
sig_atomic_t main_looping;
char *configfile="postfish-staterc";
char *version;
55
int batch = 0;
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72

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"
	    "-- monty@xiph.org, Postfish revision %s\n\n",sig,version);
    configfile="postfish-staterc-crashsave";

73 74 75 76 77 78 79 80
    if(!batch){
      save_state();

      if(main_looping){
        main_looping=0;
        gtk_main_quit();
      }
      ao_shutdown();
81 82
    }
    exit(0);
83
  }
84 85

  /* otherwise inform the UI thread that we've requested shutdown */
86
  if(eventpipe[1]!=-1) write(eventpipe[1],"\001",1);
87

88 89
}

90
const char *optstring = "-c:ghB";
91 92

struct option options [] = {
93
        {"batch-mode",no_argument,NULL,'B'},
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
        {"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"
109 110
"  -B --batch-mode            : process the input in batch mode without\n"
"                               UI or monitor output\n"
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
"  -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;
188 189 190
    case 'B':
      batch=1;
      break;
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
    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);
    }
  }
}
Monty Montgomery's avatar
Monty Montgomery committed
208

209 210 211 212 213 214
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);
215
 
216 217 218 219 220 221 222
  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);
}

223 224 225 226 227 228
static int sigill=0;
void sigill_handler(int sig){
  /* make sure */
  if(sig==SIGILL)sigill=1;
}

Monty Montgomery's avatar
Monty Montgomery committed
229
int main(int argc, char **argv){
230 231
  int wisdom=0;

232 233
  /* Init sub-components */
  feedback_init();
234
  window_init();
235 236
  input_init();

237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
  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);

255 256 257
  /* 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. */
258

259
/*
260 261
#ifndef DEBUG
  fedisableexcept(FE_INVALID);
262 263 264
  fedisableexcept(FE_INEXACT);
  fedisableexcept(FE_UNDERFLOW);
  fedisableexcept(FE_OVERFLOW);
265 266 267 268 269 270
#else
  feenableexcept(FE_INVALID);
  feenableexcept(FE_INEXACT);
  feenableexcept(FE_UNDERFLOW);
  feenableexcept(FE_OVERFLOW);
#endif 
271
*/
272 273 274

  /* Linux Altivec support has a very annoying problem; by default,
     math on denormalized floats will simply crash the program.  FFTW3
275
     uses Altivec, so boom, but only random booms.
276 277 278
     
     By the C99 spec, the above exception configuration is also
     supposed to handle Altivec config, but doesn't.  So we use the
279
     below ugliness to both handle altivec and non-alitvec PPC. */
280 281 282

#ifdef __PPC
#include <altivec.h>
283 284
  signal(SIGILL,sigill_handler);
  
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
#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");
  }
Monty Montgomery's avatar
Monty Montgomery committed
341

342
  /* probe outputs */
343
  ao_initialize();
344 345 346
  if(setvbuf(stdout, NULL, _IONBF , 0))
    fprintf(stderr,"Unable to remove block buffering on stdout; continuing\n");
  
347
  output_probe_stdout(STDOUT_FILENO);
348
  if(!batch) output_probe_monitor();
349 350

  /* open all the input files */
351 352 353 354
  if(input_load()){
    ao_shutdown();
    exit(1);
  }
355 356

  /* load config file */
357 358 359 360
  if(config_load(configfile)){
    ao_shutdown();
    exit(1);
  }
361

Monty Montgomery's avatar
Monty Montgomery committed
362 363
  /* set up filter chains */
  if(declip_load())exit(1);
364
  if(eq_load(OUTPUT_CHANNELS))exit(1);
365
  if(deverb_load())exit(1);
366 367 368
  if(multicompand_load(OUTPUT_CHANNELS))exit(1);
  if(singlecomp_load(OUTPUT_CHANNELS))exit(1);
  if(limit_load(OUTPUT_CHANNELS))exit(1);
369
  if(mute_load())exit(1);
370
  if(mix_load(OUTPUT_CHANNELS))exit(1);
Monty Montgomery's avatar
Monty Montgomery committed
371
  if(p_reverb_load())exit(1);
Monty Montgomery's avatar
Monty Montgomery committed
372

Monty Montgomery's avatar
Monty Montgomery committed
373 374
  /* easiest way to inform gtk of changes and not deal with locking
     issues around the UI */
375
  if(!batch && pipe(eventpipe)){
Monty Montgomery's avatar
Monty Montgomery committed
376 377
    fprintf(stderr,"Unable to open event pipe:\n"
            "  %s\n",strerror(errno));
378
    ao_shutdown();
Monty Montgomery's avatar
Monty Montgomery committed
379 380 381
    exit(1);
  }

Monty Montgomery's avatar
Monty Montgomery committed
382
  input_seek(0);
383 384 385

  main_looping=0;

386 387
  if(!batch)
    signal(SIGINT,clean_exit);
388 389
  signal(SIGSEGV,clean_exit);

390 391 392 393 394 395 396
  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);
Monty Montgomery's avatar
Monty Montgomery committed
397

398
  ao_shutdown();
Monty Montgomery's avatar
Monty Montgomery committed
399 400 401 402 403 404 405
  return(0);
}





406