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