1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003 The GemRB Project
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 // Classes for sound files.
23 // Supported formats: PCM-RAW, PCM-WAV (both 8 and 16 bits),
24 // Ogg Vorbis and Interplay's ACM.
27 #include "Interface.h"
31 #ifdef HAS_VORBIS_SUPPORT
32 static size_t ovfd_read(void *ptr
, size_t size
, size_t nmemb
, void *datasource
)
34 DataStream
*vb
= (DataStream
*) datasource
;
35 int bytesToRead
= size
* nmemb
;
37 int remains
= vb
->Remains();
39 /* no more reading, we're at the end */
42 if(bytesToRead
> remains
) {
43 bytesToRead
= remains
;
45 vb
->Read(ptr
, bytesToRead
);
49 static int ovfd_seek(void *datasource
, ogg_int64_t offset
, int whence
) {
50 DataStream
*vb
= (DataStream
*) datasource
;
53 if(vb
->Seek(offset
, GEM_STREAM_START
)<0)
57 if(vb
->Seek(offset
, GEM_CURRENT_POS
)<0)
61 if(vb
->Seek(vb
->Size()+offset
, GEM_STREAM_START
)<0)
70 static int ovfd_close(void * /*datasource*/) {
74 static long ovfd_tell(void *datasource
) {
75 DataStream
*vb
= (DataStream
*) datasource
;
76 return (long) vb
->GetPos();
79 int COGGReader::init_reader()
83 ov_callbacks cbstruct
= {
84 ovfd_read
, ovfd_seek
, ovfd_close
, ovfd_tell
87 res
=ov_open_callbacks(stream
, &OggStream
, NULL
, 0, cbstruct
);
89 printMessage("Sound","Couldn't initialize vorbis!\n", LIGHT_RED
);
92 info
= ov_info(&OggStream
, -1);
93 channels
= info
->channels
;
94 samplerate
= info
->rate
;
95 samples_left
= ( samples
= ov_pcm_total(&OggStream
, -1) );
99 void fix_endian(ieWord
&dest
);
101 int COGGReader::read_samples(short* buffer
, int count
)
105 if(samples_left
<count
) {
109 int samples_need
=count
;
110 while(samples_need
) {
111 int rd
=ov_read(&OggStream
, (char *)buffer
, samples_need
<<1, 0, 2, 1, &whatisthis
);
123 samples_left
-=samples_got
;
124 if (stream
->IsEndianSwitch()) {
125 for (size_t i
= 0; i
< (size_t)samples_got
; i
++) {
126 fix_endian(((ieWord
*)buffer
-samples_got
)[i
]);
135 int CACMReader::init_reader()
140 stream
->Read( tmp
, sizeof( tmp
) );
141 if (!memcmp( tmp
, "WAVC", 4 )) {
142 stream
->Seek( 24, GEM_CURRENT_POS
);
144 stream
->Seek( -4, GEM_CURRENT_POS
);
146 //stream->Read( &hdr, sizeof( ACM_Header ) );
147 //maybe this'll work on a PPC
149 stream
->ReadDword( &hdr
.signature
);
150 stream
->ReadDword( &hdr
.samples
);
151 stream
->ReadWord( &hdr
.channels
);
152 stream
->ReadWord( &hdr
.rate
);
154 stream
->ReadWord( &tmpword
);
155 //subblocks = (int) (tmpword&0xfff);
156 //levels = (int) (tmpword>>12);
157 subblocks
= (int) (tmpword
>>4);
158 levels
= (int) (tmpword
&15);
160 if (hdr
.signature
!= IP_ACM_SIG
) {
163 samples_left
= ( samples
= hdr
.samples
);
164 channels
= hdr
.channels
;
165 samplerate
= hdr
.rate
;
166 //levels = hdr.levels;
167 //subblocks = hdr.subblocks;
169 block_size
= ( 1 << levels
) * subblocks
;
170 //using malloc for simple arrays (supposed to be faster)
171 block
= (int *) malloc(sizeof(int)*block_size
);
175 unpacker
= new CValueUnpacker( levels
, subblocks
, stream
);
176 if (!unpacker
|| !unpacker
->init_unpacker()) {
179 decoder
= new CSubbandDecoder( levels
);
180 if (!decoder
|| !decoder
->init_decoder()) {
185 int CACMReader::make_new_samples()
187 if (!unpacker
->get_one_block( block
)) {
191 decoder
->decode_data( block
, subblocks
);
193 samples_ready
= ( block_size
> samples_left
) ? samples_left
: block_size
;
194 samples_left
-= samples_ready
;
199 int CACMReader::read_samples(short* buffer
, int count
)
202 while (res
< count
) {
203 if (samples_ready
== 0) {
204 if (samples_left
== 0)
206 if (!make_new_samples())
209 *buffer
= ( short ) ( ( *values
) >> levels
);
218 CSoundReader
* CreateSoundReader(DataStream
* stream
, int type
, int samples
,
221 CSoundReader
* res
= NULL
;
224 #ifdef HAS_VORBIS_SUPPORT
226 res
= new COGGReader( stream
, autoFree
);
230 res
= new CACMReader( stream
, autoFree
);
233 res
= new CWavPCMReader( stream
, samples
, autoFree
);
236 if (autoFree
) delete stream
;
240 if (!res
->init_reader()) {
248 int CRawPCMReader::init_reader()
251 samples
= stream
->Size();
252 stream
->Seek( 0, GEM_STREAM_START
);
254 samples
>>= 1; // each sample has 16 bit
257 samples_left
= samples
;
261 inline void fix_endian(ieDword
&dest
)
264 tmp
=((unsigned char *) &dest
)[0];
265 ((unsigned char *) &dest
)[0]=((unsigned char *) &dest
)[3];
266 ((unsigned char *) &dest
)[3]=tmp
;
267 tmp
=((unsigned char *) &dest
)[1];
268 ((unsigned char *) &dest
)[1]=((unsigned char *) &dest
)[2];
269 ((unsigned char *) &dest
)[2]=tmp
;
273 inline void fix_endian(ieWord
&dest
)
276 tmp
=((unsigned char *) &dest
)[0];
277 ((unsigned char *) &dest
)[0]=((unsigned char *) &dest
)[1];
278 ((unsigned char *) &dest
)[1]=tmp
;
282 int CRawPCMReader::read_samples(short* buffer
, int count
)
284 if (count
> samples_left
) {
285 count
= samples_left
;
289 res
= stream
->Read( buffer
, count
* ( ( is16bit
? 2 : 1 ) ) );
292 char* alt_buff
= ( char* ) buffer
;
295 alt_buff
[( i
<< 1 ) + 1] = ( char ) ( alt_buff
[i
] - 0x80 );
296 alt_buff
[i
<< 1] = 0;
301 if (stream
->IsEndianSwitch()) {
302 for (size_t i
= 0; i
< (size_t)count
; i
++) {
303 fix_endian(((ieWord
*)buffer
)[i
]);
311 int CWavPCMReader::init_reader()
313 int res
= CRawPCMReader::init_reader(); if (!res
) {
318 RIFF_CHUNK r_hdr
, fmt_hdr
, data_hdr
;
320 memset( &fmt
, 0, sizeof( fmt
) );
322 //stream->Read( &r_hdr, sizeof( r_hdr ) );
324 stream
->Read(&r_hdr
.fourcc
, 4);
325 stream
->ReadDword(&r_hdr
.length
);
327 stream
->Read( &wave
, 4 );
328 if (r_hdr
.fourcc
!= *( unsigned int * ) RIFF_4cc
||
329 wave
!= *( unsigned int * ) WAVE_4cc
) {
333 //stream->Read( &fmt_hdr, sizeof( fmt_hdr ) );
335 stream
->Read(&fmt_hdr
.fourcc
,4);
336 stream
->ReadDword(&fmt_hdr
.length
);
337 if (fmt_hdr
.fourcc
!= *( unsigned int * ) fmt_4cc
||
338 fmt_hdr
.length
> sizeof( cWAVEFORMATEX
)) {
341 memset(&fmt
,0,sizeof(fmt
) );
342 stream
->Read( &fmt
, fmt_hdr
.length
);
343 //hmm, we should swap fmt bytes if we are on a mac
344 //but we don't know exactly how much of the structure we'll read
345 //so we have to swap the bytes after reading them
346 if (stream
->IsEndianSwitch()) {
347 fix_endian(fmt
.wFormatTag
);
348 fix_endian(fmt
.nChannels
);
349 fix_endian(fmt
.nSamplesPerSec
);
350 fix_endian(fmt
.wBitsPerSample
);
351 //we don't use these fields, so who cares
352 //fix_endian(fmt.nAvgBytesPerSec);
353 //fix_endian(fmt.nBlockAlign);
354 //fix_endian(fmt.cbSize);
356 if (fmt
.wFormatTag
!= 1) {
359 is16bit
= ( fmt
.wBitsPerSample
== 16 );
361 //stream->Read( &data_hdr, sizeof( data_hdr ) );
363 stream
->Read(&data_hdr
.fourcc
,4);
364 stream
->ReadDword(&data_hdr
.length
);
366 if (data_hdr
.fourcc
== *( unsigned int * ) fact_4cc
) {
367 stream
->Seek( data_hdr
.length
, GEM_CURRENT_POS
);
368 //stream->Read( &data_hdr, sizeof( data_hdr ) );
369 stream
->ReadDword(&data_hdr
.fourcc
);
370 stream
->ReadDword(&data_hdr
.length
);
372 if (data_hdr
.fourcc
!= *( unsigned int * ) data_4cc
) {
376 samples
= data_hdr
.length
;
380 samples_left
= samples
;
381 channels
= fmt
.nChannels
;
382 samplerate
= fmt
.nSamplesPerSec
;