1 /**
2  * Aften: A/52 audio encoder
3  * Copyright (c) 2006 Justin Ruggles
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 /**
21  * @file aften.c
22  * Commandline encoder frontend
23  */
24 
25 #include "common.h"
26 
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <time.h>
31 
32 #ifdef _WIN32
33 #include <io.h>
34 #include <fcntl.h>
35 #endif
36 
37 #include "aften.h"
38 #include "pcm.h"
39 #include "opts.h"
40 
41 static const int acmod_to_ch[8] = { 2, 1, 2, 3, 3, 4, 4, 5 };
42 
43 static const char *acmod_str[8] = {
44     "dual mono (1+1)", "mono (1/0)", "stereo (2/0)",
45     "3/0", "2/1", "3/1", "2/2", "3/2"
46 };
47 
48 static void
print_intro(FILE * out)49 print_intro(FILE *out)
50 {
51     const char *vers = aften_get_version();
52     fprintf(out, "\nAften: A/52 audio encoder\n"
53                  "Version %s\n"
54                  "(c) 2006-2007 Justin Ruggles, Prakash Punnoor, et al.\n\n",
55                  vers);
56 }
57 
58 static void
print_simd_in_use(FILE * out,AftenSimdInstructions * simd_instructions)59 print_simd_in_use(FILE *out, AftenSimdInstructions *simd_instructions)
60 {
61     fprintf(out, "SIMD usage:");
62     if (simd_instructions->mmx)
63         fprintf(out, " MMX");
64     if (simd_instructions->sse)
65         fprintf(out, " SSE");
66     if (simd_instructions->sse2)
67         fprintf(out, " SSE2");
68     if (simd_instructions->sse3)
69         fprintf(out, " SSE3");
70     if (simd_instructions->ssse3)
71         fprintf(out, " SSSE3");
72     if (simd_instructions->amd_3dnow)
73         fprintf(out, " 3DNOW");
74     if (simd_instructions->amd_3dnowext)
75         fprintf(out, " 3DNOWEXT");
76     if (simd_instructions->amd_sse_mmx)
77         fprintf(out, " SSE-MMX");
78     if (simd_instructions->altivec)
79         fprintf(out, " Altivec");
80     fprintf(out, "\n");
81 }
82 
83 int
main(int argc,char ** argv)84 main(int argc, char **argv)
85 {
86     uint8_t *frame;
87     FLOAT *fwav;
88     int nr, fs, err;
89     FILE *ifp;
90     FILE *ofp;
91     PcmFile pf;
92     CommandOptions opts;
93     AftenContext s;
94     uint32_t samplecount, bytecount, t0, t1, percent;
95     FLOAT kbps, qual, bw;
96     int last_frame;
97     int frame_cnt;
98     int done;
99     int input_file_format;
100     enum PcmSampleFormat read_format;
101     /* update output every 200ms */
102     clock_t update_clock_span = 0.2f * CLOCKS_PER_SEC;
103     clock_t current_clock;
104     clock_t last_update_clock = clock() - update_clock_span;
105 
106     opts.s = &s;
107     aften_set_defaults(&s);
108     err = parse_commandline(argc, argv, &opts);
109     if(err) {
110         if(err == 1) {
111             print_intro(stderr);
112             print_usage(stderr);
113             return 1;
114         } else {
115             print_intro(stdout);
116             if(err == 2) {
117                 print_help(stdout);
118             } else if(err == 3) {
119                 print_long_help(stdout);
120             }
121             return 0;
122         }
123     }
124 
125     if(s.verbose > 0) {
126         print_intro(stderr);
127     }
128 
129     if(!strncmp(opts.infile, "-", 2)) {
130 #ifdef _WIN32
131         _setmode(_fileno(stdin), _O_BINARY);
132 #endif
133         ifp = stdin;
134     } else {
135         ifp = fopen(opts.infile, "rb");
136         if(!ifp) {
137             fprintf(stderr, "error opening input file: %s\n", opts.infile);
138             return 1;
139         }
140     }
141 
142 #ifdef CONFIG_DOUBLE
143     read_format = PCM_SAMPLE_FMT_DBL;
144 #else
145     read_format = PCM_SAMPLE_FMT_FLT;
146 #endif
147 
148     // initialize pcmfile using input
149     input_file_format = PCM_FORMAT_UNKNOWN;
150     if(opts.raw_input)
151         input_file_format = PCM_FORMAT_RAW;
152     if(pcmfile_init(&pf, ifp, read_format, input_file_format)) {
153         fprintf(stderr, "invalid input file: %s\n", argv[1]);
154         return 1;
155     }
156     if(opts.read_to_eof)
157         pf.read_to_eof = 1;
158     if(opts.raw_input) {
159         pf.sample_rate = opts.raw_sr;
160         pf.channels = opts.raw_ch;
161         pcmfile_set_source(&pf, opts.raw_fmt, opts.raw_order);
162     }
163 
164     // print wav info to console
165     if(s.verbose > 0) {
166         fprintf(stderr, "input format: ");
167         pcmfile_print(&pf, stderr);
168     }
169 
170     // if acmod is given on commandline, determine lfe from number of channels
171     if(s.acmod >= 0) {
172         int ch = acmod_to_ch[s.acmod];
173         if(ch == pf.channels) {
174             if(s.lfe < 0) {
175                 s.lfe = 0;
176             } else {
177                 if(s.lfe != 0) {
178                     fprintf(stderr, "acmod and lfe do not match number of channels\n");
179                     return 1;
180                 }
181             }
182         } else if(ch == (pf.channels - 1)) {
183             if(s.lfe < 0) {
184                 s.lfe = 1;
185             } else {
186                 if(s.lfe != 1) {
187                     fprintf(stderr, "acmod and lfe do not match number of channels\n");
188                     return 1;
189                 }
190             }
191         } else {
192             fprintf(stderr, "acmod does not match number of channels\n");
193             return 1;
194         }
195     } else {
196         // if acmod is not given on commandline, determine from WAVE file
197         int ch = pf.channels;
198         if(s.lfe >= 0) {
199             if(s.lfe == 0 && ch == 6) {
200                 fprintf(stderr, "cannot encode 6 channels w/o LFE\n");
201                 return 1;
202             } else if(s.lfe == 1 && ch == 1) {
203                 fprintf(stderr, "cannot encode LFE channel only\n");
204                 return 1;
205             }
206             if(s.lfe) {
207                 pf.ch_mask |= 0x08;
208             }
209         }
210         if(aften_wav_channels_to_acmod(ch, pf.ch_mask, &s.acmod, &s.lfe)) {
211             fprintf(stderr, "mismatch in channels, acmod, and lfe params\n");
212             return 1;
213         }
214     }
215     // set some encoding parameters using wav info
216     s.channels = pf.channels;
217     s.samplerate = pf.sample_rate;
218 #ifdef CONFIG_DOUBLE
219     s.sample_format = A52_SAMPLE_FMT_DBL;
220 #else
221     s.sample_format = A52_SAMPLE_FMT_FLT;
222 #endif
223 
224     // initialize encoder
225     if(aften_encode_init(&s)) {
226         fprintf(stderr, "error initializing encoder\n");
227         aften_encode_close(&s);
228         return 1;
229     }
230 
231     // open output file
232     if(!strncmp(opts.outfile, "-", 2)) {
233 #ifdef _WIN32
234         _setmode(_fileno(stdout), _O_BINARY);
235 #endif
236         ofp = stdout;
237     } else {
238         ofp = fopen(opts.outfile, "wb");
239         if(!ofp) {
240             fprintf(stderr, "error opening output file: %s\n", opts.outfile);
241             return 1;
242         }
243     }
244 
245     // print ac3 info to console
246     if(s.verbose > 0) {
247         fprintf(stderr, "output format: %d Hz %s", s.samplerate,
248                 acmod_str[s.acmod]);
249         if(s.lfe) {
250             fprintf(stderr, " + LFE");
251         }
252         fprintf(stderr, "\n\n");
253     }
254 
255     /* print SIMD instructions used */
256     print_simd_in_use(stderr, &s.system.wanted_simd_instructions);
257 
258     /* print number of threads used */
259     fprintf(stderr, "Threads: %i\n\n", s.system.n_threads);
260 
261     // allocate memory for coded frame and sample buffer
262     frame = calloc(A52_MAX_CODED_FRAME_SIZE, 1);
263     fwav = calloc(A52_SAMPLES_PER_FRAME * s.channels, sizeof(FLOAT));
264     if(frame == NULL || fwav == NULL) {
265         aften_encode_close(&s);
266         exit(1);
267     }
268 
269     samplecount = bytecount = t0 = t1 = percent = 0;
270     qual = bw = 0.0;
271     last_frame = 0;
272     frame_cnt = 0;
273     done = 0;
274     fs = 0;
275     nr = 0;
276 
277     // don't pad start with zero samples, use input audio instead.
278     // not recommended, but providing the option here for when sync is needed
279     if(!opts.pad_start) {
280         FLOAT *sptr = &fwav[1280*s.channels];
281         nr = pcmfile_read_samples(&pf, sptr, 256);
282         if(opts.chmap == 0) {
283             aften_remap_wav_to_a52(sptr, nr, s.channels, s.sample_format,
284                                    s.acmod);
285         } else if(opts.chmap == 2) {
286             aften_remap_mpeg_to_a52(sptr, nr, s.channels, s.sample_format,
287                                     s.acmod);
288         }
289         fs = aften_encode_frame(&s, frame, fwav);
290         if(fs < 0) {
291             fprintf(stderr, "Error encoding initial frame\n");
292             goto end;
293         }
294     }
295 
296     nr = pcmfile_read_samples(&pf, fwav, A52_SAMPLES_PER_FRAME);
297     while(nr > 0 || fs > 0) {
298         if(opts.chmap == 0) {
299             aften_remap_wav_to_a52(fwav, nr, s.channels, s.sample_format,
300                                    s.acmod);
301         } else if(opts.chmap == 2) {
302             aften_remap_mpeg_to_a52(fwav, nr, s.channels, s.sample_format,
303                                     s.acmod);
304         }
305 
306         // append extra silent frame if final frame is > 1280 samples
307         if(nr == 0) {
308             if(last_frame <= 1280) {
309                 done = 1;
310             }
311         }
312 
313         // zero leftover samples at end of last frame
314         if(!done && nr < A52_SAMPLES_PER_FRAME) {
315             int i;
316             for(i=nr*s.channels; i<A52_SAMPLES_PER_FRAME*s.channels; i++) {
317                 fwav[i] = 0.0;
318             }
319         }
320 
321         fs = aften_encode_frame(&s, frame, done ? NULL : fwav);
322 
323         if(fs < 0) {
324             fprintf(stderr, "Error encoding frame %d\n", frame_cnt);
325             break;
326         } else {
327             if(s.verbose > 0) {
328                 if (fs > 0) {
329                     samplecount += A52_SAMPLES_PER_FRAME;
330                     bytecount += fs;
331                     qual += s.status.quality;
332                     bw += s.status.bwcode;
333                 }
334                 current_clock = clock();
335                 /* make sure we write out when finished, i.e. when fs == 0 */
336                 if (current_clock - last_update_clock >= update_clock_span || !fs || s.verbose == 2) {
337                     if(s.verbose == 1) {
338                         t1 = samplecount / pf.sample_rate;
339                         if(frame_cnt > 0 && (t1 > t0 || samplecount >= pf.samples)) {
340                             kbps = (bytecount * FCONST(8.0) * pf.sample_rate) /
341                                 (FCONST(1000.0) * samplecount);
342                             percent = 0;
343                             if(pf.samples > 0) {
344                                 percent = (uint32_t)((samplecount * FCONST(100.0)) /
345                                                      pf.samples);
346                                 percent = CLIP(percent, 0, 100);
347                             }
348                             fprintf(stderr, "\rprogress: %3u%% | q: %4.1f | "
349                                     "bw: %2.1f | bitrate: %4.1f kbps ",
350                                     percent, (qual / (frame_cnt+1)),
351                                     (bw / (frame_cnt+1)), kbps);
352                         }
353                         t0 = t1;
354                     } else if(s.verbose == 2) {
355                         fprintf(stderr, "frame: %7d | q: %4d | bw: %2d | bitrate: %3d kbps\n",
356                                 frame_cnt, s.status.quality, s.status.bwcode,
357                                 s.status.bit_rate);
358                     }
359                     last_update_clock = current_clock;
360                 }
361             }
362             fwrite(frame, 1, fs, ofp);
363         }
364         frame_cnt++;
365         last_frame = nr;
366         nr = pcmfile_read_samples(&pf, fwav, A52_SAMPLES_PER_FRAME);
367     }
368     if(s.verbose == 1) {
369         fprintf(stderr, "\n\n");
370     } else if(s.verbose == 2) {
371         if(samplecount > 0) {
372             kbps = (bytecount * FCONST(8.0) * pf.sample_rate) / (FCONST(1000.0) * samplecount);
373         } else {
374             kbps = 0;
375         }
376         frame_cnt = MAX(frame_cnt, 1);
377         fprintf(stderr, "\n");
378         fprintf(stderr, "average quality:   %4.1f\n", (qual / frame_cnt));
379         fprintf(stderr, "average bandwidth: %2.1f\n", (bw / frame_cnt));
380         fprintf(stderr, "average bitrate:   %4.1f kbps\n\n", kbps);
381     }
382 end:
383     free(fwav);
384     free(frame);
385 
386     pcmfile_close(&pf);
387     fclose(ifp);
388     fclose(ofp);
389 
390     aften_encode_close(&s);
391 
392     return 0;
393 }
394