diff options
-rw-r--r-- | README | 50 | ||||
-rwxr-xr-x | configure | 31 | ||||
-rw-r--r-- | doc/07_05.tex | 48 | ||||
-rw-r--r-- | queen/defs.h | 3 | ||||
-rw-r--r-- | queen/sound.cpp | 15 | ||||
-rw-r--r-- | queen/sound.h | 9 | ||||
-rw-r--r-- | scumm/sound.cpp | 42 | ||||
-rw-r--r-- | scumm/sound.h | 2 | ||||
-rw-r--r-- | simon/intern.h | 4 | ||||
-rw-r--r-- | simon/simon.cpp | 14 | ||||
-rw-r--r-- | simon/sound.cpp | 41 | ||||
-rw-r--r-- | sound/audiocd.cpp | 65 | ||||
-rw-r--r-- | sound/audiostream.cpp | 59 | ||||
-rw-r--r-- | sound/audiostream.h | 13 | ||||
-rw-r--r-- | sound/flac.cpp | 840 | ||||
-rw-r--r-- | sound/flac.h | 39 | ||||
-rw-r--r-- | sound/mixer.cpp | 9 | ||||
-rw-r--r-- | sound/mixer.h | 3 | ||||
-rw-r--r-- | sound/module.mk | 3 | ||||
-rw-r--r-- | sound/mp3.cpp | 19 | ||||
-rw-r--r-- | sound/mp3.h | 4 | ||||
-rw-r--r-- | sound/vorbis.cpp | 17 | ||||
-rw-r--r-- | sound/vorbis.h | 2 | ||||
-rw-r--r-- | sword1/animation.cpp | 27 | ||||
-rw-r--r-- | sword2/driver/animation.cpp | 22 |
25 files changed, 1276 insertions, 105 deletions
@@ -32,7 +32,7 @@ Table of Contents: * 7.2 MIDI emulation * 7.3 Native MIDI support * 7.4 UNIX native & ALSA sequencer support - * 7.5 Using MP3 or OGG to store/compress audio + * 7.5 Using compressed audiofiles (MP3, Ogg Vorbis, Flac) 8.0) Configuration Files 9.0) Compiling X.X) Credits @@ -812,7 +812,22 @@ command line with the value after q specifying the desired quality from 0 to 10: oggenc -q 5 track1.wav -7.5.2) Compressing MONSTER.SOU with MP3: +7.5.2) Using Flac files for CD audio: +------ ------------------------------------ +Use flac or some other flac encoder to encode the audio tracks to files. +Name the files track1.flac track2.flac etc. In your filesystem only allows +three letter extensions, name the files track1.fla track2.fla etc. +ScummVM must be compiled with flac support to use this option. You'll need to +rip the files from the CD as a WAV file, then encode the flac files. This can +be done with the following flac command line: + + flac --best track1.wav + +Remember that the quality is always the same, varying encoder options will only +affect the encoding time and resulting filesize. + + +7.5.3) Compressing MONSTER.SOU with MP3: ------ --------------------------------- You need LAME, and our extract util from the scummvm-tools package to perform this task, and ScummVM must be compiled with MAD support. @@ -823,7 +838,7 @@ Eventually you will have a much smaller monster.so3 file, copy this file to your game directory. You can safely remove the monster.sou file. -7.5.3) Compressing MONSTER.SOU with Ogg Vorbis: +7.5.4) Compressing MONSTER.SOU with Ogg Vorbis: ------ ---------------------------------------- As above, but ScummVM must be compiled with OGG support. Run: @@ -834,10 +849,25 @@ game directory. Ogg encoding may take a considerable longer amount of time than MP3, so have a good book handy. -7.5.4) Compressing sfx/speech in Simon the Sorcerer 1 and 2 +7.5.5) Compressing MONSTER.SOU with Flac: +------ ---------------------------------------- +As above, but ScummVM must be compiled with Flac support. Run: + + extract --flac --best -b 1152 monster.sou + +This should produce a smaller monster.sof file, which you should copy to your +game directory. Remember that the quality is always the same, varying encoder +options will only affect the encoding time and resulting filesize. Playing +with the blocksize (-b <value>), has the biggest impact on the resulting +filesize - 1152 seems to be a good value for those kind of soundfiles. Be sure +to read the encoder documentation before you use other values. + + +7.5.6) Compressing sfx/speech in Simon the Sorcerer 1 and 2 ------ ---------------------------------------------------- -Use our simon2mp3 util from the scummvm-tools package to perform -this task, and ScummVM must be compiled with MAD or VORBIS support. +Use our simon2mp3 util from the scummvm-tools package to perform this task. +You can choose between multiple target formats, but note that you can only use +each if ScummVM was compiled with the respective decoder support enabled. simon2mp3 effects (For simon1acorn) simon2mp3 simon (For simon1acorn) @@ -848,11 +878,15 @@ this task, and ScummVM must be compiled with MAD or VORBIS support. simon2mp3 simon2.wav (For simon2win) simon2mp3 mac (For simon2mac) -For Ogg Vorbis add --vorbis, ie +For Ogg Vorbis add --vorbis to the options, i.e. simon2mp3 --vorbis -Eventually you will have a much smaller *.mp3 or *.ogg file, copy this +For Flac add --flac and optional parameters, i.e. + + simon2mp3 --flac --best -b 1152 + +Eventually you will have a much smaller *.mp3, *.ogg or *.fla file, copy this file to your game dir. You can safely remove the old file. @@ -26,6 +26,7 @@ CXXFLAGS="$CXXFLAGS $CPPFLAGS" # default lib behaviour yes/no/auto _vorbis=auto +_flac=auto _mad=auto _alsa=auto _zlib=auto @@ -207,6 +208,9 @@ Optional Libraries: --with-mad-prefix=PFX Prefix where libmad is installed (optional) --disable-mad disable libmad (MP3) support [autodetect] + + --with-flac-prefix=PFX Prefix where libFLAC is installed (optional) + --disable-flac disable FLAC support [autodetect] --with-zlib-prefix=PFX Prefix where zlib is installed (optional) --disable-zlib disable zlib (compression) support [autodetect] @@ -243,6 +247,8 @@ for ac_option in $@; do --disable-alsa) _alsa=no ;; --enable-vorbis) _vorbis=yes ;; --disable-vorbis) _vorbis=no ;; + --enable-flac) _flac=yes ;; + --disable-flac) _flac=no ;; --enable-mad) _mad=yes ;; --disable-mad) _mad=no ;; --enable-zlib) _zlib=yes ;; @@ -269,6 +275,11 @@ for ac_option in $@; do VORBIS_CFLAGS="-I$_prefix/include" VORBIS_LIBS="-L$_prefix/lib" ;; + --with-flac-prefix=*) + _prefix=`echo $ac_option | cut -d '=' -f 2` + FLAC_CFLAGS="-I$_prefix/include" + FLAC_LIBS="-L$_prefix/lib" + ;; --with-mad-prefix=*) _prefix=`echo $ac_option | cut -d '=' -f 2` MAD_CFLAGS="-I$_prefix/include" @@ -622,6 +633,25 @@ else fi echo "$_vorbis" +echocheck "FLAC" +if test "$_flac" = auto ; then + _flac=no + cat > $TMPC << EOF +#include <FLAC/seekable_stream_decoder.h> +int main(void) { FLAC__seekable_stream_decoder_init( 0 ); return 0; } +EOF + cc_check $LDFLAGS $CXXFLAGS $FLAC_CFLAGS $FLAY_LIBS \ + -lFLAC -lm && _flac=yes +fi +if test "$_flac" = yes ; then + _def_flac='#define USE_FLAC' + LIBS="$LIBS $FLAC_LIBS -lFLAC" + INCLUDES="$INCLUDES $FLAC_CFLAGS" +else + _def_flac='#undef USE_FLAC' +fi +echo "$_flac" + # # Check for MAD (MP3 library) # @@ -809,6 +839,7 @@ typedef signed $type_4_byte int32; /* Libs */ $_def_vorbis +$_def_flac $_def_mad $_def_alsa $_def_zlib diff --git a/doc/07_05.tex b/doc/07_05.tex index 913392fbd7..3fa002f444 100644 --- a/doc/07_05.tex +++ b/doc/07_05.tex @@ -4,7 +4,9 @@ %%% TeX-master: "readme" %%% End: -\subsection{Using MP3 files for CD audio} +\subsection{Using compressed audiofiles (MP3, Ogg Vorbis, Flac)} + +\subsubsection{Using MP3 files for CD audio} Use LAME or some other mp3 encoder to rip the cd audio tracks to files. Name the files track1.mp3 track2.mp3 etc. ScummVM must be compiled with MAD support @@ -28,6 +30,21 @@ command line with the value after q specifying the desired quality from 0 to 10: \end{verbatim} +\subsubsection{Using Flac files for CD audio} +Use flac or some other flac encoder to encode the audio tracks to files. +Name the files track1.flac track2.flac etc. In your filesystem only allows +three letter extensions, name the files track1.fla track2.fla etc. +ScummVM must be compiled with flac support to use this option. You'll need to +rip the files from the CD as a WAV file, then encode the flac files. This can +be done with the following flac command line: +\begin{verbatim} + flac --best track1.wav +\end{verbatim} +% +Remember that the quality is always the same, varying encoder options will only +affect the encoding time and resulting filesize. + + \subsubsection{Compressing MONSTER.SOU with MP3} You need LAME, and our extract util from the scummvm-tools package to perform @@ -52,10 +69,26 @@ game directory. Ogg encoding may take a considerable longer amount of time than MP3, so have a good book handy. +\subsubsection{Compressing MONSTER.SOU with Flac} + +As above, but ScummVM must be compiled with Flac support. Run: +\begin{verbatim} + extract --flac --best -b 1152 monster.sou +\end{verbatim} +% +This should produce a smaller monster.sof file, which you should copy to your +game directory. Remember that the quality is always the same, varying encoder +options will only affect the encoding time and resulting filesize. Playing +with the blocksize (-b <value>), has the biggest impact on the resulting +filesize -- 1152 seems to be a good value for those kind of soundfiles. Be sure +to read the encoder documentation before you use other values. + + \subsubsection{Compressing sfx/speech in Simon the Sorcerer 1 and 2} -Use our simon2mp3 util from the scummvm-tools package to perform -this task, and ScummVM must be compiled with MAD or VORBIS support.\\ +Use our simon2mp3 util from the scummvm-tools package to perform this task. +You can choose between multiple target formats, but note that you can only use +each if ScummVM was compiled with the respective decoder support enabled. \begin{tabular}[h]{ll} simon2mp3 effects &(For simon1acorn)\\ @@ -68,10 +101,15 @@ this task, and ScummVM must be compiled with MAD or VORBIS support.\\ simon2mp3 mac &(For simon2mac)\\ \end{tabular} -For Ogg Vorbis add --vorbis, i.e. +For Ogg Vorbis add --vorbis to the options, i.e. \begin{verbatim} simon2mp3 --vorbis \end{verbatim} % -Eventually you will have a much smaller *.mp3 or *.ogg file, copy this +For Flac add --flac and optional parameters, i.e. +\begin{verbatim} + simon2mp3 --flac --best -b 1152 +\end{verbatim} +% +Eventually you will have a much smaller *.mp3, *.ogg or *.fla file, copy this file to your game dir. You can safely remove the old file. diff --git a/queen/defs.h b/queen/defs.h index e64cd99e89..aed9ce9e09 100644 --- a/queen/defs.h +++ b/queen/defs.h @@ -29,7 +29,8 @@ namespace Queen { enum { COMPRESSION_NONE = 0, COMPRESSION_MP3 = 1, - COMPRESSION_OGG = 2 + COMPRESSION_OGG = 2, + COMPRESSION_FLAC = 3 }; enum { diff --git a/queen/sound.cpp b/queen/sound.cpp index c403d32b1c..9b2d452b20 100644 --- a/queen/sound.cpp +++ b/queen/sound.cpp @@ -61,6 +61,14 @@ Sound *Sound::giveSound(SoundMixer *mixer, QueenEngine *vm, uint8 compression) { return new OGGSound(mixer, vm); #endif break; + case COMPRESSION_FLAC: + #ifndef USE_FLAC + warning("Using FLAC compressed datafile, but FLAC support not compiled in"); + return new SilentSound(mixer, vm); + #else + return new FLACSound(mixer, vm); + #endif + break; default: warning("Unknown compression type"); return new SilentSound(mixer, vm); @@ -184,4 +192,11 @@ void OGGSound::sfxPlay(const char *name, bool isSpeech) { } #endif +#ifdef USE_FLAC +void FLACSound::sfxPlay(const char *name, bool isSpeech) { + if (_vm->resource()->fileExists(name)) + _mixer->playFlac(isSpeech ? &_speechHandle : &_sfxHandle, _vm->resource()->giveCompressedSound(name), _vm->resource()->fileSize(name)); +} +#endif + } //End of namespace Queen diff --git a/queen/sound.h b/queen/sound.h index 2bc0ec04c4..bbc48b0cea 100644 --- a/queen/sound.h +++ b/queen/sound.h @@ -133,6 +133,15 @@ public: void sfxPlay(const char *name, bool isSpeech); }; #endif + +#ifdef USE_FLAC +class FLACSound : public Sound { +public: + FLACSound(SoundMixer *mixer, QueenEngine *vm) : Sound(mixer, vm) {}; + void sfxPlay(const char *name, bool isSpeech); +}; +#endif // #ifdef USE_FLAC + } // End of namespace Queen #endif diff --git a/scumm/sound.cpp b/scumm/sound.cpp index 519ff980db..61e4da3d77 100644 --- a/scumm/sound.cpp +++ b/scumm/sound.cpp @@ -37,6 +37,7 @@ #include "sound/mp3.h" #include "sound/voc.h" #include "sound/vorbis.h" +#include "sound/flac.h" namespace Scumm { @@ -831,17 +832,25 @@ void Sound::pauseSounds(bool pause) { void Sound::startSfxSound(File *file, int file_size, PlayingSoundHandle *handle, int id) { - AudioStream *input = 0; + AudioStream *input = NULL; if (file_size > 0) { - if (_vorbis_mode) { + switch (_sound_mode) { + case kMP3Mode: +#ifdef USE_MAD + input = makeMP3Stream(file, file_size); +#endif + break; + case kVorbisMode: #ifdef USE_VORBIS input = makeVorbisStream(file, file_size); #endif - } else { -#ifdef USE_MAD - input = makeMP3Stream(file, file_size); + break; + case kFlacMode: +#ifdef USE_FLAC + input = makeFlacStream(file, file_size); #endif + break; } } else { input = makeVOCStream(_sfxFile); @@ -869,13 +878,24 @@ File *Sound::openSfxFile() { * same directory */ offset_table = NULL; +#ifdef USE_FLAC + if (!file->isOpen()) { + sprintf(buf, "%s.sof", _vm->getGameName()); + if (!file->open(buf, _vm->getGameDataPath())) + file->open("monster.sof", _vm->getGameDataPath()); + if (file->isOpen()) + _sound_mode = kFlacMode; + } +#endif + #ifdef USE_MAD - sprintf(buf, "%s.so3", _vm->getGameName()); - if (!file->open(buf, _vm->getGameDataPath())) { - file->open("monster.so3", _vm->getGameDataPath()); + if (!file->isOpen()) { + sprintf(buf, "%s.so3", _vm->getGameName()); + if (!file->open(buf, _vm->getGameDataPath())) + file->open("monster.so3", _vm->getGameDataPath()); + if (file->isOpen()) + _sound_mode = kMP3Mode; } - if (file->isOpen()) - _vorbis_mode = false; #endif #ifdef USE_VORBIS @@ -884,7 +904,7 @@ File *Sound::openSfxFile() { if (!file->open(buf, _vm->getGameDataPath())) file->open("monster.sog", _vm->getGameDataPath()); if (file->isOpen()) - _vorbis_mode = true; + _sound_mode = kVorbisMode; } #endif diff --git a/scumm/sound.h b/scumm/sound.h index c785853018..97c19029de 100644 --- a/scumm/sound.h +++ b/scumm/sound.h @@ -52,7 +52,7 @@ protected: MP3OffsetTable *offset_table; // SO3 MP3 compressed audio int num_sound_effects; // SO3 MP3 compressed audio - bool _vorbis_mode; // true if using SOG, false if using SO3 + enum { kMP3Mode, kVorbisMode, kFlacMode } _sound_mode; int _currentCDSound; diff --git a/simon/intern.h b/simon/intern.h index c948e3d64c..39d6d707cc 100644 --- a/simon/intern.h +++ b/simon/intern.h @@ -125,19 +125,23 @@ struct GameSpecificSettings { const char *voc_filename; const char *mp3_filename; const char *vorbis_filename; + const char *flac_filename; const char *voc_effects_filename; const char *mp3_effects_filename; const char *vorbis_effects_filename; + const char *flac_effects_filename; const char *gamepc_filename; #else const char gme_filename[12]; const char wav_filename[12]; const char voc_filename[12]; const char mp3_filename[12]; + const char flac_filename[12]; const char vorbis_filename[12]; const char voc_effects_filename[12]; const char mp3_effects_filename[12]; const char vorbis_effects_filename[12]; + const char flac_effects_filename[12]; const char gamepc_filename[12]; #endif }; diff --git a/simon/simon.cpp b/simon/simon.cpp index 46cf2d94a6..dac8e1bc8f 100644 --- a/simon/simon.cpp +++ b/simon/simon.cpp @@ -137,9 +137,11 @@ static const GameSpecificSettings simon1_settings = { "SIMON.VOC", // voc_filename "SIMON.MP3", // mp3_filename "SIMON.OGG", // vorbis_filename + "SIMON.FLA", // flac_filename "EFFECTS.VOC", // voc_effects_filename "EFFECTS.MP3", // mp3_effects_filename "EFFECTS.OGG", // vorbis_effects_filename + "EFFECTS.FLA", // flac_effects_filename "GAMEPC", // gamepc_filename }; @@ -149,9 +151,11 @@ static const GameSpecificSettings simon1acorn_settings = { "SIMON", // voc_filename "SIMON.MP3", // mp3_filename "SIMON.OGG", // vorbis_filename + "SIMON.FLA", // flac_filename "EFFECTS", // voc_effects_filename "EFFECTS.MP3", // mp3_effects_filename "EFFECTS.OGG", // vorbis_effects_filename + "EFFECTS.FLA", // flac_effects_filename "GAMEBASE", // gamepc_filename }; @@ -161,9 +165,11 @@ static const GameSpecificSettings simon1amiga_settings = { "", // voc_filename "SIMON.MP3", // mp3_filename "SIMON.OGG", // vorbis_filename + "SIMON.FLA", // flac_filename "", // voc_effects_filename "", // mp3_effects_filename "", // vorbis_effects_filename + "", // flac_effects_filename "gameamiga", // gamepc_filename }; @@ -173,9 +179,11 @@ static const GameSpecificSettings simon1demo_settings = { "", // voc_filename "", // mp3_filename "", // vorbis_filename + "", // flac_filename "", // voc_effects_filename "", // mp3_effects_filename "", // vorbis_effects_filename + "", // flac_effects_filename "GDEMO", // gamepc_filename }; @@ -185,9 +193,11 @@ static const GameSpecificSettings simon2win_settings = { "SIMON2.VOC", // voc_filename "SIMON2.MP3", // mp3_filename "SIMON2.OGG", // vorbis_filename + "SIMON2.FLA", // flac_filename "", // voc_effects_filename "", // mp3_effects_filename "", // vorbis_effects_filename + "", // flac_effects_filename "GSPTR30", // gamepc_filename }; @@ -197,9 +207,11 @@ static const GameSpecificSettings simon2mac_settings = { "", // voc_filename "SIMON2.MP3", // mp3_filename "SIMON2.OGG", // vorbis_filename + "SIMON2.FLA", // flac_filename "", // voc_effects_filename "", // mp3_effects_filename "", // vorbis_effects_filename + "", // flac_effects_filename "gsptr30", // gamepc_filename }; @@ -209,9 +221,11 @@ static const GameSpecificSettings simon2dos_settings = { "", // voc_filename "", // mp3_filename "", // vorbis_filename + "", // flac_filename "", // voc_effects_filename "", // mp3_effects_filename "", // vorbis_effects_filename + "", // flac_effects_filename "GAME32", // gamepc_filename }; #endif diff --git a/simon/sound.cpp b/simon/sound.cpp index 44201899f8..1d4dd92c3b 100644 --- a/simon/sound.cpp +++ b/simon/sound.cpp @@ -241,6 +241,30 @@ void VorbisSound::playSound(uint sound, PlayingSoundHandle *handle, byte flags) } #endif +#ifdef USE_FLAC +class FlacSound : public BaseSound { +public: + FlacSound(SoundMixer *mixer, File *file, uint32 base = 0) : BaseSound(mixer, file, base) {}; + void playSound(uint sound, PlayingSoundHandle *handle, byte flags); +}; + +void FlacSound::playSound(uint sound, PlayingSoundHandle *handle, byte flags) +{ + if (_offsets == NULL) + return; + + _file->seek(_offsets[sound], SEEK_SET); + + int i = 1; + while (_offsets[sound + i] == _offsets[sound]) + i++; + + uint32 size = _offsets[sound + i] - _offsets[sound]; + + _mixer->playFlac(handle, _file, size); +} +#endif + Sound::Sound(const byte game, const GameSpecificSettings *gss, const Common::String &gameDataPath, SoundMixer *mixer) : _game(game), _gameDataPath(gameDataPath), _mixer(mixer) { _voice = 0; @@ -259,6 +283,15 @@ Sound::Sound(const byte game, const GameSpecificSettings *gss, const Common::Str File *file = new File(); const char *s; +#ifdef USE_FLAC + if (!_voice && gss->flac_filename && gss->flac_filename[0]) { + file->open(gss->flac_filename, gameDataPath); + if (file->isOpen()) { + _voice_file = true; + _voice = new FlacSound(_mixer, file); + } + } +#endif #ifdef USE_MAD if (!_voice && gss->mp3_filename && gss->mp3_filename[0]) { file->open(gss->mp3_filename, gameDataPath); @@ -341,6 +374,14 @@ Sound::Sound(const byte game, const GameSpecificSettings *gss, const Common::Str } } #endif +#ifdef USE_FLAC + if (!_effects && gss->flac_effects_filename && gss->flac_effects_filename[0]) { + file->open(gss->flac_effects_filename, gameDataPath); + if (file->isOpen()) { + _effects = new FlacSound(_mixer, file); + } + } +#endif if (!_effects) { s = gss->voc_effects_filename; file->open(s, gameDataPath); diff --git a/sound/audiocd.cpp b/sound/audiocd.cpp index fbd961cd04..8c96a04097 100644 --- a/sound/audiocd.cpp +++ b/sound/audiocd.cpp @@ -24,10 +24,38 @@ #include "sound/audiocd.h" #include "sound/mp3.h" #include "sound/vorbis.h" +#include "sound/flac.h" #include "base/engine.h" #include "common/file.h" #include "common/util.h" +struct TrackFormat { + /** Decodername */ + const char* decoderName; + /** + * Pointer to a function which tries to open the specified track - the only argument + * is the number of the track to be played. + * Returns either the DigitalTrackInfo object representing the requested track or null + * in case of an error + */ + DigitalTrackInfo* (*openTrackFunction)(int); +}; + +static const TrackFormat TRACK_FORMATS[] = { + /* decoderName, openTrackFunction */ +#ifdef USE_FLAC + { "Flac", getFlacTrack }, +#endif // #ifdef USE_FLAC +#ifdef USE_VORBIS + { "Ogg Vorbis", getVorbisTrack }, +#endif // #ifdef USE_VORBIS +#ifdef USE_MAD + { "Mpeg Layer 3", getMP3Track }, +#endif // #ifdef USE_MAD + + { NULL, NULL } // Terminator +}; + AudioCDManager::AudioCDManager() { memset(&_cd, 0, sizeof(_cd)); @@ -101,15 +129,10 @@ AudioCDManager::Status AudioCDManager::getStatus() const { } int AudioCDManager::getCachedTrack(int track) { - int i; -#if defined(USE_MAD) || defined(USE_VORBIS) - char track_name[1024]; - File *file = new File(); -#endif int current_index; // See if we find the track in the cache - for (i = 0; i < CACHE_TRACKS; i++) + for (int i = 0; i < CACHE_TRACKS; i++) if (_cached_tracks[i] == track) { if (_track_info[i]) return i; @@ -127,35 +150,11 @@ int AudioCDManager::getCachedTrack(int track) { _cached_tracks[current_index] = track; -#ifdef USE_MAD - sprintf(track_name, "track%d.mp3", track); - file->open(track_name); - - if (file->isOpen()) { - _track_info[current_index] = makeMP3TrackInfo(file); - if (_track_info[current_index]->error()) { - delete _track_info[current_index]; - _track_info[current_index] = NULL; - return -1; - } - return current_index; - } -#endif + for (int i = 0; i < ARRAYSIZE(TRACK_FORMATS)-1 && _track_info[current_index] == NULL; ++i) + _track_info[current_index] = TRACK_FORMATS[i].openTrackFunction(track); -#ifdef USE_VORBIS - sprintf(track_name, "track%d.ogg", track); - file->open(track_name); - - if (file->isOpen()) { - _track_info[current_index] = makeVorbisTrackInfo(file); - if (_track_info[current_index]->error()) { - delete _track_info[current_index]; - _track_info[current_index] = NULL; - return -1; - } + if (_track_info[current_index] != NULL) return current_index; - } -#endif debug(2, "Track %d not available in compressed format", track); return -1; diff --git a/sound/audiostream.cpp b/sound/audiostream.cpp index b8ec6d2220..7a0164c1d5 100644 --- a/sound/audiostream.cpp +++ b/sound/audiostream.cpp @@ -24,7 +24,9 @@ #include "common/util.h" #include "sound/audiostream.h" #include "sound/mixer.h" - +#include "sound/mp3.h" +#include "sound/vorbis.h" +#include "sound/flac.h" // This used to be an inline template function, but // buggy template function handling in MSVC6 forced @@ -37,6 +39,61 @@ #define READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, ptr, isLE) \ ((is16Bit ? (isLE ? READ_LE_UINT16(ptr) : READ_BE_UINT16(ptr)) : (*ptr << 8)) ^ (isUnsigned ? 0x8000 : 0)) + +struct StreamFileFormat { + /** Decodername */ + const char* decoderName; + const char* fileExtension; + /** + * Pointer to a function which tries to open a file of type StreamFormat. + * Return NULL in case of an error (invalid/nonexisting file). + */ + AudioStream* (*openStreamFile)(File *file, uint32 size); +}; + +static const StreamFileFormat STREAM_FILEFORMATS[] = { + /* decoderName, fileExt, openStreamFuntion */ +#ifdef USE_FLAC + { "Flac", "flac", makeFlacStream }, + { "Flac", "fla", makeFlacStream }, +#endif // #ifdef USE_FLAC +#ifdef USE_VORBIS + { "Ogg Vorbis", "ogg", makeVorbisStream }, +#endif // #ifdef USE_VORBIS +#ifdef USE_MAD + { "Mpeg Layer 3", "mp3", makeMP3Stream }, +#endif // #ifdef USE_MAD + + { NULL, NULL, NULL } // Terminator +}; + +AudioStream* AudioStream::openStreamFile(const char* filename, File *fileHandle) +{ + char buffer[1024]; + const uint len = strlen(filename); + assert(len+6 < sizeof(buffer)); // we need a bigger buffer if wrong + + memcpy(buffer, filename, len); + buffer[len] = '.'; + char *ext = &buffer[len+1]; + + AudioStream* stream = NULL; + + for (int i = 0; i < ARRAYSIZE(STREAM_FILEFORMATS)-1 && stream == NULL; ++i) { + strcpy(ext, STREAM_FILEFORMATS[i].fileExtension); + fileHandle->open(buffer); + if (fileHandle->isOpen()) + stream = STREAM_FILEFORMATS[i].openStreamFile(fileHandle, fileHandle->size()); + } + + if (stream == NULL) { + fileHandle->close(); + debug(1, "AudioStream: Could not open compressed AudioFile %s", filename); + } + + return stream; +} + #pragma mark - #pragma mark --- LinearMemoryStream --- #pragma mark - diff --git a/sound/audiostream.h b/sound/audiostream.h index 97822c0c26..ae549afba9 100644 --- a/sound/audiostream.h +++ b/sound/audiostream.h @@ -26,6 +26,8 @@ #include "common/scummsys.h" #include "common/util.h" +class File; + /** * Generic input stream for the resampling code. @@ -74,6 +76,17 @@ public: /** Sample rate of the stream. */ virtual int getRate() const = 0; + + /** + * Tries to load a file by trying all available formats. + * In case of an error, the file handle will be closed, but deleting + * it is still the responsibilty of the caller. + * @param filename a filename without an extension + * @param fileHandle a pointer to an existing File instance + * @return an Audiostream ready to use in case of success; + * NULL in case of an error (e.g. invalid/nonexisting file) + */ + static AudioStream* openStreamFile(const char* filename, File *fileHandle); }; class AppendableAudioStream : public AudioStream { diff --git a/sound/flac.cpp b/sound/flac.cpp new file mode 100644 index 0000000000..ccbc386545 --- /dev/null +++ b/sound/flac.cpp @@ -0,0 +1,840 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003-2004 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#include "sound/flac.h" + +#ifdef USE_FLAC + +#include "common/file.h" +#include "common/util.h" + +#include "sound/audiostream.h" +#include "sound/audiocd.h" + +#define FLAC__NO_DLL // that MS-magic gave me headaches - just link the library you like +#include <FLAC/seekable_stream_decoder.h> + +#pragma mark - +#pragma mark --- Flac stream --- +#pragma mark - + +static const uint MAX_OUTPUT_CHANNELS = 2; + + +class FlacInputStream : public AudioStream { + +public: + FlacInputStream(File *sourceFile, const uint32 fileStart, const uint32 fileStop); + FlacInputStream(File *sourceFile, const uint32 fileStart = 0); + virtual ~FlacInputStream(); + + int readBuffer(int16 *buffer, const int numSamples); + + bool isStereo() const { return _streaminfo.channels >= 2; } + int getRate() const { return _streaminfo.sample_rate; } + bool endOfStream() const { return _streaminfo.channels == 0 || (_lastSampleWritten && getBufferedSamples() == 0); } + /** the mixer aint supporting it right now.. */ + //bool endOfData() const { return getBufferedSamples() == 0; } + bool endOfData() const { return endOfStream(); } + + uint getChannels() const { return MIN(_streaminfo.channels, MAX_OUTPUT_CHANNELS); } + uint getBufferedSamples() const { return _preBuffer.bufFill; }; + + const FLAC__StreamMetadata_StreamInfo& getStreamInfo() const {return _streaminfo;} + + inline FLAC__SeekableStreamDecoderState getState() const; + inline FLAC__StreamDecoderState getStreamDecoderState() const; + + + bool isStreamDecoderReady() const { return getStreamDecoderState() == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC ; } + bool init(); + bool finish(); + bool flush(); + inline bool processSingleBlock(); + inline bool processUntilEndOfMetadata(); + bool seekAbsolute(FLAC__uint64 sample); + inline void setLastSample(FLAC__uint64 absoluteSample); + +protected: + inline ::FLAC__SeekableStreamDecoderReadStatus callbackRead(FLAC__byte buffer[], uint *bytes); + inline ::FLAC__SeekableStreamDecoderSeekStatus callbackSeek(FLAC__uint64 absoluteByteOffset); + inline ::FLAC__SeekableStreamDecoderTellStatus callbackTell(FLAC__uint64 *absoluteByteOffset); + inline ::FLAC__SeekableStreamDecoderLengthStatus callbackLength(FLAC__uint64 *streamLength); + inline bool callbackEOF(); + inline ::FLAC__StreamDecoderWriteStatus callbackWrite(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]); + inline void callbackMetadata(const ::FLAC__StreamMetadata *metadata); + inline void callbackError(::FLAC__StreamDecoderErrorStatus status); + + ::FLAC__SeekableStreamDecoder *_decoder; + +private: + static ::FLAC__SeekableStreamDecoderReadStatus callWrapRead(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], uint *bytes, void *clientData); + static ::FLAC__SeekableStreamDecoderSeekStatus callWrapSeek(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 absoluteByteOffset, void *clientData); + static ::FLAC__SeekableStreamDecoderTellStatus callWrapTell(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *absoluteByteOffset, void *clientData); + static ::FLAC__SeekableStreamDecoderLengthStatus callWrapLength(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *streamLength, void *clientData); + static FLAC__bool callWrapEOF(const ::FLAC__SeekableStreamDecoder *decoder, void *clientData); + static ::FLAC__StreamDecoderWriteStatus callWrapWrite(const ::FLAC__SeekableStreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *clientData); + static void callWrapMetadata(const ::FLAC__SeekableStreamDecoder *decoder, const ::FLAC__StreamMetadata *metadata, void *clientData); + static void callWrapError(const ::FLAC__SeekableStreamDecoder *decoder, ::FLAC__StreamDecoderErrorStatus status, void *clientData); + // Private and undefined so you can't use them: + FlacInputStream(const FlacInputStream &); + void operator=(const FlacInputStream &); + + bool isValid() const { return _decoder != NULL; } + + bool allocateBuffer(uint minSamples); + inline void flushBuffer(); + inline void deleteBuffer(); + + /** Header of the Stream */ + FLAC__StreamMetadata_StreamInfo _streaminfo; + + struct { + /** Handle to the File */ + File *fileHandle; + /** Index of next Byte to read */ + uint32 filePos; + /** start of stream - not necessary start of file */ + uint32 fileStartPos; + /** last index of Stream + 1(!) - not necessary end of file */ + uint32 fileEndPos; + } _fileInfo; + + /** index of the first Sample to be played */ + FLAC__uint64 _firstSample; + /** index + 1(!) of the last Sample to be played - 0 is end of Stream*/ + FLAC__uint64 _lastSample; + + /** true if the last Sample was decoded from the FLAC-API - there might still be data in the buffer */ + bool _lastSampleWritten; + + typedef int16 bufType; + enum { BUFTYPE_BITS = 16 }; + + struct { + bufType *bufData; + bufType *bufReadPos; + uint bufSize; + uint bufFill; + } _preBuffer; + + bufType *_outBuffer; + uint _requestedSamples; + + void setBestConvertBufferMethod(); + typedef void (*PFCONVERTBUFFERS)(bufType*,const FLAC__int32*[], uint, const uint, const uint8); + PFCONVERTBUFFERS _methodConvertBuffers; + static void convertBuffersGeneric(bufType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits); + static void convertBuffersStereoNS(bufType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits); + static void convertBuffersStereo8Bit(bufType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits); + static void convertBuffersMonoNS(bufType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits); + static void convertBuffersMono8Bit(bufType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits); +}; + +FlacInputStream::FlacInputStream(File *sourceFile, const uint32 fileStart) + : _decoder(::FLAC__seekable_stream_decoder_new()), _firstSample(0), _lastSample(0), + _outBuffer(NULL), _requestedSamples(0), _lastSampleWritten(true), + _methodConvertBuffers(&FlacInputStream::convertBuffersGeneric) +{ + assert(sourceFile != NULL && sourceFile->isOpen()); + + memset(&_streaminfo, 0, sizeof(_streaminfo)); + + _preBuffer.bufData = NULL; + _preBuffer.bufFill = 0; + _preBuffer.bufSize = 0; + + _fileInfo.fileHandle = sourceFile; + _fileInfo.fileStartPos = fileStart; + _fileInfo.filePos = fileStart; + _fileInfo.fileEndPos = sourceFile->size(); +} + +FlacInputStream::FlacInputStream(File *sourceFile, const uint32 fileStart, const uint32 fileStop) + : _decoder(::FLAC__seekable_stream_decoder_new()), _firstSample(0), _lastSample(0), + _outBuffer(NULL), _requestedSamples(0), _lastSampleWritten(true), + _methodConvertBuffers(&FlacInputStream::convertBuffersGeneric) +{ + assert(sourceFile != NULL && sourceFile->isOpen()); + assert(fileStop <= 0 || (fileStart < fileStop && fileStop <= sourceFile->size())); + + memset(&_streaminfo, 0, sizeof(_streaminfo)); + + _preBuffer.bufData = NULL; + _preBuffer.bufFill = 0; + _preBuffer.bufSize = 0; + + _fileInfo.fileHandle = sourceFile; + _fileInfo.fileStartPos = fileStart; + _fileInfo.filePos = fileStart; + _fileInfo.fileEndPos = fileStop; +} + +FlacInputStream::~FlacInputStream() { + if (_decoder != NULL) { + (void) ::FLAC__seekable_stream_decoder_finish(_decoder); + ::FLAC__seekable_stream_decoder_delete(_decoder); + } + if (_preBuffer.bufData != NULL) + delete[] _preBuffer.bufData; +} + +inline FLAC__SeekableStreamDecoderState FlacInputStream::getState() const { + assert(isValid()); + return ::FLAC__seekable_stream_decoder_get_state(_decoder); +} + +inline FLAC__StreamDecoderState FlacInputStream::getStreamDecoderState() const { + assert(isValid()); + return ::FLAC__seekable_stream_decoder_get_stream_decoder_state(_decoder); +} + +bool FlacInputStream::init() { + assert(isValid()); + + memset(&_streaminfo, 0, sizeof (_streaminfo)); + deleteBuffer(); + _fileInfo.filePos = _fileInfo.fileStartPos; + _lastSampleWritten = false; + _methodConvertBuffers = &FlacInputStream::convertBuffersGeneric; + + ::FLAC__seekable_stream_decoder_set_read_callback(_decoder, &FlacInputStream::callWrapRead); + ::FLAC__seekable_stream_decoder_set_seek_callback(_decoder, &FlacInputStream::callWrapSeek); + ::FLAC__seekable_stream_decoder_set_tell_callback(_decoder, &FlacInputStream::callWrapTell); + ::FLAC__seekable_stream_decoder_set_length_callback(_decoder, &FlacInputStream::callWrapLength); + ::FLAC__seekable_stream_decoder_set_eof_callback(_decoder, &FlacInputStream::callWrapEOF); + ::FLAC__seekable_stream_decoder_set_write_callback(_decoder, &FlacInputStream::callWrapWrite); + ::FLAC__seekable_stream_decoder_set_metadata_callback(_decoder, &FlacInputStream::callWrapMetadata); + ::FLAC__seekable_stream_decoder_set_error_callback(_decoder, &FlacInputStream::callWrapError); + ::FLAC__seekable_stream_decoder_set_client_data(_decoder, (void*)this); + + if (::FLAC__seekable_stream_decoder_init(_decoder) == FLAC__SEEKABLE_STREAM_DECODER_OK) { + if (processUntilEndOfMetadata() && _streaminfo.channels > 0) { + if (_firstSample == 0 || 0 != ::FLAC__seekable_stream_decoder_seek_absolute(_decoder, _firstSample)) { + // FLAC__StreamDecoderState state = getStreamDecoderState(); + return true; // no error occured + } + } + } + + warning("FlacInputStream: could not create an Audiostream from File %s", _fileInfo.fileHandle->name()); + return false; +} + +bool FlacInputStream::finish() { + assert(isValid()); + deleteBuffer(); + return 0 != ::FLAC__seekable_stream_decoder_finish(_decoder); +} + +bool FlacInputStream::flush() { + assert(isValid()); + flushBuffer(); + return 0 != ::FLAC__seekable_stream_decoder_flush(_decoder); +} + +inline bool FlacInputStream::processSingleBlock() { + assert(isValid()); + return 0 != ::FLAC__seekable_stream_decoder_process_single(_decoder); +} + +inline bool FlacInputStream::processUntilEndOfMetadata() { + assert(isValid()); + return 0 != ::FLAC__seekable_stream_decoder_process_until_end_of_metadata(_decoder); +} + +bool FlacInputStream::seekAbsolute(FLAC__uint64 sample) { + assert(isValid()); + const bool result = (0 != ::FLAC__seekable_stream_decoder_seek_absolute(_decoder, sample)); + if (result) { + flushBuffer(); + _lastSampleWritten = (_lastSample != 0 && sample >= _lastSample); // only set if we are SURE + } + return result; +} + +int FlacInputStream::readBuffer(int16 *buffer, const int numSamples) { + const uint kNumChannels = getChannels(); + + if (kNumChannels == 0) { + warning("FlacInputStream: Stream not sucessfully initialised, cant playback"); + return -1; // streaminfo wasnt read! + } + + assert(numSamples % kNumChannels == 0); // must be multiple of channels! + assert(buffer != NULL); + assert(_outBuffer == NULL); + assert(_requestedSamples == 0); + + _outBuffer = buffer; + _requestedSamples = numSamples; + + if (_preBuffer.bufFill > 0) { + assert(_preBuffer.bufData != NULL && _preBuffer.bufReadPos != NULL && _preBuffer.bufSize > 0); + assert(_preBuffer.bufReadPos >= _preBuffer.bufData); + assert(_preBuffer.bufFill % kNumChannels == 0); + + const uint copySamples = MIN((uint)numSamples, _preBuffer.bufFill); + memcpy(buffer, _preBuffer.bufReadPos, copySamples*sizeof(buffer[0])); + + _outBuffer = buffer + copySamples; + _requestedSamples = numSamples - copySamples; + _preBuffer.bufReadPos += copySamples; + _preBuffer.bufFill -= copySamples; + } + + bool decoderOk = true; + + if (!_lastSampleWritten) { + FLAC__StreamDecoderState state = getStreamDecoderState(); + + for (; _requestedSamples > 0 && state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; state = getStreamDecoderState()) { + assert(_preBuffer.bufFill == 0); + assert(_requestedSamples % kNumChannels == 0); + processSingleBlock(); + } + + if (state != FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) { + switch (state) { + case FLAC__STREAM_DECODER_END_OF_STREAM : + _lastSampleWritten = true; + decoderOk = true; // no REAL error + break; + + default: + decoderOk = false; + warning("FlacInputStream: An error occured while decoding. DecoderState is: %s", + FLAC__StreamDecoderStateString[getStreamDecoderState()]); + } + } + } + + const int samples = (int)(_outBuffer - buffer); + assert(samples % kNumChannels == 0); + + _outBuffer = NULL; // basically unnessecary, only for the purpose of the asserts + _requestedSamples = 0; // basically unnessecary, only for the purpose of the asserts + + return decoderOk ? samples : -1; +} + +inline ::FLAC__SeekableStreamDecoderReadStatus FlacInputStream::callbackRead(FLAC__byte buffer[], uint *bytes) { + assert(_fileInfo.fileHandle != NULL); + + if (*bytes == 0) + return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR; /* abort to avoid a deadlock */ + + const uint32 length = MIN(_fileInfo.fileEndPos - _fileInfo.filePos, static_cast<uint32>(*bytes)); + + _fileInfo.fileHandle->seek(_fileInfo.filePos); + const uint32 bytesRead = _fileInfo.fileHandle->read(buffer, length); + + if (bytesRead == 0 && _fileInfo.fileHandle->ioFailed()) + return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR; + + _fileInfo.filePos += bytesRead; + *bytes = static_cast<uint>(bytesRead); + return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK; +} + +inline void FlacInputStream::setLastSample(FLAC__uint64 absoluteSample) { + if (_lastSampleWritten && absoluteSample > _lastSample) + _lastSampleWritten = false; + _lastSample = absoluteSample; +} + +inline void FlacInputStream::flushBuffer() { + _lastSampleWritten = _lastSampleWritten && _preBuffer.bufFill == 0; + _preBuffer.bufFill = 0; +} + +inline void FlacInputStream::deleteBuffer() { + flushBuffer(); + _preBuffer.bufSize = 0; + if (_preBuffer.bufData != NULL) { + delete[] _preBuffer.bufData; + _preBuffer.bufData = NULL; + } +} + +bool FlacInputStream::allocateBuffer(uint minSamples) { + uint allocateSize = minSamples / getChannels(); + /** insert funky algorythm for optimum buffersize here */ + allocateSize = MIN(_streaminfo.max_blocksize, MAX(_streaminfo.min_blocksize, allocateSize)); + allocateSize += 8 - (allocateSize % 8); // make sure its an nice even amount + allocateSize *= getChannels(); + + deleteBuffer(); + + _preBuffer.bufData = new bufType[allocateSize]; + if (_preBuffer.bufData != NULL) { + _preBuffer.bufSize = allocateSize; + return true; + } + return false; +} + +void FlacInputStream::setBestConvertBufferMethod() +{ + PFCONVERTBUFFERS tempMethod = &FlacInputStream::convertBuffersGeneric; + + const uint kNumChannels = getChannels(); + const uint8 kNumBits = (uint8)_streaminfo.bits_per_sample; + + assert(kNumChannels >= 1); + assert(kNumBits >= 4 && kNumBits <=32); + + if (kNumChannels == 1) { + if (kNumBits == 8) + tempMethod = &FlacInputStream::convertBuffersMono8Bit; + if (kNumBits == BUFTYPE_BITS) + tempMethod = &FlacInputStream::convertBuffersMonoNS; + } else if (kNumChannels == 2) { + if (kNumBits == 8) + tempMethod = &FlacInputStream::convertBuffersStereo8Bit; + if (kNumBits == BUFTYPE_BITS) + tempMethod = &FlacInputStream::convertBuffersStereoNS; + } /* else ... */ + + _methodConvertBuffers = tempMethod; +} + +// 1 channel, no scaling +void FlacInputStream::convertBuffersMonoNS(bufType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) +{ + assert(numChannels == 1); + assert(numBits == BUFTYPE_BITS); + + FLAC__int32 const* inChannel1 = inChannels[0]; + + while (numSamples >= 4) { + bufDestination[0] = static_cast<bufType>(inChannel1[0]); + bufDestination[1] = static_cast<bufType>(inChannel1[1]); + bufDestination[2] = static_cast<bufType>(inChannel1[2]); + bufDestination[3] = static_cast<bufType>(inChannel1[3]); + bufDestination += 4; + inChannel1 += 4; + numSamples -= 4; + } + + for (; numSamples > 0; --numSamples) { + *bufDestination++ = static_cast<bufType>(*inChannel1++); + } + + inChannels[0] = inChannel1; + assert(numSamples == 0); // dint copy too many samples +} + +// 1 channel, scaling from 8Bit +void FlacInputStream::convertBuffersMono8Bit(bufType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) +{ + assert(numChannels == 1); + assert(numBits == 8); + assert(8 < BUFTYPE_BITS); + + FLAC__int32 const* inChannel1 = inChannels[0]; + + while (numSamples >= 4) { + bufDestination[0] = static_cast<bufType>(inChannel1[0]) << (BUFTYPE_BITS - 8); + bufDestination[1] = static_cast<bufType>(inChannel1[1]) << (BUFTYPE_BITS - 8); + bufDestination[2] = static_cast<bufType>(inChannel1[2]) << (BUFTYPE_BITS - 8); + bufDestination[3] = static_cast<bufType>(inChannel1[3]) << (BUFTYPE_BITS - 8); + bufDestination += 4; + inChannel1 += 4; + numSamples -= 4; + } + + for (; numSamples > 0; --numSamples) { + *bufDestination++ = static_cast<bufType>(*inChannel1++) << (BUFTYPE_BITS - 8); + } + + inChannels[0] = inChannel1; + assert(numSamples == 0); // dint copy too many samples +} + +// 2 channels, no scaling +void FlacInputStream::convertBuffersStereoNS(bufType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) +{ + assert(numChannels == 2); + assert(numBits == BUFTYPE_BITS); + assert(numSamples % 2 == 0); // must be integral multiply of channels + + + FLAC__int32 const* inChannel1 = inChannels[0]; // Left Channel + FLAC__int32 const* inChannel2 = inChannels[1]; // Right Channel + + while (numSamples >= 2*2) { + bufDestination[0] = static_cast<bufType>(inChannel1[0]); + bufDestination[1] = static_cast<bufType>(inChannel2[0]); + bufDestination[2] = static_cast<bufType>(inChannel1[1]); + bufDestination[3] = static_cast<bufType>(inChannel2[1]); + bufDestination += 2 * 2; + inChannel1 += 2; + inChannel2 += 2; + numSamples -= 2 * 2; + } + + while (numSamples > 0) { + bufDestination[0] = static_cast<bufType>(*inChannel1++); + bufDestination[1] = static_cast<bufType>(*inChannel2++); + bufDestination += 2; + numSamples -= 2; + } + + inChannels[0] = inChannel1; + inChannels[1] = inChannel2; + assert(numSamples == 0); // dint copy too many samples +} + +// 2 channels, scaling from 8Bit +void FlacInputStream::convertBuffersStereo8Bit(bufType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) +{ + assert(numChannels == 2); + assert(numBits == 8); + assert(numSamples % 2 == 0); // must be integral multiply of channels + assert(8 < BUFTYPE_BITS); + + FLAC__int32 const* inChannel1 = inChannels[0]; // Left Channel + FLAC__int32 const* inChannel2 = inChannels[1]; // Right Channel + + while (numSamples >= 2*2) { + bufDestination[0] = static_cast<bufType>(inChannel1[0]) << (BUFTYPE_BITS - 8); + bufDestination[1] = static_cast<bufType>(inChannel2[0]) << (BUFTYPE_BITS - 8); + bufDestination[2] = static_cast<bufType>(inChannel1[1]) << (BUFTYPE_BITS - 8); + bufDestination[3] = static_cast<bufType>(inChannel2[1]) << (BUFTYPE_BITS - 8); + bufDestination += 2 * 2; + inChannel1 += 2; + inChannel2 += 2; + numSamples -= 2 * 2; + } + + while (numSamples > 0) { + bufDestination[0] = static_cast<bufType>(*inChannel1++) << (BUFTYPE_BITS - 8); + bufDestination[1] = static_cast<bufType>(*inChannel2++) << (BUFTYPE_BITS - 8); + bufDestination += 2; + numSamples -= 2; + } + + inChannels[0] = inChannel1; + inChannels[1] = inChannel2; + assert(numSamples == 0); // dint copy too many samples +} + +// all Purpose-conversion - slowest of em all +void FlacInputStream::convertBuffersGeneric(bufType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) +{ + assert(numSamples % numChannels == 0); // must be integral multiply of channels + + if (numBits < BUFTYPE_BITS) { + const uint8 kPower = (uint8)(BUFTYPE_BITS - numBits); + + for (; numSamples > 0; numSamples -= numChannels) { + for (uint i = 0; i < numChannels; ++i) + *bufDestination++ = static_cast<bufType>(*(inChannels[i]++)) << kPower; + } + } else if (numBits > BUFTYPE_BITS) { + const uint8 kPower = (uint8)(numBits - BUFTYPE_BITS); + + for (; numSamples > 0; numSamples -= numChannels) { + for (uint i = 0; i < numChannels; ++i) + *bufDestination++ = static_cast<bufType>(*(inChannels[i]++) >> kPower) ; + } + } else { + for (; numSamples > 0; numSamples -= numChannels) { + for (uint i = 0; i < numChannels; ++i) + *bufDestination++ = static_cast<bufType>(*(inChannels[i]++)); + } + } + + assert(numSamples == 0); // dint copy too many samples +} + +inline ::FLAC__StreamDecoderWriteStatus FlacInputStream::callbackWrite(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]) { + assert(frame->header.channels == _streaminfo.channels); + assert(frame->header.sample_rate == _streaminfo.sample_rate); + assert(frame->header.bits_per_sample == _streaminfo.bits_per_sample); + assert(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER || _streaminfo.min_blocksize == _streaminfo.max_blocksize); + + assert(_preBuffer.bufFill == 0); // we dont append data + + uint nSamples = frame->header.blocksize; + const uint kNumChannels = getChannels(); + const uint8 kNumBits = (uint8)_streaminfo.bits_per_sample; + + assert(_requestedSamples % kNumChannels == 0); // must be integral multiply of channels + + const FLAC__uint64 firstSampleNumber = (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER) ? + frame->header.number.sample_number : (static_cast<FLAC__uint64>(frame->header.number.frame_number)) * _streaminfo.max_blocksize; + + if (_lastSample != 0 && firstSampleNumber + nSamples >= _lastSample) { + nSamples = (uint)(firstSampleNumber >= _lastSample ? 0 : _lastSample - firstSampleNumber); + _requestedSamples = MIN(_requestedSamples, nSamples * kNumChannels); + _lastSampleWritten = true; + } + + nSamples *= kNumChannels; + + const FLAC__int32 *inChannels[MAX_OUTPUT_CHANNELS] = { buffer[0] }; // one channel is a given... + for (uint i = 1; i < kNumChannels; ++i) + inChannels[i] = buffer[i]; + + + // writing DIRECTLY to the Buffer ScummVM provided + if (_requestedSamples > 0) { + assert(_requestedSamples % kNumChannels == 0); // must be integral multiply of channels + assert(_outBuffer != NULL); + + const uint copySamples = MIN(_requestedSamples,nSamples); + (*_methodConvertBuffers)(_outBuffer, inChannels, copySamples, kNumChannels, kNumBits); + + _requestedSamples -= copySamples; + nSamples -= copySamples; + _outBuffer += copySamples; + } + + // checking if Buffer fits + if (_preBuffer.bufSize < nSamples) { + if (!allocateBuffer(nSamples)) + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } // optional check if buffer is wasting too much memory ? + + (*_methodConvertBuffers)(_preBuffer.bufData, inChannels, nSamples, kNumChannels, kNumBits); + + _preBuffer.bufFill = nSamples; + _preBuffer.bufReadPos = _preBuffer.bufData; + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +inline ::FLAC__SeekableStreamDecoderSeekStatus FlacInputStream::callbackSeek(FLAC__uint64 absoluteByteOffset) { + FLAC__uint64 newPos = absoluteByteOffset + _fileInfo.fileStartPos; + const bool result = (newPos < _fileInfo.fileEndPos); + + if (result) + _fileInfo.filePos = static_cast<uint32>(newPos); + + return result ? FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK : FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR; + +} + +inline ::FLAC__SeekableStreamDecoderTellStatus FlacInputStream::callbackTell(FLAC__uint64 *absoluteByteOffset) { + /*if () + return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR;*/ + *absoluteByteOffset = static_cast<FLAC__uint64>(_fileInfo.filePos-_fileInfo.fileStartPos); + return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK; +} + +inline ::FLAC__SeekableStreamDecoderLengthStatus FlacInputStream::callbackLength(FLAC__uint64 *streamLength) { + if (_fileInfo.fileStartPos > _fileInfo.fileEndPos) + return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR; + + *streamLength = static_cast<FLAC__uint64>(_fileInfo.fileEndPos - _fileInfo.fileStartPos); + return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK; +} + +inline bool FlacInputStream::callbackEOF() { + return _fileInfo.filePos >= _fileInfo.fileEndPos; +} + + +inline void FlacInputStream::callbackMetadata(const ::FLAC__StreamMetadata *metadata) { + assert(isValid()); + assert(metadata->type == FLAC__METADATA_TYPE_STREAMINFO); // others arent really interesting + + _streaminfo = metadata->data.stream_info; + setBestConvertBufferMethod(); // should be set after getting stream-information. FLAC always parses the info first +} +inline void FlacInputStream::callbackError(::FLAC__StreamDecoderErrorStatus status) { + // some of these are non-critical-Errors + debug(1, "FlacInputStream: An error occured while decoding. DecoderState is: %s", + FLAC__StreamDecoderErrorStatusString[status]); +} + +/* Static Callback Wrappers */ +::FLAC__SeekableStreamDecoderReadStatus FlacInputStream::callWrapRead(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], uint *bytes, void *clientData) { + assert(0 != clientData); + FlacInputStream *instance = reinterpret_cast<FlacInputStream *>(clientData); + assert(0 != instance); + return instance->callbackRead(buffer, bytes); +} + +::FLAC__SeekableStreamDecoderSeekStatus FlacInputStream::callWrapSeek(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 absoluteByteOffset, void *clientData) { + assert(0 != clientData); + FlacInputStream *instance = reinterpret_cast<FlacInputStream *>(clientData); + assert(0 != instance); + return instance->callbackSeek(absoluteByteOffset); +} + +::FLAC__SeekableStreamDecoderTellStatus FlacInputStream::callWrapTell(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *absoluteByteOffset, void *clientData) { + assert(0 != clientData); + FlacInputStream *instance = reinterpret_cast<FlacInputStream *>(clientData); + assert(0 != instance); + return instance->callbackTell(absoluteByteOffset); +} + +::FLAC__SeekableStreamDecoderLengthStatus FlacInputStream::callWrapLength(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *streamLength, void *clientData) { + assert(0 != clientData); + FlacInputStream *instance = reinterpret_cast<FlacInputStream *>(clientData); + assert(0 != instance); + return instance->callbackLength(streamLength); +} + +FLAC__bool FlacInputStream::callWrapEOF(const ::FLAC__SeekableStreamDecoder *decoder, void *clientData) { + assert(0 != clientData); + FlacInputStream *instance = reinterpret_cast<FlacInputStream *>(clientData); + assert(0 != instance); + return instance->callbackEOF(); +} + +::FLAC__StreamDecoderWriteStatus FlacInputStream::callWrapWrite(const ::FLAC__SeekableStreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *clientData) { + assert(0 != clientData); + FlacInputStream *instance = reinterpret_cast<FlacInputStream *>(clientData); + assert(0 != instance); + return instance->callbackWrite(frame, buffer); +} + +void FlacInputStream::callWrapMetadata(const ::FLAC__SeekableStreamDecoder *decoder, const ::FLAC__StreamMetadata *metadata, void *clientData) { + assert(0 != clientData); + FlacInputStream *instance = reinterpret_cast<FlacInputStream *>(clientData); + assert(0 != instance); + instance->callbackMetadata(metadata); +} + +void FlacInputStream::callWrapError(const ::FLAC__SeekableStreamDecoder *decoder, ::FLAC__StreamDecoderErrorStatus status, void *clientData) { + assert(0 != clientData); + FlacInputStream *instance = reinterpret_cast<FlacInputStream *>(clientData); + assert(0 != instance); + instance->callbackError(status); +} + + +#pragma mark - +#pragma mark --- Flac Audio CD emulation --- +#pragma mark - + + +class FlacTrackInfo : public DigitalTrackInfo { +private: + File *_file; + FlacInputStream *_firstStream; // avoid having to open the Stream twice the first time + +public: + FlacTrackInfo(File *file); + ~FlacTrackInfo(); + bool error() { return _file == NULL; } + void play(SoundMixer *mixer, PlayingSoundHandle *handle, int startFrame, int duration); +}; + +FlacTrackInfo::FlacTrackInfo(File *file) : _file(NULL), _firstStream(NULL) +{ + FlacInputStream *tempStream = new FlacInputStream(file); + /* first time the file will be tested, but not used */ + if (tempStream->init()) { + _firstStream = tempStream; + _file = file; + } else + delete tempStream; +} + +void FlacTrackInfo::play(SoundMixer *mixer, PlayingSoundHandle *handle, int startFrame, int duration) { + if (error()) { + debug(1, "FlacTrackInfo::play: invalid state, method should not been called"); + } + + FlacInputStream *flac; + + if (_firstStream != NULL) { + flac = _firstStream; + _firstStream = NULL; + } else { + flac = new FlacInputStream(_file); + flac->init(); + } + + if (flac->isStreamDecoderReady()) { + const FLAC__StreamMetadata_StreamInfo &info = flac->getStreamInfo(); + flac->setLastSample(static_cast<FLAC__uint64>(startFrame + duration) * (info.sample_rate / 75)); + + if (flac->seekAbsolute(static_cast<FLAC__uint64>(startFrame) * (info.sample_rate / 75))) { + mixer->playInputStream(handle, flac, true); + return; + } + // startSample is beyond the existing Samples + debug(1, "FlacTrackInfo: Audiostream %s coud not seek to frame %d (ca %d secs)", _file->name(), startFrame, startFrame/75); + flac->finish(); + } + delete flac; +} + +FlacTrackInfo::~FlacTrackInfo() +{ + if (_firstStream) + delete _firstStream; + if (_file) + delete _file; +} + +DigitalTrackInfo* getFlacTrack(int track) +{ + assert(track >=1); + char track_name[32]; + File *file = new File(); + + sprintf(track_name, "track%d.flac", track); + file->open(track_name); + + if (file->isOpen()) { + FlacTrackInfo *trackInfo = new FlacTrackInfo(file); + if (!trackInfo->error()) + return trackInfo; + delete trackInfo; + } + + sprintf(track_name, "track%d.fla", track); + file->open(track_name); + + if (file->isOpen()) { + FlacTrackInfo *trackInfo = new FlacTrackInfo(file); + if (!trackInfo->error()) + return trackInfo; + delete trackInfo; + } + + + delete file; + return NULL; +} + +AudioStream *makeFlacStream(File *file, uint32 length) +{ + assert(file != NULL); + uint32 start = file->pos(); + + FlacInputStream *flac = new FlacInputStream(file, start, start + length); + if (flac->init()) + return flac; + + delete flac; + return NULL; +} + + +#endif // #ifdef USE_FLAC diff --git a/sound/flac.h b/sound/flac.h new file mode 100644 index 0000000000..055a9fa2a7 --- /dev/null +++ b/sound/flac.h @@ -0,0 +1,39 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003-2004 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#ifndef SOUND_FLAC_H +#define SOUND_FLAC_H + +#include "stdafx.h" +#include "common/scummsys.h" + +#ifdef USE_FLAC + +class AudioStream; +class DigitalTrackInfo; +class File; + +DigitalTrackInfo *getFlacTrack(int track); + +AudioStream *makeFlacStream(File *file, uint32 size); + +#endif // #ifdef USE_FLAC +#endif // #ifndef SOUND_FLAC_H diff --git a/sound/mixer.cpp b/sound/mixer.cpp index af3bbc3156..c7260ee02e 100644 --- a/sound/mixer.cpp +++ b/sound/mixer.cpp @@ -29,6 +29,7 @@ #include "sound/audiostream.h" #include "sound/mp3.h" #include "sound/vorbis.h" +#include "sound/flac.h" #pragma mark - @@ -269,6 +270,14 @@ void SoundMixer::playVorbis(PlayingSoundHandle *handle, File *file, uint32 size, } #endif +#ifdef USE_FLAC +void SoundMixer::playFlac(PlayingSoundHandle *handle, File *file, uint32 size, byte volume, int8 balance, int id) { + // Create the input stream + AudioStream *input = makeFlacStream(file, size); + playInputStream(handle, input, false, volume, balance, id); +} +#endif + void SoundMixer::playInputStream(PlayingSoundHandle *handle, AudioStream *input, bool isMusic, byte volume, int8 balance, int id, bool autofreeStream) { Common::StackLock lock(_mutex); diff --git a/sound/mixer.h b/sound/mixer.h index 3b26f0f763..a559e3faa7 100644 --- a/sound/mixer.h +++ b/sound/mixer.h @@ -112,6 +112,9 @@ public: #ifdef USE_VORBIS void playVorbis(PlayingSoundHandle *handle, File *file, uint32 size, byte volume = 255, int8 balance = 0, int id = -1); #endif +#ifdef USE_FLAC + void playFlac(PlayingSoundHandle *handle, File *file, uint32 size, byte volume = 255, int8 balance = 0, int id = -1); +#endif void playInputStream(PlayingSoundHandle *handle, AudioStream *input, bool isMusic, byte volume = 255, int8 balance = 0, int id = -1, bool autofreeStream = true); diff --git a/sound/module.mk b/sound/module.mk index 61bf20f658..12b5da5868 100644 --- a/sound/module.mk +++ b/sound/module.mk @@ -13,7 +13,8 @@ MODULE_OBJS := \ sound/mpu401.o \ sound/rate.o \ sound/voc.o \ - sound/vorbis.o + sound/vorbis.o \ + sound/flac.o # sound/resample.o \ MODULE_DIRS += \ diff --git a/sound/mp3.cpp b/sound/mp3.cpp index 556a43cdb3..27938b7aa0 100644 --- a/sound/mp3.cpp +++ b/sound/mp3.cpp @@ -261,7 +261,7 @@ int MP3InputStream::readBuffer(int16 *buffer, const int numSamples) { return samples; } -AudioStream *makeMP3Stream(File *file, uint size) { +AudioStream *makeMP3Stream(File *file, uint32 size) { return new MP3InputStream(file, mad_timer_zero, size); } @@ -385,8 +385,21 @@ MP3TrackInfo::~MP3TrackInfo() { _file->close(); } -DigitalTrackInfo *makeMP3TrackInfo(File *file) { - return new MP3TrackInfo(file); +DigitalTrackInfo *getMP3Track(int track) { + char track_name[32]; + File *file = new File(); + + sprintf(track_name, "track%d.mp3", track); + file->open(track_name); + + if (file->isOpen()) { + MP3TrackInfo *trackInfo = new MP3TrackInfo(file); + if (!trackInfo->error()) + return trackInfo; + delete trackInfo; + } + delete file; + return NULL; } diff --git a/sound/mp3.h b/sound/mp3.h index b6ec46628d..a782322966 100644 --- a/sound/mp3.h +++ b/sound/mp3.h @@ -31,9 +31,9 @@ class AudioStream; class DigitalTrackInfo; class File; -DigitalTrackInfo *makeMP3TrackInfo(File *file); +DigitalTrackInfo *getMP3Track(int track); -AudioStream *makeMP3Stream(File *file, uint size); +AudioStream *makeMP3Stream(File *file, uint32 size); #endif diff --git a/sound/vorbis.cpp b/sound/vorbis.cpp index 4c515257af..6f2cd53463 100644 --- a/sound/vorbis.cpp +++ b/sound/vorbis.cpp @@ -156,8 +156,21 @@ VorbisTrackInfo::~VorbisTrackInfo() { } } -DigitalTrackInfo *makeVorbisTrackInfo(File *file) { - return new VorbisTrackInfo(file); +DigitalTrackInfo *getVorbisTrack(int track) { + char track_name[32]; + File *file = new File(); + + sprintf(track_name, "track%d.ogg", track); + file->open(track_name); + + if (file->isOpen()) { + VorbisTrackInfo *trackInfo = new VorbisTrackInfo(file); + if (!trackInfo->error()) + return trackInfo; + delete trackInfo; + } + delete file; + return NULL; } #pragma mark - diff --git a/sound/vorbis.h b/sound/vorbis.h index 9c404e46e8..8b2b5ecee0 100644 --- a/sound/vorbis.h +++ b/sound/vorbis.h @@ -31,7 +31,7 @@ class AudioStream; class DigitalTrackInfo; class File; -DigitalTrackInfo *makeVorbisTrackInfo(File *file); +DigitalTrackInfo *getVorbisTrack(int track); AudioStream *makeVorbisStream(File *file, uint32 size); diff --git a/sword1/animation.cpp b/sword1/animation.cpp index 00e5658dc2..960e8f8306 100644 --- a/sword1/animation.cpp +++ b/sword1/animation.cpp @@ -21,11 +21,9 @@ #include "common/stdafx.h" #include "common/file.h" -#include "sound/vorbis.h" -#include "sound/mp3.h" - #include "sword1/animation.h" + #define MOVIE_WIDTH 640 #define MOVIE_HEIGHT 400 @@ -139,24 +137,13 @@ bool AnimationState::init(const char *name) { ticks = _sys->get_msecs(); /* Play audio - TODO: Sync with video?*/ - sndfile = new File; - -#ifdef USE_VORBIS - sprintf(tempFile, "%s.ogg", name); - if (sndfile->open(tempFile)) - bgSoundStream = makeVorbisStream(sndfile, sndfile->size()); -#endif - -#ifdef USE_MAD - if (!sndfile->isOpen()) { - sprintf(tempFile, "%s.mp3", name); - if (sndfile->open(tempFile)) - bgSoundStream = makeMP3Stream(sndfile, sndfile->size()); + sndfile = new File(); + bgSoundStream = AudioStream::openStreamFile(name, sndfile); + if (bgSoundStream != NULL) { + _snd->playInputStream(&bgSound, bgSoundStream, false, 255, 0, -1, false); + } else { + warning("Cutscene: Could not open Audio Track for %s", name); } -#endif - - if (sndfile->isOpen()) - _snd->playInputStream(&bgSound, bgSoundStream, false, 255, 0, -1, false); return true; #else /* USE_MPEG2 */ diff --git a/sword2/driver/animation.cpp b/sword2/driver/animation.cpp index 1cd2def9f5..31a008cab9 100644 --- a/sword2/driver/animation.cpp +++ b/sword2/driver/animation.cpp @@ -145,24 +145,14 @@ bool AnimationState::init(const char *name) { ticks = _vm->_system->get_msecs(); // Play audio - sndfile = new File; + sndfile = new File(); + bgSoundStream = AudioStream::openStreamFile( name, sndfile ); -#ifdef USE_VORBIS - sprintf(tempFile, "%s.ogg", name); - if (sndfile->open(tempFile)) - bgSoundStream = makeVorbisStream(sndfile, sndfile->size()); -#endif - -#ifdef USE_MAD - if (!sndfile->isOpen()) { - sprintf(tempFile, "%s.mp3", name); - if (sndfile->open(tempFile)) - bgSoundStream = makeMP3Stream(sndfile, sndfile->size()); + if (bgSoundStream != NULL) { + _vm->_mixer->playInputStream(&bgSound, bgSoundStream, false, 255, 0, -1, false); + } else { + warning("Cutscene: Could not open Audio Track for %s", name); } -#endif - - if (sndfile->isOpen()) - _vm->_mixer->playInputStream(&bgSound, bgSoundStream, false, 255, 0, -1, false); return true; #else /* USE_MPEG2 */ |