2003-11-29 12:11:01 +00:00
/* ScummVM - Scumm Interpreter
2006-01-18 17:39:49 +00:00
* Copyright ( C ) 2003 - 2006 The ScummVM project
2003-11-29 12:11:01 +00:00
*
* 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
2005-10-18 01:30:26 +00:00
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
2003-11-29 12:11:01 +00:00
*
2006-02-11 10:05:31 +00:00
* $ URL $
* $ Id $
2003-11-29 12:11:01 +00:00
*
*/
# include "sound/vorbis.h"
2004-01-03 01:25:45 +00:00
2005-08-10 12:42:56 +00:00
# ifdef USE_VORBIS
2004-01-03 01:25:45 +00:00
2003-11-29 12:11:01 +00:00
# include "common/file.h"
# include "common/util.h"
2004-01-03 01:25:45 +00:00
# include "sound/audiostream.h"
# include "sound/audiocd.h"
2005-06-30 09:14:36 +00:00
# ifdef USE_TREMOR
2006-04-07 10:44:20 +00:00
# ifdef __GP32__ // GP32 uses custom libtremor
# include <ivorbisfile.h>
# else
2005-06-30 09:14:36 +00:00
# include <tremor/ivorbisfile.h>
2006-04-07 10:44:20 +00:00
# endif
2005-06-30 09:14:36 +00:00
# else
2004-01-03 01:25:45 +00:00
# include <vorbis/vorbisfile.h>
2005-06-30 09:14:36 +00:00
# endif
2004-01-03 01:25:45 +00:00
2005-05-10 22:56:25 +00:00
using Common : : File ;
2004-01-03 01:25:45 +00:00
2006-04-29 22:33:31 +00:00
namespace Audio {
2005-05-10 22:56:25 +00:00
static AudioStream * makeVorbisStream ( OggVorbis_File * file , int duration ) ;
2004-01-03 01:25:45 +00:00
2003-11-29 12:11:01 +00:00
// These are wrapper functions to allow using a File object to
// provide data to the OggVorbis_File object.
struct file_info {
File * file ;
int start , curr_pos ;
size_t len ;
} ;
static size_t read_wrap ( void * ptr , size_t size , size_t nmemb , void * datasource ) {
file_info * f = ( file_info * ) datasource ;
int result ;
2005-12-01 19:14:38 +00:00
# ifdef __SYMBIAN32__
2005-12-02 18:36:21 +00:00
// For symbian we must check that an alternative file pointer is created, see if its open
// If not re-open file and seek to the last read position
if ( f - > file & & ! f - > file - > isOpen ( ) ) {
f - > file - > open ( f - > file - > name ( ) ) ;
f - > file - > seek ( f - > curr_pos ) ;
}
2005-12-01 19:14:38 +00:00
# endif
2003-11-29 12:11:01 +00:00
nmemb * = size ;
if ( f - > curr_pos > ( int ) f - > len )
nmemb = 0 ;
else if ( nmemb > f - > len - f - > curr_pos )
nmemb = f - > len - f - > curr_pos ;
2004-03-21 16:59:10 +00:00
// There is no guarantee that the Vorbis stream is alone in accessing
// the file, so make sure the current position is what we think it is.
f - > file - > seek ( f - > start + f - > curr_pos ) ;
2003-11-29 12:11:01 +00:00
result = f - > file - > read ( ptr , nmemb ) ;
2005-12-01 19:14:38 +00:00
# ifdef __SYMBIAN32__
2005-12-02 18:36:21 +00:00
// For symbian we now store the last read position and then close the file
if ( f - > file ) {
f - > file - > close ( ) ;
}
2005-12-01 19:14:38 +00:00
# endif
2003-11-29 12:11:01 +00:00
if ( result = = - 1 ) {
f - > curr_pos = f - > file - > pos ( ) - f - > start ;
return ( size_t ) - 1 ;
} else {
f - > curr_pos + = result ;
return result / size ;
}
}
static int seek_wrap ( void * datasource , ogg_int64_t offset , int whence ) {
file_info * f = ( file_info * ) datasource ;
if ( whence = = SEEK_SET )
offset + = f - > start ;
else if ( whence = = SEEK_END ) {
offset + = f - > start + f - > len ;
whence = SEEK_SET ;
}
2005-12-01 19:14:38 +00:00
# ifdef __SYMBIAN32__
2005-12-02 18:36:21 +00:00
// For symbian we must check that an alternative file pointer is created, see if its open
// If not re-open file and seek to the last read position
if ( f - > file & & ! f - > file - > isOpen ( ) ) {
f - > file - > open ( f - > file - > name ( ) ) ;
f - > file - > seek ( f - > curr_pos ) ;
}
2005-12-01 19:14:38 +00:00
# endif
2003-11-29 12:11:01 +00:00
f - > file - > seek ( offset , whence ) ;
f - > curr_pos = f - > file - > pos ( ) - f - > start ;
2005-12-01 19:14:38 +00:00
# ifdef __SYMBIAN32__
2006-04-29 22:33:31 +00:00
// For symbian we now store the last read position and then close the file
2005-12-02 18:36:21 +00:00
if ( f - > file ) {
f - > file - > close ( ) ;
}
2005-12-01 19:14:38 +00:00
# endif
2003-11-29 12:11:01 +00:00
return f - > curr_pos ;
}
static int close_wrap ( void * datasource ) {
file_info * f = ( file_info * ) datasource ;
2004-06-28 22:35:22 +00:00
f - > file - > decRef ( ) ;
2003-11-29 12:11:01 +00:00
delete f ;
return 0 ;
}
static long tell_wrap ( void * datasource ) {
file_info * f = ( file_info * ) datasource ;
return f - > curr_pos ;
}
static ov_callbacks g_File_wrap = {
read_wrap , seek_wrap , close_wrap , tell_wrap
} ;
2006-04-30 13:40:35 +00:00
# pragma mark -
# pragma mark --- Ogg Vorbis Audio CD emulation ---
# pragma mark -
class VorbisTrackInfo : public DigitalTrackInfo {
private :
File * _file ;
OggVorbis_File _ov_file ;
bool _error_flag ;
public :
VorbisTrackInfo ( File * file ) ;
~ VorbisTrackInfo ( ) ;
bool openTrack ( ) ;
bool error ( ) { return _error_flag ; }
void play ( Audio : : Mixer * mixer , Audio : : SoundHandle * handle , int startFrame , int duration ) ;
} ;
2003-11-29 12:11:01 +00:00
VorbisTrackInfo : : VorbisTrackInfo ( File * file ) {
2005-12-03 21:29:13 +00:00
//debug(5, "" __FILE__ ":%i", __LINE__);
2005-07-30 21:11:48 +00:00
2004-06-29 23:27:18 +00:00
_file = file ;
if ( openTrack ( ) ) {
warning ( " Invalid file format " ) ;
_error_flag = true ;
_file = 0 ;
} else {
_error_flag = false ;
_file - > incRef ( ) ;
ov_clear ( & _ov_file ) ;
}
}
bool VorbisTrackInfo : : openTrack ( ) {
2005-12-03 21:29:13 +00:00
//debug(5, "" __FILE__ ":%i", __LINE__);
2004-06-29 23:27:18 +00:00
assert ( _file ) ;
2003-11-29 12:11:01 +00:00
file_info * f = new file_info ;
2005-12-01 19:14:38 +00:00
# if defined(__SYMBIAN32__)
2005-12-02 18:36:21 +00:00
// Symbian can't share filehandles between different threads.
// So create a new file and seek that to the other filehandles position
f - > file = new File ;
f - > file - > open ( _file - > name ( ) ) ;
f - > file - > seek ( _file - > pos ( ) ) ;
2005-12-01 19:14:38 +00:00
# else
2005-12-02 18:36:21 +00:00
f - > file = _file ;
2005-12-01 19:14:38 +00:00
# endif
2003-11-29 12:11:01 +00:00
f - > start = 0 ;
2004-06-29 23:27:18 +00:00
f - > len = _file - > size ( ) ;
f - > curr_pos = 0 ;
_file - > seek ( 0 ) ;
2005-07-30 21:11:48 +00:00
2004-06-29 23:27:18 +00:00
bool err = ( ov_open_callbacks ( ( void * ) f , & _ov_file , NULL , 0 , g_File_wrap ) < 0 ) ;
if ( err ) {
2005-12-01 19:14:38 +00:00
# ifdef __SYMBIAN32__
2005-12-02 18:36:21 +00:00
delete f - > file ;
2005-12-01 19:14:38 +00:00
# endif
2003-11-29 12:11:01 +00:00
delete f ;
} else {
2005-12-01 19:14:38 +00:00
# ifndef __SYMBIAN32__
2005-12-02 18:36:21 +00:00
_file - > incRef ( ) ;
2005-12-01 19:14:38 +00:00
# endif
2006-04-29 22:33:31 +00:00
}
2004-06-29 23:27:18 +00:00
return err ;
}
VorbisTrackInfo : : ~ VorbisTrackInfo ( ) {
2005-12-03 21:29:13 +00:00
//debug(5, "" __FILE__ ":%i", __LINE__);
2004-06-29 23:27:18 +00:00
if ( ! _error_flag ) {
ov_clear ( & _ov_file ) ;
_file - > decRef ( ) ;
2003-11-29 12:11:01 +00:00
}
}
2005-05-11 00:01:44 +00:00
void VorbisTrackInfo : : play ( Audio : : Mixer * mixer , Audio : : SoundHandle * handle , int startFrame , int duration ) {
2005-12-03 21:29:13 +00:00
//debug(5, "" __FILE__ ":%i", __LINE__);
bool err = openTrack ( ) ; err = err ; //satisfy unused variable
2004-06-29 23:27:18 +00:00
assert ( ! err ) ;
2005-12-01 19:14:38 +00:00
# ifdef USE_TREMOR // In Tremor, the ov_time_seek() and ov_time_seek_page() calls take seeking positions in milliseconds as 64 bit integers, rather than in seconds as doubles as in Vorbisfile.
# if defined(__SYMBIAN32__) && defined(__GCC32__) // SumthinWicked says: fixing "relocation truncated to fit: ARM_26 __fixdfdi" during linking on GCC, see portdefs.h
2005-12-02 18:36:21 +00:00
ov_time_seek ( & _ov_file , ( ogg_int64_t ) scumm_fixdfdi ( startFrame / 75.0 * 1000 ) ) ;
2005-12-01 19:14:38 +00:00
# else
2005-12-02 18:36:21 +00:00
ov_time_seek ( & _ov_file , ( ogg_int64_t ) ( startFrame / 75.0 * 1000 ) ) ;
2005-12-01 19:14:38 +00:00
# endif
2003-11-29 12:11:01 +00:00
# else
2006-04-29 22:33:31 +00:00
ov_time_seek ( & _ov_file , startFrame / 75.0 ) ;
2003-11-29 12:11:01 +00:00
# endif
2004-01-03 01:25:45 +00:00
2004-01-03 14:10:13 +00:00
AudioStream * input = makeVorbisStream ( & _ov_file , duration * ov_info ( & _ov_file , - 1 ) - > rate / 75 ) ;
2005-05-10 23:48:48 +00:00
mixer - > playInputStream ( Audio : : Mixer : : kMusicSoundType , handle , input ) ;
2003-11-29 12:11:01 +00:00
}
2004-02-22 14:11:16 +00:00
DigitalTrackInfo * getVorbisTrack ( int track ) {
2005-12-03 21:29:13 +00:00
//debug(5, "" __FILE__ ":%i", __LINE__);
2004-02-22 14:11:16 +00:00
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 ) ;
2004-06-29 23:27:18 +00:00
file - > decRef ( ) ;
2004-02-22 14:11:16 +00:00
if ( ! trackInfo - > error ( ) )
return trackInfo ;
delete trackInfo ;
}
delete file ;
return NULL ;
2004-01-03 01:25:45 +00:00
}
2003-12-19 00:32:47 +00:00
# pragma mark -
# pragma mark --- Ogg Vorbis stream ---
# pragma mark -
2004-01-03 14:10:13 +00:00
class VorbisInputStream : public AudioStream {
2003-12-19 00:32:47 +00:00
OggVorbis_File * _ov_file ;
int _end_pos ;
int _numChannels ;
int16 _buffer [ 4096 ] ;
const int16 * _bufferEnd ;
const int16 * _pos ;
2004-09-04 10:12:22 +00:00
bool _deleteFileAfterUse ;
2005-07-30 21:11:48 +00:00
2003-12-19 00:32:47 +00:00
void refill ( ) ;
inline bool eosIntern ( ) const ;
public :
2004-09-04 10:12:22 +00:00
VorbisInputStream ( OggVorbis_File * file , int duration , bool deleteFileAfterUse ) ;
2004-06-28 22:35:22 +00:00
~ VorbisInputStream ( ) ;
2003-12-19 00:32:47 +00:00
int readBuffer ( int16 * buffer , const int numSamples ) ;
2003-12-19 01:30:19 +00:00
bool endOfData ( ) const { return eosIntern ( ) ; }
2003-12-19 00:32:47 +00:00
bool isStereo ( ) const { return _numChannels > = 2 ; }
2005-07-30 21:11:48 +00:00
2003-12-19 00:32:47 +00:00
int getRate ( ) const { return ov_info ( _ov_file , - 1 ) - > rate ; }
2004-01-17 14:20:32 +00:00
2003-12-19 00:32:47 +00:00
} ;
2005-07-30 21:11:48 +00:00
VorbisInputStream : : VorbisInputStream ( OggVorbis_File * file , int duration , bool deleteFileAfterUse )
2004-09-04 10:12:22 +00:00
: _ov_file ( file ) ,
_bufferEnd ( _buffer + ARRAYSIZE ( _buffer ) ) ,
_deleteFileAfterUse ( deleteFileAfterUse ) {
2005-12-03 21:29:13 +00:00
//debug(5, "" __FILE__ ":%i", __LINE__);
2003-12-19 00:32:47 +00:00
// Check the header, determine if this is a stereo stream
_numChannels = ov_info ( _ov_file , - 1 ) - > channels ;
// Determine the end position
if ( duration )
_end_pos = ov_pcm_tell ( _ov_file ) + duration ;
else
_end_pos = ov_pcm_total ( _ov_file , - 1 ) ;
// Read in initial data
refill ( ) ;
}
2004-06-28 22:35:22 +00:00
VorbisInputStream : : ~ VorbisInputStream ( ) {
2005-12-03 21:29:13 +00:00
//debug(5, "" __FILE__ ":%i", __LINE__);
2004-06-28 22:35:22 +00:00
ov_clear ( _ov_file ) ;
2004-09-04 10:12:22 +00:00
if ( _deleteFileAfterUse )
delete _ov_file ;
2004-06-28 22:35:22 +00:00
}
2003-12-19 00:32:47 +00:00
inline bool VorbisInputStream : : eosIntern ( ) const {
2005-12-03 21:29:13 +00:00
//debug(5, "" __FILE__ ":%i", __LINE__);
2003-12-19 00:32:47 +00:00
return _pos > = _bufferEnd ;
}
int VorbisInputStream : : readBuffer ( int16 * buffer , const int numSamples ) {
2005-12-03 21:29:13 +00:00
//debug(5, "" __FILE__ ":%i", __LINE__);
2003-12-19 00:32:47 +00:00
int samples = 0 ;
while ( samples < numSamples & & ! eosIntern ( ) ) {
2004-01-18 20:57:45 +00:00
const int len = MIN ( numSamples - samples , ( int ) ( _bufferEnd - _pos ) ) ;
2003-12-19 00:32:47 +00:00
memcpy ( buffer , _pos , len * 2 ) ;
buffer + = len ;
_pos + = len ;
samples + = len ;
if ( _pos > = _bufferEnd ) {
refill ( ) ;
}
}
return samples ;
}
void VorbisInputStream : : refill ( ) {
2005-12-03 21:29:13 +00:00
//debug(5, "" __FILE__ ":%i", __LINE__);
2003-12-19 00:32:47 +00:00
// Read the samples
uint len_left = sizeof ( _buffer ) ;
char * read_pos = ( char * ) _buffer ;
while ( len_left > 0 & & _end_pos > ov_pcm_tell ( _ov_file ) ) {
long result = ov_read ( _ov_file , read_pos , len_left ,
2005-12-01 19:14:38 +00:00
# ifndef USE_TREMOR // Tremor ov_read() always returns data as signed 16 bit interleaved PCM in host byte order. As such, it does not take arguments to request specific signedness, byte order or bit depth as in Vorbisfile.
2003-12-19 00:32:47 +00:00
# ifdef SCUMM_BIG_ENDIAN
1 ,
# else
0 ,
# endif
2 , // 16 bit
1 , // signed
# endif
NULL ) ;
if ( result = = OV_HOLE ) {
// Possibly recoverable, just warn about it
warning ( " Corrupted data in Vorbis file " ) ;
} else if ( result < = 0 ) {
if ( result < 0 )
2004-03-28 15:04:48 +00:00
debug ( 1 , " Decode error %ld in Vorbis file " , result ) ;
2003-12-19 00:32:47 +00:00
// Don't delete it yet, that causes problems in
// the CD player emulation code.
memset ( read_pos , 0 , len_left ) ;
break ;
} else {
len_left - = result ;
read_pos + = result ;
}
}
_pos = _buffer ;
_bufferEnd = ( int16 * ) read_pos ;
}
2004-09-04 10:12:22 +00:00
static AudioStream * makeVorbisStream ( OggVorbis_File * file , int duration ) {
2005-12-03 21:29:13 +00:00
//debug(5, "" __FILE__ ":%i", __LINE__);
2004-09-04 10:12:22 +00:00
return new VorbisInputStream ( file , duration , false ) ;
2003-12-19 00:32:47 +00:00
}
2004-01-03 14:10:13 +00:00
AudioStream * makeVorbisStream ( File * file , uint32 size ) {
2005-12-03 21:29:13 +00:00
//debug(5, "" __FILE__ ":%i", __LINE__);
2003-12-23 19:14:57 +00:00
OggVorbis_File * ov_file = new OggVorbis_File ;
file_info * f = new file_info ;
2005-12-01 19:14:38 +00:00
# if defined(__SYMBIAN32__)
2005-12-02 18:36:21 +00:00
// Symbian can't share filehandles between different threads.
// So create a new file and seek that to the other filehandles position
f - > file = new File ;
f - > file - > open ( file - > name ( ) ) ;
f - > file - > seek ( file - > pos ( ) ) ;
2005-12-01 19:14:38 +00:00
# else
2005-12-02 18:36:21 +00:00
f - > file = file ;
2005-12-01 19:14:38 +00:00
# endif
2006-04-29 22:33:31 +00:00
f - > start = file - > pos ( ) ;
2003-12-23 19:14:57 +00:00
f - > len = size ;
f - > curr_pos = 0 ;
if ( ov_open_callbacks ( ( void * ) f , ov_file , NULL , 0 , g_File_wrap ) < 0 ) {
warning ( " Invalid file format " ) ;
delete ov_file ;
delete f ;
2006-04-29 22:33:31 +00:00
return 0 ;
} else {
2005-12-01 19:14:38 +00:00
# ifndef __SYMBIAN32__
2005-12-02 18:36:21 +00:00
file - > incRef ( ) ;
2005-12-01 19:14:38 +00:00
# endif
2005-12-02 18:36:21 +00:00
return new VorbisInputStream ( ov_file , 0 , true ) ;
}
2006-04-29 22:33:31 +00:00
}
} // End of namespace Audio
# endif // #ifdef USE_VORBIS