1 /* ScummVM Tools
2  *
3  * ScummVM Tools is the legal property of its developers, whose
4  * names are too numerous to list here. Please refer to the
5  * COPYRIGHT file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21 
22 #include <assert.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sstream>
26 #include <stdio.h>
27 
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif /* HAVE_CONFIG_H */
31 
32 #include "compress.h"
33 #include "common/endian.h"
34 
35 #ifdef USE_VORBIS
36 #include <vorbis/vorbisenc.h>
37 #endif
38 #ifdef USE_FLAC
39 #define FLAC__NO_DLL 1
40 #include <FLAC/stream_encoder.h>
41 #endif
42 
43 struct lameparams {
44 	int32 minBitr;
45 	int32 maxBitr;
46 	uint32 targetBitr;
47 	CompressionType type;
48 	uint32 algqual;
49 	uint32 vbrqual;
50 	bool silent;
51 	std::string lamePath;
52 };
53 
54 struct oggencparams {
55 	int nominalBitr;
56 	int minBitr;
57 	int maxBitr;
58 	float quality;
59 	bool silent;
60 };
61 
62 struct flaccparams {
63 	int compressionLevel;
64 	int blocksize;
65 	bool verify;
66 	bool silent;
67 };
68 
69 struct rawtype {
70 	bool isLittleEndian, isStereo;
71 	uint8 bitsPerSample;
72 };
73 
74 lameparams lameparms = { -1, -1, 32, VBR, algqualDef, vbrqualDef, 0, "lame" };
75 oggencparams oggparms = { -1, -1, -1, (float)oggqualDef, 0 };
76 flaccparams flacparms = { flacCompressDef, flacBlocksizeDef, false, false };
77 rawtype	rawAudioType = { false, false, 8 };
78 
79 const char *tempEncoded = TEMP_MP3;
80 
setRawAudioType(bool isLittleEndian,bool isStereo,uint8 bitsPerSample)81 void CompressionTool::setRawAudioType(bool isLittleEndian, bool isStereo, uint8 bitsPerSample) {
82 	rawAudioType.isLittleEndian = isLittleEndian;
83 	rawAudioType.isStereo = isStereo;
84 	rawAudioType.bitsPerSample = bitsPerSample;
85 }
86 
getSampleRateFromVOCRate(int vocSR)87 int getSampleRateFromVOCRate(int vocSR) {
88 	if (vocSR == 0xa5 || vocSR == 0xa6 || vocSR == 0x83) {
89 		return 11025;
90 	} else if (vocSR == 0xd2 || vocSR == 0xd3) {
91 		return 22050;
92 	} else {
93 		int sr = 1000000L / (256L - vocSR);
94 		/* inexact sampling rates occur e.g. in the kitchen in Monkey
95 		 * Island, very easy to reach right from the start of the game.
96 		 * warning("inexact sample rate used: %i (0x%x)", sr, vocSR);
97 		 */
98 		return sr;
99 	}
100 }
101 
102 /* map frequency to a valid MP3 sample frequency
103  *
104  * Robert Hegemann 2000-07-01
105  *
106  * Copied from lame 3.96.1
107  */
map2MP3Frequency(int freq)108 static int map2MP3Frequency(int freq) {
109     if (freq <=  8000) return  8000;
110     if (freq <= 11025) return 11025;
111     if (freq <= 12000) return 12000;
112     if (freq <= 16000) return 16000;
113     if (freq <= 22050) return 22050;
114     if (freq <= 24000) return 24000;
115     if (freq <= 32000) return 32000;
116     if (freq <= 44100) return 44100;
117 
118     return 48000;
119 }
120 
encodeAudio(const char * inname,bool rawInput,int rawSamplerate,const char * outname,AudioFormat compmode)121 void CompressionTool::encodeAudio(const char *inname, bool rawInput, int rawSamplerate, const char *outname, AudioFormat compmode) {
122 	bool err = false;
123 	char fbuf[2048];
124 	char *tmp = fbuf;
125 
126 	if (compmode == AUDIO_MP3) {
127 		tmp += sprintf(tmp, "%s -t ", lameparms.lamePath.c_str());
128 		if (rawInput) {
129 			tmp += sprintf(tmp, "-r ");
130 			tmp += sprintf(tmp, "--bitwidth %d ", rawAudioType.bitsPerSample);
131 
132 			if (rawAudioType.isLittleEndian) {
133 				tmp += sprintf(tmp, "--little-endian ");
134 			} else {
135 				tmp += sprintf(tmp, "--big-endian ");
136 			}
137 
138 			tmp += sprintf(tmp, (rawAudioType.isStereo ? "-m j " : "-m m "));
139 			tmp += sprintf(tmp, "-s %d ", rawSamplerate);
140 		}
141 
142 		if (lameparms.type == CBR)
143 			tmp += sprintf(tmp, "--cbr -b %d ", lameparms.targetBitr);
144 		else {
145 			if (lameparms.type == ABR)
146 				tmp += sprintf(tmp, "--abr %d ", lameparms.targetBitr);
147 			else
148 				tmp += sprintf(tmp, "--vbr-new -V %d ", lameparms.vbrqual);
149 
150 			if (lameparms.minBitr != -1)
151 				tmp += sprintf(tmp, "-b %d ", lameparms.minBitr);
152 			if (lameparms.maxBitr != -1)
153 				tmp += sprintf(tmp, "-B %d ", lameparms.maxBitr);
154 		}
155 
156 		/* Explicitly specify a target sample rate, to work around a bug (?)
157 		* in newer lame versions (>= 3.95) which causes it to malfunction
158 		* for odd sample rates when in VBR mode. See also bug #934026.
159 		* We essentially duplicate the old behaviour of lame (found in e.g.
160 		* version 3.93.1): we round the input sample rate up to the next
161 		* higher valid MP3 sample rate, with a margin of 3%.
162 		*/
163 		if (rawSamplerate != -1) {
164 			tmp += sprintf(tmp, "--resample %d ", map2MP3Frequency(97 * rawSamplerate / 100));
165 		}
166 
167 		if (lameparms.silent) {
168 			tmp += sprintf(tmp, " --silent ");
169 		}
170 
171 		tmp += sprintf(tmp, "-q %d ", lameparms.algqual);
172 
173 		tmp += sprintf(tmp, "\"%s\" \"%s\" ", inname, outname);
174 
175 		err = spawnSubprocess(fbuf) != 0;
176 
177 		if (err) {
178 			char buf[2048];
179 			sprintf(buf, "Error in MP3 encoder.(check parameters) \nMP3 Encoder Commandline:%s\n", fbuf);
180 			throw ToolException(buf, err);
181 		} else {
182 			return;
183 		}
184 	}
185 
186 #ifndef USE_VORBIS
187 	if (compmode == AUDIO_VORBIS) {
188 		tmp += sprintf(tmp, "oggenc ");
189 		if (rawInput) {
190 			tmp += sprintf(tmp, "--raw ");
191 			tmp += sprintf(tmp, "--raw-chan=%d ", (rawAudioType.isStereo ? 2 : 1));
192 			tmp += sprintf(tmp, "--raw-bits=%d ", rawAudioType.bitsPerSample);
193 			tmp += sprintf(tmp, "--raw-rate=%d ", rawSamplerate);
194 			tmp += sprintf(tmp, "--raw-endianness=%d ", (rawAudioType.isLittleEndian ? 0 : 1));
195 		}
196 
197 		if (oggparms.nominalBitr != -1) {
198 			tmp += sprintf(tmp, "--bitrate=%d ", oggparms.nominalBitr);
199 		} else {
200 			tmp += sprintf(tmp, "--quality=%f ", oggparms.quality);
201 		}
202 
203 		if (oggparms.minBitr != -1) {
204 			tmp += sprintf(tmp, "--min-bitrate=%d ", oggparms.minBitr);
205 		}
206 
207 		if (oggparms.maxBitr != -1) {
208 			tmp += sprintf(tmp, "--max-bitrate=%d ", oggparms.maxBitr);
209 		}
210 
211 		if (oggparms.silent) {
212 			tmp += sprintf(tmp, "--quiet ");
213 		}
214 
215 		tmp += sprintf(tmp, "--output=\"%s\" ", outname);
216 		tmp += sprintf(tmp, "\"%s\" ", inname);
217 
218 		err = spawnSubprocess(fbuf) != 0;
219 
220 		if (err) {
221 			char buf[2048];
222 			sprintf(buf, "Error in Vorbis encoder. (check parameters)\nVorbis Encoder Commandline:%s\n", fbuf);
223 			throw ToolException(buf, err);
224 		} else {
225 			return;
226 		}
227 	}
228 #endif
229 
230 #ifndef USE_FLAC
231 	if (compmode == AUDIO_FLAC) {
232 		/* --lax is needed to allow 11kHz, we dont need place for meta-tags, and no seektable */
233 		/* -f is reqired to force override of unremoved temp file. See bug #1294648 */
234 		tmp += sprintf(tmp, "flac -f --lax --no-padding --no-seektable --no-ogg ");
235 
236 		if (rawInput) {
237 			tmp += sprintf(tmp, "--force-raw-format ");
238 			tmp += sprintf(tmp, "--sign=%s ", ((rawAudioType.bitsPerSample == 8) ? "unsigned" : "signed"));
239 			tmp += sprintf(tmp, "--channels=%d ", (rawAudioType.isStereo ? 2 : 1));
240 			tmp += sprintf(tmp, "--bps=%d ", rawAudioType.bitsPerSample);
241 			tmp += sprintf(tmp, "--sample-rate=%d ", rawSamplerate);
242 			tmp += sprintf(tmp, "--endian=%s ", (rawAudioType.isLittleEndian ? "little" : "big"));
243 		}
244 
245 		if (flacparms.silent) {
246 			tmp += sprintf(tmp, "--silent ");
247 		}
248 
249 		if (flacparms.verify) {
250 			tmp += sprintf(tmp, "--verify ");
251 		}
252 
253 		tmp += sprintf(tmp, "--compression-level-%d ", flacparms.compressionLevel);
254 		tmp += sprintf(tmp, "-b %d ", flacparms.blocksize);
255 		tmp += sprintf(tmp, "-o \"%s\" ", outname);
256 		tmp += sprintf(tmp, "\"%s\" ", inname);
257 
258 		err = spawnSubprocess(fbuf) != 0;
259 
260 		if (err) {
261 			char buf[2048];
262 			sprintf(buf, "Error in FLAC encoder. (check parameters)\nFLAC Encoder Commandline:%s\n", fbuf);
263 			throw ToolException(buf, err);
264 		} else {
265 			return;
266 		}
267 	}
268 #endif
269 	if (rawInput) {
270 		long length;
271 		char *rawData;
272 
273 		Common::File inputRaw(inname, "rb");
274 		length = inputRaw.size();
275 		rawData = (char *)malloc(length);
276 		inputRaw.read_throwsOnError(rawData, length);
277 
278 		encodeRaw(rawData, length, rawSamplerate, outname, compmode);
279 
280 		free(rawData);
281 	} else {
282 		int fmtHeaderSize, length, numChannels, sampleRate, bitsPerSample;
283 		char *wavData;
284 
285 		Common::File inputWav(inname, "rb");
286 
287 		/* Standard PCM fmt header is 16 bits, but at least Simon 1 and 2 use 18 bits */
288 		inputWav.seek(16, SEEK_SET);
289 		fmtHeaderSize = inputWav.readUint32LE();
290 
291 		inputWav.seek(22, SEEK_SET);
292 		numChannels = inputWav.readUint16LE();
293 		sampleRate = inputWav.readUint32LE();
294 
295 		inputWav.seek(34, SEEK_SET);
296 		bitsPerSample = inputWav.readUint16LE();
297 
298 		/* The size of the raw audio is after the RIFF chunk (12 bytes), fmt chunk (8 + fmtHeaderSize bytes), and data chunk id (4 bytes) */
299 		inputWav.seek(24 + fmtHeaderSize, SEEK_SET);
300 		length = inputWav.readUint32LE();
301 
302 		wavData = (char *)malloc(length);
303 		inputWav.read_throwsOnError(wavData, length);
304 
305 		setRawAudioType(true, numChannels == 2, (uint8)bitsPerSample);
306 		encodeRaw(wavData, length, sampleRate, outname, compmode);
307 
308 		free(wavData);
309 	}
310 }
311 
encodeRaw(const char * rawData,int length,int samplerate,const char * outname,AudioFormat compmode)312 void CompressionTool::encodeRaw(const char *rawData, int length, int samplerate, const char *outname, AudioFormat compmode) {
313 	print(" - len=%ld, ch=%d, rate=%d, %dbits", length, (rawAudioType.isStereo ? 2 : 1), samplerate, rawAudioType.bitsPerSample);
314 
315 #ifdef USE_VORBIS
316 	if (compmode == AUDIO_VORBIS) {
317 		char outputString[256] = "";
318 		int numChannels = (rawAudioType.isStereo ? 2 : 1);
319 		int totalSamples = length / ((rawAudioType.bitsPerSample / 8) * numChannels);
320 		int samplesLeft = totalSamples;
321 		int eos = 0;
322 		int totalBytes = 0;
323 
324 		vorbis_info vi;
325 		vorbis_comment vc;
326 		vorbis_dsp_state vd;
327 		vorbis_block vb;
328 
329 		ogg_stream_state os;
330 		ogg_page og;
331 		ogg_packet op;
332 
333 		ogg_packet header;
334 		ogg_packet header_comm;
335 		ogg_packet header_code;
336 
337 		Common::File outputOgg(outname, "wb");
338 
339 		vorbis_info_init(&vi);
340 
341 		if (oggparms.nominalBitr > 0) {
342 			int result = 0;
343 
344 			/* Input is in kbps, function takes bps */
345 			result = vorbis_encode_setup_managed(&vi, numChannels, samplerate, (oggparms.maxBitr > 0 ? 1000 * oggparms.maxBitr : -1), (1000 * oggparms.nominalBitr), (oggparms.minBitr > 0 ? 1000 * oggparms.minBitr : -1));
346 
347 			if (result == OV_EFAULT) {
348 				vorbis_info_clear(&vi);
349 				error("Error: Internal Logic Fault");
350 			} else if ((result == OV_EINVAL) || (result == OV_EIMPL)) {
351 				vorbis_info_clear(&vi);
352 				error("Error: Invalid bitrate parameters");
353 			}
354 
355 			if (!oggparms.silent) {
356 				sprintf(outputString, "Encoding to\n         \"%s\"\nat average bitrate %i kbps (", outname, oggparms.nominalBitr);
357 
358 				if (oggparms.minBitr > 0) {
359 					sprintf(outputString + strlen(outputString), "min %i kbps, ", oggparms.minBitr);
360 				} else {
361 					sprintf(outputString + strlen(outputString), "no min, ");
362 				}
363 
364 				if (oggparms.maxBitr > 0) {
365 					sprintf(outputString + strlen(outputString), "max %i kbps),\nusing full bitrate management engine\nSet optional hard quality restrictions\n", oggparms.maxBitr);
366 				} else {
367 					sprintf(outputString + strlen(outputString), "no max),\nusing full bitrate management engine\nSet optional hard quality restrictions\n");
368 				}
369 			}
370 		} else {
371 			int result = 0;
372 
373 			/* Quality input is -1 - 10, function takes -0.1 through 1.0 */
374 			result = vorbis_encode_setup_vbr(&vi, numChannels, samplerate, oggparms.quality * 0.1f);
375 
376 			if (result == OV_EFAULT) {
377 				vorbis_info_clear(&vi);
378 				error("Internal Logic Fault");
379 			} else if ((result == OV_EINVAL) || (result == OV_EIMPL)) {
380 				vorbis_info_clear(&vi);
381 				error("Invalid bitrate parameters");
382 			}
383 
384 			if (!oggparms.silent) {
385 				sprintf(outputString, "Encoding to\n         \"%s\"\nat quality %2.2f", outname, oggparms.quality);
386 			}
387 
388 			if ((oggparms.minBitr > 0) || (oggparms.maxBitr > 0)) {
389 				struct ovectl_ratemanage_arg extraParam;
390 				vorbis_encode_ctl(&vi, OV_ECTL_RATEMANAGE_GET, &extraParam);
391 
392 				extraParam.bitrate_hard_min = (oggparms.minBitr > 0 ? (1000 * oggparms.minBitr) : -1);
393 				extraParam.bitrate_hard_max = (oggparms.maxBitr > 0 ? (1000 * oggparms.maxBitr) : -1);
394 				extraParam.management_active = 1;
395 
396 				vorbis_encode_ctl(&vi, OV_ECTL_RATEMANAGE_SET, &extraParam);
397 
398 				if (!oggparms.silent) {
399 					sprintf(outputString + strlen(outputString), " using constrained VBR (");
400 
401 					if (oggparms.minBitr != -1) {
402 						sprintf(outputString + strlen(outputString), "min %i kbps, ", oggparms.minBitr);
403 					} else {
404 						sprintf(outputString + strlen(outputString), "no min, ");
405 					}
406 
407 					if (oggparms.maxBitr != -1) {
408 						sprintf(outputString + strlen(outputString), "max %i kbps)\nSet optional hard quality restrictions\n", oggparms.maxBitr);
409 					} else {
410 						sprintf(outputString + strlen(outputString), "no max)\nSet optional hard quality restrictions\n");
411 					}
412 				}
413 			} else {
414 				sprintf(outputString + strlen(outputString), "\n");
415 			}
416 		}
417 
418 		puts(outputString);
419 
420 		vorbis_encode_setup_init(&vi);
421 		vorbis_comment_init(&vc);
422 		vorbis_analysis_init(&vd, &vi);
423 		vorbis_block_init(&vd, &vb);
424 		ogg_stream_init(&os, 0);
425 		vorbis_analysis_headerout(&vd, &vc, &header, &header_comm, &header_code);
426 
427 		ogg_stream_packetin(&os, &header);
428 		ogg_stream_packetin(&os, &header_comm);
429 		ogg_stream_packetin(&os, &header_code);
430 
431 		while (!eos) {
432 			int result = ogg_stream_flush(&os,&og);
433 
434 			if (result == 0) {
435 				break;
436 			}
437 
438 			outputOgg.write(og.header, og.header_len);
439 			outputOgg.write(og.body, og.body_len);
440 		}
441 
442 		while (!eos) {
443 			int numSamples = ((samplesLeft < 2048) ? samplesLeft : 2048);
444 			float **buffer = vorbis_analysis_buffer(&vd, numSamples);
445 
446 			/* We must tell the encoder that we have reached the end of the stream */
447 			if (numSamples == 0) {
448 				vorbis_analysis_wrote(&vd, 0);
449 			} else {
450 				/* Adapted from oggenc 1.1.1 */
451 				if (rawAudioType.bitsPerSample == 8) {
452 					const byte *rawDataUnsigned = (const byte *)rawData;
453 					for (int i = 0; i < numSamples; i++) {
454 						for (int j = 0; j < numChannels; j++) {
455 							buffer[j][i] = ((int)(rawDataUnsigned[i * numChannels + j]) - 128) / 128.0f;
456 						}
457 					}
458 				} else if (rawAudioType.bitsPerSample == 16) {
459 					if (rawAudioType.isLittleEndian) {
460 						for (int i = 0; i < numSamples; i++) {
461 							for (int j = 0; j < numChannels; j++) {
462 								buffer[j][i] = ((rawData[(i * 2 * numChannels) + (2 * j) + 1] << 8) | (rawData[(i * 2 * numChannels) + (2 * j)] & 0xff)) / 32768.0f;
463 							}
464 						}
465 					} else {
466 						for (int i = 0; i < numSamples; i++) {
467 							for (int j = 0; j < numChannels; j++) {
468 								buffer[j][i] = ((rawData[(i * 2 * numChannels) + (2 * j)] << 8) | (rawData[(i * 2 * numChannels) + (2 * j) + 1] & 0xff)) / 32768.0f;
469 							}
470 						}
471 					}
472 				}
473 
474 				vorbis_analysis_wrote(&vd, numSamples);
475 			}
476 
477 			while (vorbis_analysis_blockout(&vd, &vb) == 1) {
478 				vorbis_analysis(&vb, NULL);
479 				vorbis_bitrate_addblock(&vb);
480 
481 				while (vorbis_bitrate_flushpacket(&vd, &op)) {
482 					ogg_stream_packetin(&os, &op);
483 
484 					while (!eos) {
485 						int result = ogg_stream_pageout(&os, &og);
486 
487 						if (result == 0) {
488 							break;
489 						}
490 
491 						totalBytes += outputOgg.write(og.header, og.header_len);
492 						totalBytes += outputOgg.write(og.body, og.body_len);
493 
494 						if (ogg_page_eos(&og)) {
495 							eos = 1;
496 						}
497 					}
498 				}
499 			}
500 
501 			rawData += 2048 * (rawAudioType.bitsPerSample / 8) * numChannels;
502 			samplesLeft -= 2048;
503 		}
504 
505 		ogg_stream_clear(&os);
506 		vorbis_block_clear(&vb);
507 		vorbis_dsp_clear(&vd);
508 		vorbis_info_clear(&vi);
509 
510 		if (!oggparms.silent) {
511 			print("\nDone encoding file \"%s\"", outname);
512 			print("\n\tFile length:  %dm %ds", (int)(totalSamples / samplerate / 60), (totalSamples / samplerate % 60));
513 			print("\tAverage bitrate: %.1f kb/s\n", (8.0 * (double)totalBytes / 1000.0) / ((double)totalSamples / (double)samplerate));
514 		}
515 	}
516 #endif
517 
518 #ifdef USE_FLAC
519 	if (compmode == AUDIO_FLAC) {
520 		int i;
521 		int numChannels = (rawAudioType.isStereo ? 2 : 1);
522 		int samplesPerChannel = length / ((rawAudioType.bitsPerSample / 8) * numChannels);
523 		FLAC__StreamEncoder *encoder;
524 		FLAC__StreamEncoderInitStatus initStatus;
525 		FLAC__int32 *flacData;
526 
527 		flacData = (FLAC__int32 *)malloc(samplesPerChannel * numChannels * sizeof(FLAC__int32));
528 
529 		if (rawAudioType.bitsPerSample == 8) {
530 			for (i = 0; i < samplesPerChannel * numChannels; i++) {
531 				flacData[i] = (FLAC__int32)(FLAC__uint8)rawData[i] - 0x80;
532 			}
533 		} else if (rawAudioType.bitsPerSample == 16) {
534 			if (rawAudioType.isLittleEndian) {
535 				for (i = 0; i < samplesPerChannel * numChannels; i++) {
536 					flacData[i] = (FLAC__int32)((FLAC__int16)(FLAC__int8)(FLAC__byte)rawData[2 * i + 1] << 8 |
537 								                (FLAC__int16)(FLAC__byte)rawData[2 * i    ]);
538 				}
539 			} else {
540 				for (i = 0; i < samplesPerChannel * numChannels; i++) {
541 					flacData[i] = (FLAC__int32)((FLAC__int16)(FLAC__int8)(FLAC__byte)rawData[2 * i    ] << 8 |
542 								                (FLAC__int16)(FLAC__byte)rawData[2 * i + 1]);
543 				}
544 			}
545 		}
546 
547 		if (!flacparms.silent) {
548 			print("Encoding to\n         \"%s\"\nat compression level %d using blocksize %d\n", outname, flacparms.compressionLevel, flacparms.blocksize);
549 		}
550 
551 		encoder = FLAC__stream_encoder_new();
552 
553 		FLAC__stream_encoder_set_bits_per_sample(encoder, rawAudioType.bitsPerSample);
554 		FLAC__stream_encoder_set_blocksize(encoder, flacparms.blocksize);
555 		FLAC__stream_encoder_set_channels(encoder, numChannels);
556 		FLAC__stream_encoder_set_compression_level(encoder, flacparms.compressionLevel);
557 		FLAC__stream_encoder_set_sample_rate(encoder, samplerate);
558 		FLAC__stream_encoder_set_streamable_subset(encoder, false);
559 		FLAC__stream_encoder_set_total_samples_estimate(encoder, samplesPerChannel);
560 		FLAC__stream_encoder_set_verify(encoder, flacparms.verify);
561 
562 		initStatus = FLAC__stream_encoder_init_file(encoder, outname, NULL, NULL);
563 
564 		if (initStatus != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
565 			char buf[2048];
566 			sprintf(buf, "Error in FLAC encoder. (check the parameters)\nExact error was:%s", FLAC__StreamEncoderInitStatusString[initStatus]);
567 			free(flacData);
568 			throw ToolException(buf);
569 		} else {
570 			FLAC__stream_encoder_process_interleaved(encoder, flacData, samplesPerChannel);
571 		}
572 
573 		FLAC__stream_encoder_finish(encoder);
574 		FLAC__stream_encoder_delete(encoder);
575 
576 		free(flacData);
577 
578 		if (!flacparms.silent) {
579 			print("\nDone encoding file \"%s\"", outname);
580 			print("\n\tFile length:  %dm %ds\n", (int)(samplesPerChannel / samplerate / 60), (samplesPerChannel / samplerate % 60));
581 		}
582 	}
583 #endif
584 }
585 
extractAndEncodeWAV(const char * outName,Common::File & input,AudioFormat compMode)586 void CompressionTool::extractAndEncodeWAV(const char *outName, Common::File &input, AudioFormat compMode) {
587 	unsigned int length;
588 	char fbuf[2048];
589 	size_t size;
590 
591 	input.seek(-4, SEEK_CUR);
592 	length = input.readUint32LE();
593 	length += 8;
594 	input.seek(-8, SEEK_CUR);
595 
596 	/* Copy the WAV data to a temporary file */
597 	Common::File f(outName, "wb");
598 	while (length > 0) {
599 		size = input.read_noThrow(fbuf, length > sizeof(fbuf) ? sizeof(fbuf) : length);
600 		if (size <= 0)
601 			break;
602 		length -= (int)size;
603 		f.write(fbuf, size);
604 	}
605 	f.close();
606 
607 	/* Convert the WAV temp file to OGG/MP3 */
608 	encodeAudio(outName, false, -1, tempEncoded, compMode);
609 }
610 
extractAndEncodeAIFF(const char * inName,const char * outName,AudioFormat compmode)611 void CompressionTool::extractAndEncodeAIFF(const char *inName, const char *outName, AudioFormat compmode) {
612 	// Get sound definition (length, frequency, stereo, ...)
613 	char buf[4];
614 	Common::File inFile(inName, "rb");
615 	inFile.read_throwsOnError(buf, 4);
616 	if (memcmp(buf, "FORM", 4) != 0)
617 		error("Error: AIFF file has no 'FORM' header");
618 	inFile.readUint32BE();
619 	// Only AIFF (uncompressed) is supported, not AIFC
620 	inFile.read_throwsOnError(buf, 4);
621 	if (memcmp(buf, "AIFF", 4) != 0)
622 		error("Error: AIFF file has no 'AIFF' header");
623 
624 	bool foundCOMM = false;
625 	bool foundSSND = false;
626 	uint16 numChannels = 0, bitsPerSample = 0;
627 	uint32 numSampleFrames = 0, offset = 0, blockSize = 0, soundOffset = 0;
628 	uint32 sampleRate = 0;
629 
630 	while ((!foundCOMM || !foundSSND) && !inFile.err() && !inFile.eos()) {
631 
632 		inFile.read_throwsOnError(buf, 4);
633 		uint32 length = inFile.readUint32BE();
634 		uint32 pos = inFile.pos();
635 
636 		if (memcmp(buf, "COMM", 4) == 0) {
637 			foundCOMM = true;
638 			numChannels = inFile.readUint16BE();
639 			numSampleFrames = inFile.readUint32BE();
640 			bitsPerSample = inFile.readUint16BE();
641 			// The sample rate is stored as an "80 bit IEEE Standard 754 floating
642 			// point number (Standard Apple Numeric Environment [SANE] data type
643 			// Extended).
644 			byte rate_buf[10];
645 			uint32 last = 0;
646 			inFile.read_throwsOnError(rate_buf, 10);
647 			sampleRate = READ_BE_UINT32(rate_buf + 2);
648 			byte exp = 30 - rate_buf[1];
649 
650 			while (exp--) {
651 				last = sampleRate;
652 				sampleRate >>= 1;
653 			}
654 
655 			if (last & 0x00000001)
656 				sampleRate++;
657 		} else if (memcmp(buf, "SSND", 4) == 0) {
658 			foundSSND = true;
659 			offset = inFile.readUint32BE();
660 			blockSize = inFile.readUint32BE();
661 			soundOffset = inFile.pos();
662 		}
663 
664 		inFile.seek(pos + length, SEEK_SET);
665 	}
666 	if (!foundCOMM)
667 		error("Error: AIFF file has no 'COMM' chunk in 'AIFF' header");
668 
669 	if (!foundSSND)
670 		error("Error: AIFF file has no 'COMM' chunk in 'SSND' header");
671 
672 	// Only a subset of the AIFF format is supported
673 	if (numChannels < 1 || numChannels > 2)
674 		error("Error: AIFF file has an unsupported number of channels");
675 
676 	if (bitsPerSample != 8 && bitsPerSample != 16)
677 		error("Error: AIFF file has an unsupported number of bits per sample");
678 
679 	if (offset != 0 || blockSize != 0)
680 		error("Error: AIFF file has block-aligned data, which is not supported");
681 
682 	// Get data and write to temporary file
683 	uint32 size = numSampleFrames * numChannels * (bitsPerSample / 8);
684 	inFile.seek(soundOffset, SEEK_SET);
685 	char *aifData = (char *)malloc(size);
686 	inFile.read_throwsOnError(aifData, size);
687 	Common::File tmpFile(TEMP_RAW, "wb");
688 	tmpFile.write(aifData, size);
689 	tmpFile.close();
690 	free(aifData);
691 
692 	// Convert the temporary raw file to MP3/OGG/FLAC
693 	// Samples are always signed, and big endian.
694 	setRawAudioType(false, numChannels == 2, bitsPerSample);
695 	encodeAudio(TEMP_RAW, true, sampleRate, outName, compmode);
696 
697 	// Delete temporary file
698 	Common::removeFile(TEMP_RAW);
699 }
700 
extractAndEncodeVOC(const char * outName,Common::File & input,AudioFormat compMode)701 void CompressionTool::extractAndEncodeVOC(const char *outName, Common::File &input, AudioFormat compMode) {
702 	int bits;
703 	int blocktype;
704 	int channels;
705 	unsigned int length;
706 	int sample_rate;
707 	int comp;
708 	char fbuf[2048];
709 	size_t size;
710 	int real_samplerate = -1;
711 
712 	Common::File f(outName, "wb");
713 
714 	while ((blocktype = input.readByte())) {
715 		if (blocktype != 1 && blocktype != 9) {
716 			/*
717 			   We only generate a warning, instead of erroring out, because
718 			   at least the monster.sou file of Full Throttle contains VOCs
719 			   with an invalid length field (value to small). So we encounter
720 			   the "block types" 0x80, 0x82 etc.. Not sure if there is another
721 			   (maybe even better) way to work around that... ?
722 			 */
723 			warning("Unsupported VOC block type: %02x", blocktype);
724 			break;
725 		}
726 
727 		/* Sound Data */
728 		print(" Sound Data");
729 		length = input.readChar();
730 		length |= input.readChar() << 8;
731 		length |= input.readChar() << 16;
732 
733 		if (blocktype == 1) {
734 			length -= 2;
735 			sample_rate = input.readByte();
736 			comp = input.readByte();
737 			real_samplerate = getSampleRateFromVOCRate(sample_rate);
738 		} else { /* (blocktype == 9) */
739 			length -= 12;
740 			real_samplerate = sample_rate = input.readUint32LE();
741 			bits = input.readChar();;
742 			channels = input.readChar();;
743 			if (bits != 8 || channels != 1) {
744 				error("Unsupported VOC file format (%d bits per sample, %d channels)", bits, channels);
745 			}
746 			comp = input.readUint16LE();
747 			input.readUint32LE();
748 		}
749 
750 		print(" - length = %d", length);
751 		print(" - sample rate = %d (%02x)", real_samplerate, sample_rate);
752 		print(" - compression = %s (%02x)",
753 			   (comp ==	   0 ? "8bits"   :
754 				(comp ==   1 ? "4bits"   :
755 				 (comp ==  2 ? "2.6bits" :
756 				  (comp == 3 ? "2bits"   :
757 								"Multi")))), comp);
758 
759 		if (comp != 0) {
760 			error("Cannot handle compressed VOC data");
761 		}
762 
763 		/* Copy the raw data to a temporary file */
764 		while (length > 0) {
765 			size = input.read_noThrow(fbuf, length > sizeof(fbuf) ? sizeof(fbuf) : (uint32)length);
766 
767 			if (size <= 0) {
768 				break;
769 			}
770 
771 			length -= (int)size;
772 			f.write(fbuf, size);
773 		}
774 	}
775 
776 	f.close();
777 
778 	assert(real_samplerate != -1);
779 
780 	setRawAudioType(false, false, 8);
781 
782 	/* Convert the raw temp file to OGG/MP3 */
783 	encodeAudio(outName, true, real_samplerate, tempEncoded, compMode);
784 }
785 
786 // mp3 settings
setMp3LamePath(const std::string & arg)787 void CompressionTool::setMp3LamePath(const std::string& arg) {
788 	lameparms.lamePath = arg;
789 }
790 
setMp3CompressionType(const std::string & arg)791 void CompressionTool::setMp3CompressionType(const std::string& arg) {
792 	if (arg == "CBR")
793 		lameparms.type = CBR;
794 	else if (arg == "ABR")
795 		lameparms.type = ABR;
796 	else
797 		lameparms.type = VBR;
798 }
799 
setMp3CompressionType(CompressionType type)800 void CompressionTool::setMp3CompressionType(CompressionType type) {
801 	lameparms.type = type;
802 }
803 
setMp3MpegQuality(const std::string & arg)804 void CompressionTool::setMp3MpegQuality(const std::string& arg) {
805 	lameparms.algqual = atoi(arg.c_str());
806 
807 	if (lameparms.algqual == 0 && arg != "0")
808 		throw ToolException("Quality (-q) must be a number.");
809 
810 	if (lameparms.algqual > 9)
811 		throw ToolException("Quality (-q) out of bounds, must be between 0 and 9.");
812 }
813 
setMp3TargetBitrate(const std::string & arg)814 void CompressionTool::setMp3TargetBitrate(const std::string& arg) {
815 	lameparms.targetBitr = atoi(arg.c_str());
816 
817 	if (lameparms.targetBitr == 0 && arg != "0")
818 		throw ToolException("Target bitrate must be a number.");
819 
820 	if (lameparms.targetBitr < 8 || lameparms.targetBitr > 160)
821 		throw ToolException("Target bitrate out of bounds, must be between 8 and 160.");
822 }
823 
setMp3MinBitrate(const std::string & arg)824 void CompressionTool::setMp3MinBitrate(const std::string& arg) {
825 	lameparms.minBitr = atoi(arg.c_str());
826 
827 	if (lameparms.minBitr == 0 && arg != "0")
828 		throw ToolException("Minimum bitrate (-b) must be a number.");
829 
830 	if ((lameparms.minBitr % 8) != 0)
831 		lameparms.minBitr -= lameparms.minBitr % 8;
832 	if (lameparms.minBitr > 64 && (lameparms.minBitr % 16) != 0)
833 		lameparms.minBitr -= lameparms.minBitr % 16;
834 
835 	if (lameparms.minBitr < 8 || lameparms.minBitr > 160)
836 		throw ToolException("Minimum bitrate out of bounds (-b), must be between 8 and 160.");
837 }
838 
setMp3MaxBitrate(const std::string & arg)839 void CompressionTool::setMp3MaxBitrate(const std::string& arg) {
840 	lameparms.maxBitr = atoi(arg.c_str());
841 
842 	if (lameparms.maxBitr == 0 && arg != "0")
843 		throw ToolException("Maximum bitrate (-B) must be a number.");
844 
845 	if ((lameparms.maxBitr % 8) != 0)
846 		lameparms.maxBitr -= lameparms.maxBitr % 8;
847 	if (lameparms.maxBitr > 64 && (lameparms.maxBitr % 16) != 0)
848 		lameparms.maxBitr -= lameparms.maxBitr % 16;
849 
850 	if (lameparms.maxBitr < 8 || lameparms.maxBitr > 160)
851 		throw ToolException("Maximum bitrate out of bounds (-B), must be between 8 and 160.");
852 }
853 
unsetMp3MinBitrate()854 void CompressionTool::unsetMp3MinBitrate() {
855 	lameparms.minBitr = -1;
856 }
857 
unsetMp3MaxBitrate()858 void CompressionTool::unsetMp3MaxBitrate() {
859 	lameparms.maxBitr = -1;
860 }
861 
setMp3VBRQuality(const std::string & arg)862 void CompressionTool::setMp3VBRQuality(const std::string& arg) {
863 	lameparms.vbrqual = atoi(arg.c_str());
864 	if (lameparms.vbrqual > 9)
865 		throw ToolException("Quality (-q) out of bounds, must be between 0 and 9.");
866 }
867 
868 // flac
setFlacCompressionLevel(const std::string & arg)869 void CompressionTool::setFlacCompressionLevel(const std::string& arg) {
870 	flacparms.compressionLevel = atoi(arg.c_str());
871 
872 	if (flacparms.compressionLevel == 0 && arg != "0")
873 		throw ToolException("FLAC compression level must be a number.");
874 
875 	if (flacparms.compressionLevel < 0 || flacparms.compressionLevel > 8)
876 		throw ToolException("FLAC compression level ot of bounds, must be between 0 and 8.");
877 
878 }
879 
setFlacBlockSize(const std::string & arg)880 void CompressionTool::setFlacBlockSize(const std::string& arg) {
881 	flacparms.blocksize = atoi(arg.c_str());
882 
883 	if (flacparms.blocksize == 0 && arg != "0")
884 		throw ToolException("FLAC block size (-b) must be a number.");
885 }
886 
887 // vorbis
setOggQuality(const std::string & arg)888 void CompressionTool::setOggQuality(const std::string& arg) {
889 	oggparms.quality = (float)atof(arg.c_str());
890 
891 	if (oggparms.quality == 0. && arg != "0")
892 		throw ToolException("Quality (-q) must be a number.");
893 
894 	if (oggparms.quality < -1.f || oggparms.quality > 10.f)
895 		throw ToolException("Quality out of bounds (-q), must be between -1 and 10.");
896 
897 	// Also unset nominal bitrate so that quality is used
898 	oggparms.nominalBitr = -1;
899 }
900 
setOggMinBitrate(const std::string & arg)901 void CompressionTool::setOggMinBitrate(const std::string& arg) {
902 	oggparms.minBitr = atoi(arg.c_str());
903 
904 	if (oggparms.minBitr == 0 && arg != "0")
905 		throw ToolException("Minimum bitrate (-m) must be a number.");
906 
907 	if (oggparms.minBitr < 8 || oggparms.minBitr > 160)
908 		throw ToolException("Minimum bitrate out of bounds (-m), must be between 8 and 160.");
909 }
910 
setOggAvgBitrate(const std::string & arg)911 void CompressionTool::setOggAvgBitrate(const std::string& arg) {
912 	oggparms.nominalBitr = atoi(arg.c_str());
913 
914 	if (oggparms.nominalBitr == 0 && arg != "0")
915 		throw ToolException("Nominal bitrate (-b) must be a number.");
916 
917 	if (oggparms.nominalBitr < 8 || oggparms.nominalBitr > 160)
918 		throw ToolException("Nominal bitrate out of bounds (-b), must be between 8 and 160.");
919 }
920 
setOggMaxBitrate(const std::string & arg)921 void CompressionTool::setOggMaxBitrate(const std::string& arg) {
922 	oggparms.maxBitr = atoi(arg.c_str());
923 
924 	if (oggparms.maxBitr == 0 && arg != "0")
925 		throw ToolException("Maximum bitrate (-M) must be a number.");
926 
927 	if (oggparms.maxBitr < 8 || oggparms.maxBitr > 160)
928 		throw ToolException("Maximum bitrate out of bounds (-M), must be between 8 and 160.");
929 }
930 
unsetOggMinBitrate()931 void CompressionTool::unsetOggMinBitrate() {
932 	oggparms.minBitr = -1;
933 }
934 
unsetOggMaxBitrate()935 void CompressionTool::unsetOggMaxBitrate() {
936 	oggparms.maxBitr = -1;
937 }
938 
processMp3Parms()939 bool CompressionTool::processMp3Parms() {
940 	while (!_arguments.empty()) {
941 		std::string arg = _arguments.front();
942 		_arguments.pop_front();
943 
944 		if (arg == "--vbr") {
945 			lameparms.type = VBR;
946 		} else if (arg == "--abr") {
947 			if (_arguments.empty())
948 				throw ToolException("Could not parse command line options, expected target bitrate after --abr");
949 			lameparms.type = VBR;
950 			setMp3TargetBitrate(_arguments.front());
951 			_arguments.pop_front();
952 		} else if (arg == "--cbr") {
953 			if (_arguments.empty())
954 				throw ToolException("Could not parse command line options, expected target bitrate after --cbr");
955 			lameparms.type = CBR;
956 			setMp3TargetBitrate(_arguments.front());
957 			_arguments.pop_front();
958 
959 		} else if (arg == "--lame-path") {
960 			if (_arguments.empty())
961 				throw ToolException("Could not parse command line options, expected value after --lame-path");
962 			setMp3LamePath(_arguments.front());
963 			_arguments.pop_front();
964 
965 		} else if (arg == "-b") {
966 			if (_arguments.empty())
967 				throw ToolException("Could not parse command line options, expected value after -b");
968 			setMp3MinBitrate(_arguments.front());
969 			_arguments.pop_front();
970 
971 		} else if (arg == "-B") {
972 			if (_arguments.empty())
973 				throw ToolException("Could not parse command line options, expected value after -B");
974 			setMp3MaxBitrate(_arguments.front());
975 			_arguments.pop_front();
976 
977 		} else if (arg == "-V") {
978 			if (_arguments.empty())
979 				throw ToolException("Could not parse command line options, expected value after -V");
980 			setMp3VBRQuality(_arguments.front());
981 			_arguments.pop_front();
982 
983 		} else if (arg == "-q") {
984 			if (_arguments.empty())
985 				throw ToolException("Could not parse command line options, expected value after -q");
986 			setMp3MpegQuality(_arguments.front());
987 			_arguments.pop_front();
988 
989 		} else if (arg == "--silent") {
990 			lameparms.silent = 1;
991 		} else {
992 			_arguments.push_front(arg);	//put back the non-audio argument we popped.
993 			break;
994 		}
995 	}
996 
997 	return true;
998 }
999 
processOggParms()1000 bool CompressionTool::processOggParms() {
1001 	while (!_arguments.empty()) {
1002 		std::string arg = _arguments.front();
1003 		_arguments.pop_front();
1004 
1005 		if (arg == "-b") {
1006 			if (_arguments.empty())
1007 				throw ToolException("Could not parse command line options, expected value after -b");
1008 			setOggAvgBitrate(_arguments.front());
1009 			_arguments.pop_front();
1010 
1011 		} else if (arg == "-m") {
1012 			if (_arguments.empty())
1013 				throw ToolException("Could not parse command line options, expected value after -m");
1014 			setOggMinBitrate(_arguments.front());
1015 			_arguments.pop_front();
1016 
1017 		} else if (arg == "-M") {
1018 			if (_arguments.empty())
1019 				throw ToolException("Could not parse command line options, expected value after -M");
1020 			setOggMaxBitrate(_arguments.front());
1021 			_arguments.pop_front();
1022 
1023 		} else if (arg == "-q") {
1024 			if (_arguments.empty())
1025 				throw ToolException("Could not parse command line options, expected value after -q");
1026 			setOggQuality(_arguments.front());
1027 			_arguments.pop_front();
1028 
1029 		} else if (arg == "--silent") {
1030 			oggparms.silent = 1;
1031 		} else {
1032 			_arguments.push_front(arg);	//put back the non-audio argument we popped.
1033 			break;
1034 		}
1035 	}
1036 
1037 	return true;
1038 }
1039 
processFlacParms()1040 bool CompressionTool::processFlacParms(){
1041 	while (!_arguments.empty()) {
1042 		std::string arg = _arguments.front();
1043 		_arguments.pop_front();
1044 
1045 		if (arg == "-b") {
1046 			if (_arguments.empty())
1047 				throw ToolException("Could not parse command line options, expected value after -b");
1048 			setFlacBlockSize(_arguments.front());
1049 			_arguments.pop_front();
1050 		} else if (arg == "--fast") {
1051 			flacparms.compressionLevel = 0;
1052 		} else if (arg == "--best") {
1053 			flacparms.compressionLevel = 8;
1054 		} else if (arg == "-0") {
1055 			flacparms.compressionLevel = 0;
1056 		} else if (arg == "-1") {
1057 			flacparms.compressionLevel = 1;
1058 		} else if (arg == "-2") {
1059 			flacparms.compressionLevel = 2;
1060 		} else if (arg == "-3") {
1061 			flacparms.compressionLevel = 3;
1062 		} else if (arg == "-4") {
1063 			flacparms.compressionLevel = 4;
1064 		} else if (arg == "-5") {
1065 			flacparms.compressionLevel = 5;
1066 		} else if (arg == "-6") {
1067 			flacparms.compressionLevel = 6;
1068 		} else if (arg == "-7") {
1069 			flacparms.compressionLevel = 7;
1070 		} else if (arg == "-8") {
1071 			flacparms.compressionLevel = 8;
1072 		} else if (arg == "--verify") {
1073 			flacparms.verify = true;
1074 		} else if (arg == "--silent") {
1075 			flacparms.silent = true;
1076 		} else {
1077 			_arguments.push_front(arg);	//put back the non-audio argument we popped.
1078 			break;
1079 		}
1080 	}
1081 
1082 	return true;
1083 }
1084 
1085 // Compression tool interface
1086 // Duplicates code above in the new way
1087 // The old code can be removed once all tools have been converted
1088 
CompressionTool(const std::string & name,ToolType type)1089 CompressionTool::CompressionTool(const std::string &name, ToolType type) : Tool(name, type) {
1090 	_supportedFormats = AUDIO_ALL;
1091 	_format = AUDIO_MP3;
1092 }
1093 
parseAudioArguments()1094 void CompressionTool::parseAudioArguments() {
1095 	if (_supportedFormats == AUDIO_NONE)
1096 		return;
1097 
1098 	_format = AUDIO_MP3;
1099 
1100 	if (_arguments.front() ==  "--mp3")
1101 		_format = AUDIO_MP3;
1102 	else if (_arguments.front() == "--vorbis")
1103 		_format = AUDIO_VORBIS;
1104 	else if (_arguments.front() == "--flac")
1105 		_format = AUDIO_FLAC;
1106 	else
1107 		// No audio arguments then
1108 		return;
1109 
1110 	_arguments.pop_front();
1111 
1112 	// Need workaround to be sign-correct
1113 	switch (_format) {
1114 	case AUDIO_MP3:
1115 		if (!processMp3Parms())
1116 			throw ToolException("Could not parse command line arguments, use --help for options");
1117 		break;
1118 	case AUDIO_VORBIS:
1119 		if (!processOggParms())
1120 			throw ToolException("Could not parse command line arguments, use --help for options");
1121 		break;
1122 	case AUDIO_FLAC:
1123 		if (!processFlacParms())
1124 			throw ToolException("Could not parse arguments: Use --help for options");
1125 		break;
1126 	default: // cannot occur but we check anyway to avoid compiler warnings
1127 		throw ToolException("Unknown audio format, should be impossible!");
1128 	}
1129 }
1130 
setTempFileName()1131 void CompressionTool::setTempFileName() {
1132 	switch (_format) {
1133 	case AUDIO_MP3:
1134 		tempEncoded = TEMP_MP3;
1135 		break;
1136 	case AUDIO_VORBIS:
1137 		tempEncoded = TEMP_OGG;
1138 		break;
1139 	case AUDIO_FLAC:
1140 		tempEncoded = TEMP_FLAC;
1141 		break;
1142 	default: // cannot occur but we check anyway to avoid compiler warnings
1143 		throw ToolException("Unknown audio format, should be impossible!");
1144 	}
1145 }
1146 
getHelp() const1147 std::string CompressionTool::getHelp() const {
1148 	std::ostringstream os;
1149 
1150 	// Standard help text + our additions
1151 	os << Tool::getHelp();
1152 
1153 	if (_supportedFormats == AUDIO_NONE)
1154 		return os.str();
1155 
1156 	os << "\nParams:\n";
1157 
1158 	if (_supportedFormats & AUDIO_MP3)
1159 		os << " --mp3        encode to MP3 format (default)\n";
1160 	if (_supportedFormats & AUDIO_VORBIS)
1161 		os << " --vorbis     encode to Vorbis format\n";
1162 	if (_supportedFormats & AUDIO_FLAC)
1163 		os << " --flac       encode to Flac format\n";
1164 	os << "(If one of these is specified, it must be the first parameter.)\n";
1165 
1166 	if (_supportedFormats & AUDIO_MP3) {
1167 		os << "\nMP3 mode params:\n";
1168 		os << " --lame-path <path> Path to the lame executable to use (default:lame)\n";
1169 		os << " -b <rate>    <rate> is the minimal bitrate (default:unset)\n";
1170 		os << " -B <rate>    <rate> is the maximum bitrate (default:unset)\n";
1171 		os << " --vbr        LAME uses the VBR mode (default)\n";
1172 		os << " --abr <rate> LAME uses the ABR mode with the given target bitrate\n";
1173 		os << " --cbr <rate> LAME uses the CBR mode with the given bitrate\n";
1174 		os << " -V <value>   specifies the value (0 - 9) of VBR quality (0=best) (default:" << vbrqualDef << ")\n";
1175 		os << " -q <value>   specifies the MPEG algorithm quality (0-9; 0=best) (default:" << algqualDef << ")\n";
1176 		os << " --silent     the output of LAME is hidden (default:disabled)\n";
1177 	}
1178 
1179 	if (_supportedFormats & AUDIO_VORBIS) {
1180 		os << "\nVorbis mode params:\n";
1181 		os << " -b <rate>    <rate> is the nominal bitrate (default:unset)\n";
1182 		os << " -m <rate>    <rate> is the minimum bitrate (default:unset)\n";
1183 		os << " -M <rate>    <rate> is the maximum bitrate (default:unset)\n";
1184 		os << " -q <value>   specifies the value (-1 - 10) of VBR quality (10=best) (default:" << oggqualDef << ")\n";
1185 		os << " --silent     the output of oggenc is hidden (default:disabled)\n";
1186 	}
1187 
1188 	if (_supportedFormats & AUDIO_FLAC) {
1189 		os << "\nFlac mode params:\n";
1190 		os << " --fast       FLAC uses compression level 0\n";
1191 		os << " --best       FLAC uses compression level 8\n";
1192 		os << " -<value>     specifies the value (0 - 8) of compression (8=best)(default:" << flacCompressDef << ")\n";
1193 		os << " -b <value>   specifies a blocksize of <value> samples (default:" << flacBlocksizeDef << ")\n";
1194 		os << " --verify     files are encoded and then decoded to check accuracy\n";
1195 		os << " --silent     the output of FLAC is hidden (default:disabled)\n";
1196 	}
1197 
1198 	os << "\n --help     this help message\n";
1199 
1200 	os << "\n\nIf a parameter is not given the default value is used\n";
1201 	os << "If using VBR mode for MP3 -b and -B must be multiples of 8; the maximum is 160!\n";
1202 
1203 	return os.str();
1204 }
1205 
audio_extensions(AudioFormat format)1206 const char *audio_extensions(AudioFormat format) {
1207 	switch(format) {
1208 	case AUDIO_MP3:
1209 		return ".mp3";
1210 	case AUDIO_VORBIS:
1211 		return ".ogg";
1212 	case AUDIO_FLAC:
1213 		return ".fla";
1214 	case AUDIO_NONE:
1215 	default:
1216 		return ".unk";
1217 	}
1218 }
1219 
compression_format(AudioFormat format)1220 int compression_format(AudioFormat format) {
1221 	switch(format) {
1222 	case AUDIO_MP3:
1223 		return 1;
1224 	case AUDIO_VORBIS:
1225 		return 2;
1226 	case AUDIO_FLAC:
1227 		return 3;
1228 	case AUDIO_NONE:
1229 	default:
1230 		throw ToolException("Unknown compression format");
1231 	}
1232 }
1233 
1234