parent
6db3a8819e
commit
aa6ec62e9d
25 changed files with 1278 additions and 107 deletions
50
README
50
README
|
@ -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.
|
||||
|
||||
|
||||
|
|
31
configure
vendored
31
configure
vendored
|
@ -26,6 +26,7 @@ CXXFLAGS="$CXXFLAGS $CPPFLAGS"
|
|||
|
||||
# default lib behaviour yes/no/auto
|
||||
_vorbis=auto
|
||||
_flac=auto
|
||||
_mad=auto
|
||||
_alsa=auto
|
||||
_zlib=auto
|
||||
|
@ -208,6 +209,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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -29,7 +29,8 @@ namespace Queen {
|
|||
enum {
|
||||
COMPRESSION_NONE = 0,
|
||||
COMPRESSION_MP3 = 1,
|
||||
COMPRESSION_OGG = 2
|
||||
COMPRESSION_OGG = 2,
|
||||
COMPRESSION_FLAC = 3
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
#ifdef USE_VORBIS
|
||||
input = makeVorbisStream(file, file_size);
|
||||
#endif
|
||||
} else {
|
||||
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
|
||||
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_MAD
|
||||
sprintf(buf, "%s.so3", _vm->getGameName());
|
||||
if (!file->open(buf, _vm->getGameDataPath())) {
|
||||
file->open("monster.so3", _vm->getGameDataPath());
|
||||
}
|
||||
#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())
|
||||
_vorbis_mode = false;
|
||||
_sound_mode = kFlacMode;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_MAD
|
||||
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;
|
||||
}
|
||||
#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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
@ -340,6 +373,14 @@ Sound::Sound(const byte game, const GameSpecificSettings *gss, const Common::Str
|
|||
_effects = new VorbisSound(_mixer, file);
|
||||
}
|
||||
}
|
||||
#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;
|
||||
|
|
|
@ -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);
|
||||
for (int i = 0; i < ARRAYSIZE(TRACK_FORMATS)-1 && _track_info[current_index] == NULL; ++i)
|
||||
_track_info[current_index] = TRACK_FORMATS[i].openTrackFunction(track);
|
||||
|
||||
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;
|
||||
}
|
||||
if (_track_info[current_index] != NULL)
|
||||
return current_index;
|
||||
}
|
||||
#endif
|
||||
|
||||
#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;
|
||||
}
|
||||
return current_index;
|
||||
}
|
||||
#endif
|
||||
|
||||
debug(2, "Track %d not available in compressed format", track);
|
||||
return -1;
|
||||
|
|
|
@ -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 -
|
||||
|
|
|
@ -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 {
|
||||
|
|
840
sound/flac.cpp
Normal file
840
sound/flac.cpp
Normal file
|
@ -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
|
39
sound/flac.h
Normal file
39
sound/flac.h
Normal file
|
@ -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
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 += \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 -
|
||||
|
|
|
@ -31,7 +31,7 @@ class AudioStream;
|
|||
class DigitalTrackInfo;
|
||||
class File;
|
||||
|
||||
DigitalTrackInfo *makeVorbisTrackInfo(File *file);
|
||||
DigitalTrackInfo *getVorbisTrack(int track);
|
||||
|
||||
AudioStream *makeVorbisStream(File *file, uint32 size);
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sndfile->isOpen())
|
||||
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);
|
||||
}
|
||||
|
||||
return true;
|
||||
#else /* USE_MPEG2 */
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sndfile->isOpen())
|
||||
if (bgSoundStream != NULL) {
|
||||
_vm->_mixer->playInputStream(&bgSound, bgSoundStream, false, 255, 0, -1, false);
|
||||
} else {
|
||||
warning("Cutscene: Could not open Audio Track for %s", name);
|
||||
}
|
||||
|
||||
return true;
|
||||
#else /* USE_MPEG2 */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue