1 /*
2     MPEG Maaate: An Australian MPEG audio analysis toolkit
3     Copyright (C) 2000 Commonwealth Scientific and Industrial Research Organisation
4     (CSIRO), Australia.
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 
21 #include "MPEGfile.H"
22 #include "SOUNDfile.H"
23 
24 #include <float.h>
25 
26 using namespace std;
27 
28 ///// TODOs: /////
29 // - output time step/resolution for each extracted parameter
30 // - output in xwaves format
31 
32 
33 // parameters to control what is to be extracted
34 static float from          = 0.0;      // start at second 0.0
35 static float to            = FLT_MAX;  // end at end of file
36 static bool  verbose       = false;
37 static short printHeader   = -1;       // -1=no 0=first 1=all
38 static short printSideinfo = -1;       // -1=no 0=first 1=all
39 static bool  printDur      = false;    // total file duration
40 static short fromSb        = 0;        // first subband to be extracted
41 static short toSb          = 576;      // last subband to be extracted
42 static short channels      = 1;        // mono/stereo processing?
43 static bool  wantBitalloc  = false;    // want bitallocation?
44 static bool  wantScf       = false;    // want scalefactors?
45 static bool  wantScfsi     = false;    // want scalefactor select info?
46 static bool  wantValues    = false;    // want frequency values?
47 static bool  wantPcm       = false;    // want pcm values?
48 static bool  wantPcmFile   = false;    // want pcm output file?
49 static string outfile      = string("out.raw"); // name of pcm output file
50 static char *filename      = NULL;     // mpeg-audio-file
51 
52 static FILE     *outpcm = NULL;
53 static SOUNDfile *sfile = NULL;
54 static MPEGfile *infile = NULL;
55 
56 // prints help information about parameter usage
57 void
PrintUsage(char * prog)58 PrintUsage(char *prog) {
59     cerr << "\nUsage:" << endl;
60     cerr << prog << " [Options] <mpeg-audio-file>" << endl;
61     cerr << "Default Options: -S 0.0 -E eof" << endl;
62     cerr << "                 (i.e. analyse whole file)" << endl;
63     cerr << endl;
64     cerr << "Possible Options:" << endl;
65     cerr << "-h         help=prints this information" << endl;
66     cerr << "-S <from>  the second (float) to start analysing from" <<endl;
67     cerr << "           Default: 0.0" << endl;
68     cerr << "-E <to>    the second (float) to stop analysing at" << endl;
69     cerr << "           Default: end of file" << endl;
70     cerr << "-V         verbose" << endl;
71     cerr << "-H <0/1>   0=gives header info on first audio frame" << endl;
72     cerr << "           1=gives header info on all audio frames" << endl;
73     cerr << "           Default: 0" << endl;
74     cerr << "-I <0/1>   0=gives Layer III side info on first frame" <<endl;
75     cerr << "           1=gives Layer III side info on all frames" << endl;
76     cerr << "           Default: 0" << endl;
77     cerr << "-D         calculates total duration of audio file" << endl;
78     cerr << "-B <int> <int> subband range that gets extracted" << endl;
79     cerr << "           (only required with -X" << endl;
80     cerr << "           Default: 0 31 (Layers I, II)" << endl;
81     cerr << "                    0 575 (Layer III)" << endl;
82     cerr << "-C <1/2>   1=extract only first channel" << endl;
83     cerr << "           2=extract both channels (if existant)" << endl;
84     cerr << "           Default: 1" << endl;
85     cerr << "-X [field] extracts info from fields from audio file" << endl;
86     cerr << "           Possible field types depend on the Layer" << endl;
87     cerr << "           at which the audio file is encoded:" << endl;
88     cerr << "   bitalloc (Layers I, II) -B fromsb tosb -C 1/2" << endl;
89     cerr << "   scalefac (Layers I, II, III) -B fromsb tosb -C 1/2" <<endl;
90     cerr << "   scfsi    (Layers II, III) -B fromsb tosb -C 1/2" << endl;
91     cerr << "   freqval  (Layers I, II, III) -B fromsb tosb -C 1/2" <<endl;
92     cerr << "   pcmval   (Layers I, II, III) -C 1/2" << endl;
93     cerr << "-O <file>  output pcm samples to file" << endl;
94     cerr << "           Default: file=out.raw" << endl;
95     exit (1);
96 }
97 
98 
99 void
ParseArguments(int argc,char ** argv)100 ParseArguments (int argc, char **argv) {
101     // go through parameters and check values
102     for (int i=1; i<argc; i++) {
103 	if (argv[i][0] == '-' && argv[i][1]) {
104 	    switch (argv[i][1]) {
105 	    case 'h': // help
106 		PrintUsage(argv[0]);
107 		break;
108 	    case 'S': case 's':// second to start from
109 		if ((i+1<argc) && !isalpha(argv[i+1][0])) {
110 		    from = atof(argv[++i]);
111 		}
112 		break;
113 	    case 'E': case 'e':// second to end at
114 		if ((i+1<argc) && !isalpha(argv[i+1][0])) {
115 		    to = atof(argv[++i]);
116 		}
117 		break;
118 	    case 'V': case 'v':// verbose
119 	        verbose = true;
120 	        break;
121 	    case 'H': // header
122 	        printHeader = 0;
123 		if ((i+1<argc) && !isalpha(argv[i+1][0])) {
124 		    printHeader = atoi(argv[++i]);
125 		}
126 		break;
127 	    case 'I': case 'i':// side information
128 	        printSideinfo = 0;
129 		if ((i+1<argc) && !isalpha(argv[i+1][0])) {
130 		    printSideinfo = atoi(argv[++i]);
131 		}
132 		break;
133 	    case 'D': case 'd':// total duration
134 	        printDur = true;
135 	        break;
136 	    case 'B': case 'b':// subband range
137 		if ((i+2<argc) && !isalpha(argv[i+1][0])
138 		    && !isalpha(argv[i+2][0])) {
139 		    fromSb = atoi(argv[++i]);
140 		    toSb   = atoi(argv[++i]);
141 		} else {
142 		    cerr << "inspectMPaudio: -B option needs two integers to follow"
143 			 << endl;
144 		    exit(1);
145 		}
146 		break;
147 	    case 'C': case 'c':// channels
148 		if ((i+1<argc) && !isalpha(argv[i+1][0])) {
149 		    channels = atoi(argv[++i]);
150 		}
151 		break;
152 	    case 'X': case 'x':// fields
153 		if ((i+1 == argc) || !isalpha(argv[i+1][0])) {
154 		    cerr << "inspectMPaudio: -X option needs string-argument"
155 			 << endl;
156 		    exit(1);
157 		} else {
158 		    if (!strncmp(argv[i+1],"bitalloc",3)) {
159 			wantBitalloc = true; i++;
160 		    } else if (!strncmp(argv[i+1],"scalefac",3)) {
161 			wantScf = true; i++;
162 		    } else if (!strncmp(argv[i+1],"scfsi",3)) {
163 			wantScfsi = true; i++;
164 		    } else if (!strncmp(argv[i+1],"freqval",3)) {
165 			wantValues = true; i++;
166 		    } else if (!strncmp(argv[i+1],"pcmval",3)) {
167 			wantPcm = true; i++;
168 		    } else if (argv[i+1][0] != '-') {
169 			cerr << "inspectMPaudio: -X unknown string-argument "
170 			     << argv[i+1] << endl;
171 			exit(1);
172 		    }
173 		}
174 		break;
175 	    case 'O': case 'o': case '0': // output pcm samples to file
176 	        wantPcmFile = true;
177 		if ((i+1<argc) && isalpha(argv[i+1][0])
178 		    && (argv[i+1][0] != '-')) {
179 		    outfile = string(argv[++i]);
180 		}
181 		break;
182 	    } // switch
183 	} else {
184 	    filename = argv[i];
185 	}
186     }
187 }
188 
189 
190 void
CheckArguments()191 CheckArguments( ) {
192 #ifdef DEBUG
193     cout << "Parameter Settings:"             << endl;
194     cout << "From="          << from          << endl;
195     cout << "To="            << to            << endl;
196     cout << "Verbose="       << verbose       << endl;
197     cout << "printHeader="   << printHeader   << endl;
198     cout << "printSideinfo=" << printSideinfo << endl;
199     cout << "printDur="      << printDur      << endl;
200     cout << "fromSb="        << fromSb        << endl;
201     cout << "toSb="          << toSb          << endl;
202     cout << "channels="      << channels      << endl;
203     cout << "wantBitalloc="  << wantBitalloc  << endl;
204     cout << "wantScf="       << wantScf       << endl;
205     cout << "wantScfsi="     << wantScfsi     << endl;
206     cout << "wantValues="    << wantValues    << endl;
207     cout << "wantPcm="       << wantPcm       << endl;
208     cout << "wantPcmFile="   << wantPcmFile   << endl;
209     cout << "outfile="       << outfile       << endl;
210     cout << "filename="      << filename      << endl;
211 #endif
212     if (filename == NULL) {
213 	cerr << "inspectMPaudio: Mpeg audio filename missing" << endl;
214 	exit(1);
215     }
216     if (channels < 1)  channels = 1;
217     if (channels > 2)  channels = 2;
218     if (fromSb   < 0)  fromSb   = 0;
219     if (toSb < fromSb) toSb     = fromSb;
220     if (printSideinfo > 1) printSideinfo = 1;
221     if (printHeader   > 1) printHeader   = 1;
222     if (from < 0.0)    from = 0.0;
223     if (to   < from)   to   = from;
224 }
225 
226 
227 int
main(int argc,char ** argv)228 main(int argc, char **argv) {
229 
230     // check no. of args
231     if (argc < 2) {
232 	PrintUsage(argv[0]);
233     }
234 
235     // parse arguments
236     ParseArguments (argc, argv);
237 
238     // check arguments and correct if appropriate
239     CheckArguments ();
240 
241     // open file
242     sfile = new SOUNDfile(filename);
243 
244     if (sfile->file_type() == MPEG) {
245     infile = (MPEGfile *) sfile->format;
246     } else {
247       infile = NULL;
248     }
249     if (verbose) {
250 	cout << "Audio file opened: " << sfile->file() << endl;
251     }
252 
253     // open pcm output file
254     if (wantPcmFile) {
255 	outpcm=fopen(outfile.c_str(),"wb");
256     }
257 
258     // position at start second
259     if (!sfile->seek_time(from)) {
260 	cerr << "inspectMPaudio: error positioning at starting sec ("
261 	     << from << ") failed." << endl;
262 	exit(1);
263     }
264     if (verbose) cout << "starting at " << sfile->at_time() << " s" << endl;
265 
266 
267     // go through file parsing and displaying
268     int ch = 0;
269 
270     while(sfile->data_available() && (sfile->at_time() <= to)) {
271       // parse next frame
272       if ( wantPcm||wantPcmFile) {
273 	infile->goTo_nextFrame(PCM);
274       } else {
275 	sfile->next_window(PCM);
276       }
277 	// print header if requested
278 	if (printHeader >= 0 && sfile->file_type() == MPEG) {
279 	    infile->printheader();
280 	    if (printHeader == 0) printHeader = -1;
281 	}
282 
283 	// print side info if requested
284 	if (printSideinfo >= 0 && sfile->file_type() == MPEG) {
285 	    infile->printSideinfo();
286 	    if (printSideinfo == 0) printSideinfo = -1;
287 	}
288 
289 	// check requested subbands for this frame
290 	int fromSub = fromSb;
291 	int toSub   = toSb;
292 	if (sfile->file_type() == MPEG) {
293 	if (infile->layer() == III) {
294 	    if (fromSub > 576) fromSub = 576;
295 	    if (toSub   > 576) toSub   = 576;
296 	} else {
297 	    if (fromSub > SBLIMIT) fromSub = SBLIMIT;
298 	    if (toSub   > SBLIMIT) toSub   = SBLIMIT;
299 	}
300 	}
301 
302 	// check channels
303 	ch = min ((int) channels, sfile->channels());
304 
305 	// print requested information:
306 
307 	// Bitallocation information
308 	if (wantBitalloc) {
309 	  if (sfile->file_type() == MPEG) {
310 	    if (infile->layer() == III) {
311 		cerr << "inspectMPaudio: Layer III has no bitallocation information"
312 		     << endl;
313 	    } else {
314 		for (int i=fromSub; i<toSub; i++) { // subbands
315 		    for (int j=0; j<ch; j++) { // channels
316 			cout << "Allocation["<<j<<","<<i<<"]="
317 			     <<infile->bitallocation(j,i)<<endl;
318 		    }
319 		}
320 	    }
321 	  } else {
322 	    cout << "Not an MPEG file: no bitallocation available."<<endl;
323 	  }
324 	}
325 
326 	// Scalefactors
327 	if (wantScf) {
328 	  if (sfile->file_type() == MPEG) {
329 	    // print scalefactors
330 	    for (int i=fromSub; i<toSub; i++) { // subbands
331 	      for (int j=0; j<ch; j++) { // channels
332 		cout << "Scalefactor["<<j<<","<<i<<"]="
333 		     <<infile->scalefactor(j,i)<<endl;
334 	      }
335 	    }
336 	  } else {
337 	    cout << "Not an MPEG file: no scakefactors available."<<endl;
338 	  }
339 	}
340 
341 	// Scalefactor selection information
342 	if (wantScfsi) {
343 	  if (sfile->file_type() == MPEG) {
344 	    if (infile->layer() == I) {
345 		cerr << "inspectMPaudio: Layer I has no scalefactor selection information"
346 		     << endl;
347 	    } else {
348 		if (infile->layer() != III) {
349 		    for (int i=fromSub; i<toSub; i++) { // subbands
350 			for (int j=0; j<ch; j++) { // channels
351 			    cout << "Scfsi["<<j<<","<<i<<"]="
352 				 <<infile->scfsi(j,i)<<endl;
353 			}
354 		    }
355 		} else { // layer III
356 		    for (int i=0; i<4; i++) {
357 			for (int j=0; j<ch; j++) { // channels
358 			    cout << "Scfsi["<<j<<","<<i<<"]="
359 				 << infile->scfsi(j,i) <<endl;
360 			}
361 		    }
362 		}
363 	    }
364 	  } else {
365 	    cout << "Not an MPEG file: no scfis available."<<endl;
366 	  }
367 
368 	}
369 
370 	// Frequency values (spectral values)
371 	if (wantValues) {
372 	  //no. samples
373 	  for (unsigned int i=0; i<sfile->timeticks(HIGH); i++) {
374 	    for (int sb=fromSub; sb<toSub; sb++) { //subbands
375 	      for (int j=0; j<ch; j++) { //channels
376 		    cout << "FreqVal[ch="<<j<<",sb="<<sb<<",timeticks="
377 			 <<i<<"]="
378 			 <<sfile->freq_value(j,sb,i,HIGH)<<endl;
379 	      }
380 	    }
381 	  }
382 	  /*
383 	    if (infile->layer() != III) {
384 	      for (int s=0; s<12; s++) { // no. samples
385 		for (int i=fromSub; i<toSub; i++) { // subbands
386 		  for (int j=0; j<ch; j++) { // channels
387 		    //				cout << "Sample["<<j<<","<<i<<","
388 		    //				     <<s<<","<<k<<"]="
389 		    //				     <<infile->sample(j,i,s,k)<<endl;
390 		    cout << "FreqVal["<<j<<","<<i<<","
391 			 <<s<<"]="
392 			 <<infile->restored_sample(j,i,s)<<endl;
393 		  }
394 		}
395 	      }
396 	    } else { // Layer III (can it be integrated with others?)
397 	      for (int j=0; j<ch; j++) { // channels
398 		for (int sb=fromSub; sb<toSub; sb++) {
399 		  int subb = sb/SSLIMIT;
400 		  int ss   = sb%SSLIMIT;
401 		  //			    cout << "Sample["<<j<<","<<gr<<","
402 		  //				 << sb<<"]="
403 		  //				 << infile->sample(j,gr,subb,ss)
404 		  //				 << endl;
405 		  cout << "Restored_Sample["<<j<<","
406 		       << subb<<","<<ss<<"]="
407 		       << infile->restored_sample(j,subb,ss)
408 		       << endl;
409 		}
410 	      }
411 	    }
412 	  */
413 	}
414 
415 
416 
417 	// PCM samples
418 	if (wantPcm||wantPcmFile) {
419 
420 	  for (unsigned int i=0;
421 	       i<(sfile->timeticks(HIGH)*sfile->nb_subbands(HIGH));
422 	       i++) {
423 	    for (int j=0; j<ch; j++) { //channels
424 	      cout << "pcm[ch="<<j<<",i="<<i<<"]="
425 		   <<sfile->pcm(j,i)<<endl;
426 	      }
427 	    }
428 	  }
429 
430 	/*
431 	  if (infile->layer() != III) {
432 	    int kmax=1; // Layer I
433 	    short sample;
434 	    if (infile->layer() == II) kmax=3; // Layer II
435 	    for (int s=0; s<12; s++) { // no. samples
436 	      for (int k=0; k<kmax; k++) {
437 		for (int i=fromSub; i<toSub; i++) { // subbands
438 		  for (int j=0; j<ch; j++) {
439 				if (wantPcm) {
440 				  cout << "PCM["<<j<<","<<i<<","
441 				       <<s<<","<<k<<"]="
442 				       <<infile->pcm_sample(j,i,s,k)<<endl;
443 				} else { // wantPcmFile
444 				  sample = infile->pcm_sample(j,i,s,k);
445 				  fwrite(&sample, 1, 2, outpcm);
446 				}
447 		  }
448 		}
449 	      }
450 	    }
451 	  } else { // Layer III (can it be integrated with others?)
452 	    short sample;
453 	    for (int gr=0; gr<(2-infile->version()); gr++) {
454 	      for (int sb=fromSub; sb<toSub; sb++) {
455 		int subb = sb%SBLIMIT;
456 		int ss   = sb/SBLIMIT;
457 		for (int j=0; j<ch; j++) { // channels
458 		  if (wantPcm) {
459 		    cout << "PCM_Sample["<<j<<","<<gr<<","
460 			 <<ss <<","<< subb<<"]="
461 			 <<infile->pcm_sample(j,gr,subb,ss)<<endl;
462 		  } else { // wantPcmFile
463 		    sample = infile->pcm_sample(j,gr,subb,ss);
464 		    fwrite(&sample, 1, 2, outpcm);
465 		  }
466 		}
467 	      }
468 	    }
469 	  }
470 	}
471 
472 
473 	// Layer 3
474 	// side information
475 	/*
476 	for (int ch=0; ch<infile->channels(); ch++) {
477 	    for (int i=0; i<4; i++) {
478 		cout << "Scfsi["<<ch<<","<<i<<"]="
479 		     << infile->scfsi(ch,i) <<endl;
480 	    }
481 	    for (int gr=0; gr<(2-infile->version()); gr++) {
482 		cout << "Part2_3_Length["<<ch<<","<<gr<<"]="
483 		     << infile->part2_3_length(ch,gr) << endl;
484 		cout << "Big_Values["<<ch<<","<<gr<<"]="
485 		     << infile->big_values(ch,gr) << endl;
486 		cout << "Global_Gain["<<ch<<","<<gr<<"]="
487 		     << infile->global_gain(ch,gr) << endl;
488 		cout << "Scalefac_Compress["<<ch<<","<<gr<<"]="
489 		     << infile->scalefac_compress(ch,gr) << endl;
490 		cout << "Window_Switching["<<ch<<","<<gr<<"]="
491 		     << infile->window_switching(ch,gr) << endl;
492 		cout << "Block_Type["<<ch<<","<<gr<<"]="
493 		     << infile->blocktype(ch,gr) << endl;
494 		cout << "Mixedblock["<<ch<<","<<gr<<"]="
495 		     << infile->mixedblock(ch,gr) << endl;
496 		for (int j=0; j<3; j++) {
497 		    cout << "table_select["<<ch<<","<<gr<<","<<j<<"]="
498 			 << infile->table_select(ch,gr,j) << endl;
499 		    cout << "subblock_gain["<<ch<<","<<gr<<","<<j<<"]="
500 			 << infile->subblock_gain(ch,gr,j) << endl;
501 		}
502 		cout << "preflag["<<ch<<","<<gr<<"]="
503 		     << infile->preflag(ch,gr) << endl;
504 		cout << "Scalefac_Scale["<<ch<<","<<gr<<"]="
505 		     << infile->scalefac_scale(ch,gr) << endl;
506 		cout << "Count1Table_select["<<ch<<","<<gr<<"]="
507 		     << infile->count1table_select(ch,gr) << endl;
508 		cout << "region0_samps["<<ch<<","<<gr<<"]="
509 		     << infile->region0_samps(ch,gr) << endl;
510 		cout << "region1_samps["<<ch<<","<<gr<<"]="
511 		     << infile->region1_samps(ch,gr) << endl;
512 		cout << "region2_samps["<<ch<<","<<gr<<"]="
513 		     << infile->region2_samps(ch,gr) << endl;
514 	    }
515 	}
516 	*/
517 
518     }
519 
520     if (printDur) {
521 	cout << "No. of Frames analyzed: " << sfile->at_window() << endl;
522 	cout << "stopped at " << sfile->at_time() << " s" << endl;
523     }
524 
525 
526     // close output sound file
527     if (wantPcmFile) {
528 	fclose (outpcm);
529 	cout << "PCM file written (16 bit linear, "
530 	     << sfile->sampling_rate() << " kHz samplingrate, "
531 	     << ch << " channels): "
532 	     << outfile.c_str() << endl;
533     }
534 
535     exit (0);
536 }
537