Patch #885904 (Flac Support) with some tweaks by me

svn-id: r12984
This commit is contained in:
Max Horn 2004-02-22 14:11:16 +00:00
parent 6db3a8819e
commit aa6ec62e9d
25 changed files with 1278 additions and 107 deletions

50
README
View file

@ -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
View file

@ -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

View file

@ -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.

View file

@ -29,7 +29,8 @@ namespace Queen {
enum {
COMPRESSION_NONE = 0,
COMPRESSION_MP3 = 1,
COMPRESSION_OGG = 2
COMPRESSION_OGG = 2,
COMPRESSION_FLAC = 3
};
enum {

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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
};

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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 -

View file

@ -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
View 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
View 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

View file

@ -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);

View file

@ -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);

View file

@ -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 += \

View file

@ -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;
}

View file

@ -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

View file

@ -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 -

View file

@ -31,7 +31,7 @@ class AudioStream;
class DigitalTrackInfo;
class File;
DigitalTrackInfo *makeVorbisTrackInfo(File *file);
DigitalTrackInfo *getVorbisTrack(int track);
AudioStream *makeVorbisStream(File *file, uint32 size);

View file

@ -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 */

View file

@ -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 */