9 #include <vorbis/vorbisfile.h>
10 #include <alsa/asoundlib.h>
13 static void key_cleanup(void);
15 static void __attribute__((noreturn)) die(char *msg, ...)
19 vfprintf(stderr, msg, args);
25 typedef long long s64;
27 #define MIN(a,b) ((a)<(b) ? (a) : (b))
28 #define MAX(a,b) ((a)>(b) ? (a) : (b))
29 #define TRIPLE(pos) (int)(((pos)/rate)/60), (int)(((pos)/rate)%60), (int)((pos)%rate)
31 static struct termios tios, tios_old;
32 static int tios_inited;
34 static void key_init(void)
36 if (tcgetattr(0, &tios_old) < 0)
37 die("tcgetattr failed: %m");
39 tios.c_iflag = IGNBRK;
43 if (tcsetattr(0, 0, &tios) < 0)
44 die("tcsetattr failed: %m");
45 fcntl(0, F_SETFL, O_NONBLOCK);
49 static void key_cleanup(void)
53 tcsetattr(0, 0, &tios_old);
58 static int key_get(void)
61 static int esc_state, esc_num;
65 int e = read(0, keybuf, 1);
88 if (key >= '0' && key <= '9')
89 esc_num = 10*esc_num + key - '0';
93 return (esc_num << 16) + 0x200 + key;
101 enum { IN_WAV, IN_OGG } inmode;
102 static OggVorbis_File vf;
103 static SNDFILE *sndf;
104 static unsigned int rate;
105 static char *find_title = "Toulky ceskou minulosti";
106 static s64 total_samples = 0;
107 static s64 start_pos = -1;
108 static s64 end_pos = -1;
109 double prefade = 1, postfade = 1;
111 static void scan_streams(void)
113 int nstr = ov_streams(&vf);
114 printf("OGG: Scanning %d logical streams:\n", nstr);
116 die("No streams found");
117 for (int i=0; i<nstr; i++)
120 vi = ov_info(&vf, i);
122 die("ov_info failed");
123 if (vi->channels != 2)
124 die("Stream %d has %d channels, which is not supported", i, vi->channels);
125 if ((unsigned int) vi->rate != rate)
128 die("Stream %d has sample rate %d, while the previous had %d", i, vi->rate, rate);
134 vc = ov_comment(&vf, i);
136 die("ov_comment failed");
139 for (int j=0; j<vc->comments; j++)
140 if (vc->comment_lengths[j] > 6 && !strncasecmp(vc->user_comments[j], "title=", 6))
142 int l = vc->comment_lengths[j] - 6;
143 memcpy(title, vc->user_comments[j]+6, l);
147 strcpy(title, "<none>");
149 s64 samples = ov_pcm_total(&vf, i);
150 int sec = (samples + rate - 1) / rate;
151 printf(" %d: `%s' (%d:%02d, %d bits/sec) @%Ld\n", i, title, sec/60, sec%60, (int)vi->bitrate_nominal, total_samples);
153 if (find_title && strstr(title, find_title))
156 start_pos = total_samples;
157 else if (end_pos != total_samples)
158 printf("WARNING: Gap encountered!\n");
159 end_pos = total_samples + samples;
162 total_samples += samples;
164 if (ov_pcm_total(&vf, -1) != total_samples)
165 die("ov_pcm_total mismatch");
170 printf("WARNING: Title not found, marking whole file\n");
172 end_pos = total_samples;
176 static void in_open(char *name)
178 infile = fopen(name, "r");
180 die("Cannot open %s: %m", name);
183 if (fread(s, 1, 4, infile) != 4)
184 die("Input file too short");
186 if (!memcmp(s, "RIFF", 4))
188 puts("INPUT: WAV file detected");
191 else if (!memcmp(s, "OggS", 4))
193 puts("INPUT: OGG file detected");
196 else if (!memcmp(s, "HTTP", 4))
198 puts("INPUT: HTTP header detected, expecting OGG inside");
202 die("Unable to identify input format");
204 if (inmode == IN_WAV)
207 bzero(&si, sizeof(si));
208 lseek(fileno(infile), 0, SEEK_SET);
209 sndf = sf_open_fd(fileno(infile), SFM_READ, &si, 0);
211 die("sf_open_fd() failed: %s", sf_strerror(NULL));
212 total_samples = si.frames;
213 rate = si.samplerate;
214 if (si.channels != 2)
215 die("Got %d channels instead of 2", si.channels);
216 if (si.sections != 1)
217 die("Found %d sections, what does it mean?", si.sections);
218 printf("WAV: format id=%08x\n", si.format);
220 end_pos = total_samples;
224 int err = ov_open(infile, &vf, NULL, 0);
226 die("ov_open: error %d", err);
227 if (!ov_seekable(&vf))
228 die("Input is not seekable, how come?");
232 printf("INPUT: length %3d:%02d.%05d, %Ld samples at rate %d\n", TRIPLE(total_samples), total_samples, rate);
235 static void in_goto(s64 go)
237 if (inmode == IN_OGG)
238 ov_pcm_seek(&vf, go);
240 sf_seek(sndf, go, SEEK_SET);
243 static int in_read(s16 *buf, s64 pos, int nsamp)
245 if (inmode == IN_OGG)
247 if (ov_pcm_tell(&vf) != pos)
249 printf("\n!!! CONFUSED POSITION\n");
250 //ov_pcm_seek(&vf, pos);
251 //if (ov_pcm_tell(&vf) != pos)
252 //printf(">>> unable to correct :(\n");
257 int e = ov_read(&vf, (char*)buf, 4*nsamp, 0, 2, 1, &bp);
259 printf("!!! HOLE DETECTED\n");
260 else if (e == OV_EBADLINK)
261 printf("!!! BAD LINK\n");
263 die("ov_read returned %d bytes, which means non-integer number of samples. Huh.", e);
265 die("ov_read returned %d. Huh!", e);
266 else if (e/4 <= nsamp)
269 die("ov_read returned %d samples, although we wanted %d", e/4, nsamp);
274 int e = sf_readf_short(sndf, buf, nsamp);
276 printf("!!!! sf_readf_short mismatch: %d != %d\n", e, nsamp);
281 static void in_close(void)
283 if (inmode == IN_OGG)
289 static s16 *prefader, *postfader;
291 static s16 *calc_fader(int len, int rev)
295 s16 *x = malloc(2*len);
296 for (int i=0; i<len; i++)
297 x[rev ? len-1-i : i] = (s64)i*32767/len;
301 static void recalc_faders(void)
303 // printf("FADERS: set to %g pre, %g post\n", prefade, postfade);
305 prefader = calc_fader(prefade*rate, 0);
307 postfader = calc_fader(postfade*rate, 1);
310 static void apply_fader(s16 *sig, int nsamp, s64 pos, s64 fstart, int flen, s16 *fader)
312 s64 relpos = pos - fstart;
313 if (relpos <= -nsamp || relpos >= flen)
323 while (nsamp > 0 && rel < flen)
325 sig[0] = (sig[0] * fader[rel]) >> 15;
326 sig[1] = (sig[1] * fader[rel]) >> 15;
333 static int cooked_read(s16 *buf, s64 pos, int nsamp, int apply_pre, int apply_post)
335 nsamp = in_read(buf, pos, nsamp);
336 int presamp = prefade * rate;
337 int postsamp = postfade * rate;
339 apply_fader(buf, nsamp, pos, start_pos, presamp, prefader);
341 apply_fader(buf, nsamp, pos, end_pos-postsamp, postsamp, postfader);
345 static char *cuename;
347 static int cue_load(void)
351 FILE *f = fopen(cuename, "r");
354 if (fscanf(f, "%Ld%lf%Ld%lf", &start_pos, &prefade, &end_pos, &postfade) != 4)
355 die("CUE: Invalid syntax");
357 printf("CUE: Loaded\n");
364 static void cue_save(void)
368 FILE *f = fopen(cuename, "w");
370 die("CUE: Cannot write");
371 fprintf(f, "%Ld %g %Ld %g\n", start_pos, prefade, end_pos, postfade);
376 static void editor(void)
378 printf("Initializing ALSA\n");
380 snd_pcm_hw_params_t *apars;
382 #define ALSACALL(f,args) if ((err = f args) < 0) die(#f " failed: %s", snd_strerror(err))
383 ALSACALL(snd_pcm_open, (&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0));
384 ALSACALL(snd_pcm_hw_params_malloc, (&apars));
385 ALSACALL(snd_pcm_hw_params_any, (pcm, apars));
386 ALSACALL(snd_pcm_hw_params_set_access, (pcm, apars, SND_PCM_ACCESS_RW_INTERLEAVED));
387 ALSACALL(snd_pcm_hw_params_set_format, (pcm, apars, SND_PCM_FORMAT_S16_LE));
388 unsigned int xrate = rate;
390 ALSACALL(snd_pcm_hw_params_set_rate_near, (pcm, apars, &xrate, &dir));
392 printf("WARNING: Rate set to %d instead of %d\n", xrate, rate);
393 ALSACALL(snd_pcm_hw_params_set_channels, (pcm, apars, 2));
394 ALSACALL(snd_pcm_hw_params, (pcm, apars));
395 snd_pcm_hw_params_free(apars);
396 ALSACALL(snd_pcm_prepare, (pcm));
401 #define CLAMP(x) (((x) < 0) ? 0 : ((x) >= total_samples) ? total_samples-1 : (x))
433 if (mode == M_START || mode == M_PRE)
439 else if (mode == M_END || mode == M_POST)
442 go = CLAMP(end_pos - lback);
455 fst = 1. / (16 >> (key - '0'));
459 fst = 1 << (key - '4');
465 start_pos = CLAMP(start_pos - step);
468 else if (mode == M_POST)
470 end_pos = CLAMP(end_pos - step);
471 go = CLAMP(end_pos - lback);
474 go = CLAMP(pos - step);
479 start_pos = CLAMP(start_pos + step);
482 else if (mode == M_POST)
484 end_pos = CLAMP(end_pos + step);
485 go = CLAMP(end_pos - lback);
488 go = CLAMP(pos + step);
497 else if (mode == M_POST)
499 go = CLAMP(end_pos - lback);
505 case 0x7027e: // Home
511 go = CLAMP(total_samples - lback);
513 case 0x5027e: // PgUp
515 go = CLAMP(pos - 60*rate);
517 case 0x6027e: // PgUp
519 go = CLAMP(pos + 60*rate);
521 case 0x2027e: // Insert
524 case 0x3027e: // Delete
526 go = end_pos - lback;
534 go = CLAMP(end_pos - lback);
543 if (mode == M_PRE || mode == M_POST)
545 double *f = (mode == M_PRE ? &prefade : &postfade);
551 *f = MIN(*f + fst, 10);
553 *f = MAX(*f - fst, 0);
558 go = CLAMP(end_pos - lback);
568 printf("KEY <%x>\n", key);
574 printf("%3d:%02d.%05d [%s %3d:%02d.%05d/%g -> %s %3d:%02d.%05d\\%g] step=%g%s\e[K\r",
576 (mode == M_START) ? "START" : (mode == M_PRE) ? "START>" : "start", TRIPLE(start_pos), prefade,
577 (mode == M_END) ? "END" : (mode == M_POST) ? ">END" : "end", TRIPLE(end_pos), postfade,
579 (silence < 0) ? " [STOP]" : "");
582 s64 end = (mode == M_POST) ? end_pos : total_samples;
584 if (pos >= end || silence)
590 nsamp = MIN(nsamp, silence);
597 nsamp = sizeof(buf)/4;
598 if (pos + nsamp > end)
600 nsamp = cooked_read(buf, pos, nsamp, (mode == M_PRE), (mode == M_POST));
604 int err = snd_pcm_writei(pcm, buf, nsamp);
607 puts("[xrun detected]");
608 err = snd_pcm_prepare(pcm);
610 die("xrun recovery failed: error %d", err);
613 die("snd_pcm_writei failed: error %d", err);
623 static int nchildren;
625 static int add_filter(int fd, char **args)
640 execvp(args[0], args);
641 die("execvp(%s) failed: %m", args[0]);
646 printf("FILTER: Forked pid %d for %s\n", pid, args[0]);
653 static int orig_stdout;
655 static void render(char *name)
659 if (!strcmp(name, "-"))
666 sfix = strrchr(name, '.') ? : "";
667 fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666);
669 die("Unable to create %s: %m", name);
671 if (!strcmp(sfix, ".WAV"))
672 printf("RENDER: WAV output without sample rate conversion\n");
673 else if (!strcmp(sfix, ".wav"))
675 printf("RENDER: WAV output\n");
678 printf("RENDER: Adding resample filter\n");
679 char *f[] = { "sox", "-twav", "-", "-r44100", "-twav", "-", "vol", "0.9", "resample", NULL };
680 fd = add_filter(fd, f);
684 die("Unknown output file suffix");
686 s64 outlen = end_pos - start_pos;
689 /* GRRRR! libsndfile is unable to write WAV files to pipes! */
694 .format = SF_FORMAT_WAV | SF_FORMAT_PCM_16,
698 SNDFILE *sf = sf_open_fd(fd, SFM_WRITE, &si, 0);
700 die("Output sf_open_fd() failed: %s", sf_strerror(NULL));
702 FILE *of = fdopen(fd, "w");
703 void wt(char *x) { fwrite(x, 4, 1, of); }
704 void wr(unsigned int x) { fwrite(&x, sizeof(x), 1, of); }
706 wr(8 + 0x10 + 8 + 4*outlen);
710 wr(0x00020001); // 2 channels, not compressed
713 wr(0x00100004); // 16-bit
722 printf("RENDER: Generating %d:%02d.%05d of output\n", TRIPLE(outlen));
725 int n = sizeof(buf) / 4;
726 if (end_pos - pos < n)
730 n = cooked_read(buf, pos, n, 1, 1);
734 if (sf_writef_short(sf, buf, n) != n)
736 if ((int)fwrite(buf, 4, n, of) != n)
738 die("Short write, oops!");
739 printf("RENDER: %3d:%02d.%05d\r", TRIPLE(outpos));
754 printf("wait(): %m\n");
761 printf("!!! pid %d failed with exit code %d\n", p, WEXITSTATUS(st));
763 printf("FILTER: pid %d finished OK\n", p);
766 printf("!!! pid %d failed with status %x\n", p, st);
769 printf("RENDER: Rendering successfully completed.\n");
772 int main(int argc, char **argv)
774 if (argc < 2 || argc > 4)
775 die("Usage: vorbiscut <infile> [<cuefile> [<outfile>|-]]");
779 orig_stdout = dup(1);
786 int cueok = cue_load();
791 printf("!!!!! Cue sheet not present, rendering with defaults !!!!!\n");