00001
00002
00003
00004
00005
00006
00007
00008
00009
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00030
00031
00032
00033
00034
00035
00036
00037 #include "stratagus.h"
00038
00039 #ifdef USE_VORBIS // {
00040
00041 #include <stdlib.h>
00042 #include <string.h>
00043
00044 #include <vorbis/codec.h>
00045 #include <vorbis/vorbisfile.h>
00046 #ifdef USE_THEORA
00047 #include <theora/theora.h>
00048 #endif
00049
00050 #include "SDL.h"
00051 #include "SDL_endian.h"
00052
00053 #include "iolib.h"
00054 #include "movie.h"
00055 #include "sound_server.h"
00056
00057
00058
00059
00060
00061 class CSampleVorbis : public CSample
00062 {
00063 public:
00064 ~CSampleVorbis();
00065 int Read(void *buf, int len);
00066
00067 OggData Data;
00068 };
00069
00070 class CSampleVorbisStream : public CSample
00071 {
00072 public:
00073 ~CSampleVorbisStream();
00074 int Read(void *buf, int len);
00075
00076 OggData Data;
00077 };
00078
00079
00080
00081
00082
00083 int OggGetNextPage(ogg_page *page, ogg_sync_state *sync, CFile *f)
00084 {
00085 char *buf;
00086 int bytes;
00087
00088 while (ogg_sync_pageout(sync, page) != 1) {
00089
00090 buf = ogg_sync_buffer(sync, 4096);
00091 bytes = f->read(buf, 4096);
00092 if (!bytes || ogg_sync_wrote(sync, bytes)) {
00093 return -1;
00094 }
00095 }
00096
00097 return 0;
00098 }
00099
00100 int VorbisProcessData(OggData *data, char *buffer)
00101 {
00102 int num_samples;
00103 float **pcm;
00104 float *chan;
00105 int i, j;
00106 int val;
00107 ogg_packet packet;
00108 int len;
00109
00110 len = 0;
00111 num_samples = 0;
00112
00113 while (!len) {
00114 if (ogg_stream_packetout(&data->astream, &packet) != 1) {
00115 if (OggGetNextPage(&data->page, &data->sync, data->File)) {
00116
00117 return -1;
00118 }
00119
00120 ogg_stream_pagein(&data->astream, &data->page);
00121 } else {
00122 if (vorbis_synthesis(&data->vblock, &packet) == 0) {
00123 vorbis_synthesis_blockin(&data->vdsp, &data->vblock);
00124 }
00125
00126 while ((num_samples = vorbis_synthesis_pcmout(&data->vdsp, &pcm)) > 0) {
00127 j = 0;
00128 for (i = 0; i < data->vinfo.channels; ++i) {
00129 chan = pcm[i];
00130 for (j = 0; j < num_samples; ++j) {
00131 val = static_cast<int>(chan[j] * 32767.f);
00132 if (val > 32767) {
00133 val = 32767;
00134 } else if (val < -32768) {
00135 val = -32768;
00136 }
00137
00138 *(Sint16 *)(buffer + len
00139 + (j * 2 * data->vinfo.channels) + i * 2) = (Sint16)val;
00140 }
00141 }
00142 len += j * 2 * data->vinfo.channels;
00143
00144
00145 vorbis_synthesis_read(&data->vdsp, num_samples);
00146 }
00147 }
00148 }
00149
00150 return len;
00151 }
00152
00153 int OggInit(CFile *f, OggData *data)
00154 {
00155 ogg_packet packet;
00156 int num_vorbis;
00157 #ifdef USE_THEORA
00158 int num_theora;
00159 #endif
00160 int stream_start;
00161 int ret;
00162
00163 unsigned magic;
00164 f->read(&magic, sizeof(magic));
00165 if (SDL_SwapLE32(magic) != 0x5367674F) {
00166 return -1;
00167 }
00168 f->seek(0, SEEK_SET);
00169
00170 ogg_sync_init(&data->sync);
00171
00172 vorbis_info_init(&data->vinfo);
00173 vorbis_comment_init(&data->vcomment);
00174
00175 #ifdef USE_THEORA
00176 theora_info_init(&data->tinfo);
00177 theora_comment_init(&data->tcomment);
00178 #endif
00179
00180 #ifdef USE_THEORA
00181 num_theora = 0;
00182 #endif
00183 num_vorbis = 0;
00184 stream_start = 0;
00185 while (!stream_start) {
00186 ogg_stream_state test;
00187
00188 if (OggGetNextPage(&data->page, &data->sync, f)) {
00189 return -1;
00190 }
00191
00192 if (!ogg_page_bos(&data->page)) {
00193 if (num_vorbis) {
00194 ogg_stream_pagein(&data->astream, &data->page);
00195 }
00196 #ifdef USE_THEORA
00197 if (num_theora) {
00198 ogg_stream_pagein(&data->vstream, &data->page);
00199 }
00200 #endif
00201 stream_start = 1;
00202 break;
00203 }
00204
00205 ogg_stream_init(&test, ogg_page_serialno(&data->page));
00206 ogg_stream_pagein(&test, &data->page);
00207
00208
00209 while (ogg_stream_packetout(&test, &packet) == 1) {
00210 #ifdef USE_THEORA
00211 if (theora_decode_header(&data->tinfo, &data->tcomment, &packet) >= 0) {
00212 memcpy(&data->vstream, &test, sizeof(test));
00213 ++num_theora;
00214 } else
00215 #endif
00216 if (!vorbis_synthesis_headerin(&data->vinfo, &data->vcomment, &packet)) {
00217 memcpy(&data->astream, &test, sizeof(test));
00218 ++num_vorbis;
00219 } else {
00220 ogg_stream_clear(&test);
00221 }
00222 }
00223 }
00224
00225 data->audio = num_vorbis;
00226 #ifdef USE_THEORA
00227 data->video = num_theora;
00228 #endif
00229
00230
00231 while ((num_vorbis && num_vorbis < 3)
00232 #ifdef USE_THEORA
00233 || (num_theora && num_theora < 3) ) {
00234
00235 while (num_theora && num_theora < 3 &&
00236 (ret = ogg_stream_packetout(&data->vstream, &packet))) {
00237 if (ret < 0) {
00238 return -1;
00239 }
00240 if (theora_decode_header(&data->tinfo, &data->tcomment, &packet)) {
00241 return -1;
00242 }
00243 ++num_theora;
00244 }
00245 #else
00246 ) {
00247 #endif
00248
00249
00250 while (num_vorbis && num_vorbis < 3 &&
00251 (ret = ogg_stream_packetout(&data->astream, &packet))) {
00252 if (ret < 0) {
00253 return -1;
00254 }
00255 if (vorbis_synthesis_headerin(&data->vinfo, &data->vcomment, &packet)) {
00256 return -1;
00257
00258 }
00259 ++num_vorbis;
00260 }
00261
00262 if (OggGetNextPage(&data->page, &data->sync, f)) {
00263 break;
00264 }
00265
00266 if (num_vorbis) {
00267 ogg_stream_pagein(&data->astream, &data->page);
00268 }
00269 #ifdef USE_THEORA
00270 if (num_theora) {
00271 ogg_stream_pagein(&data->vstream, &data->page);
00272 }
00273 #endif
00274 }
00275
00276 if (num_vorbis) {
00277 vorbis_synthesis_init(&data->vdsp, &data->vinfo);
00278 vorbis_block_init(&data->vdsp, &data->vblock);
00279 } else {
00280 vorbis_info_clear(&data->vinfo);
00281 vorbis_comment_clear(&data->vcomment);
00282 }
00283
00284 #ifdef USE_THEORA
00285 if (num_theora) {
00286 theora_decode_init(&data->tstate, &data->tinfo);
00287 data->tstate.internal_encode = NULL;
00288 } else {
00289 theora_info_clear(&data->tinfo);
00290 theora_comment_clear(&data->tcomment);
00291 }
00292
00293 return !(num_vorbis || num_theora);
00294 #else
00295 return !num_vorbis;
00296 #endif
00297 }
00298
00299 void OggFree(OggData *data)
00300 {
00301 if (data->audio) {
00302 ogg_stream_clear(&data->astream);
00303 vorbis_block_clear(&data->vblock);
00304 vorbis_dsp_clear(&data->vdsp);
00305 vorbis_comment_clear(&data->vcomment);
00306 vorbis_info_clear(&data->vinfo);
00307 }
00308 #ifdef USE_THEORA
00309 if (data->video) {
00310 ogg_stream_clear(&data->vstream);
00311 theora_comment_clear(&data->tcomment);
00312 theora_info_clear(&data->tinfo);
00313 theora_clear(&data->tstate);
00314 }
00315 #endif
00316 ogg_sync_clear(&data->sync);
00317 }
00318
00319
00320 int VorbisStreamRead(CSample *sample, OggData *data, void *buf, int len)
00321 {
00322 int bytes;
00323
00324 if (sample->Pos > SOUND_BUFFER_SIZE / 2) {
00325 memcpy(sample->Buffer, sample->Buffer + sample->Pos, sample->Len);
00326 sample->Pos = 0;
00327 }
00328
00329 while (sample->Len < SOUND_BUFFER_SIZE / 4) {
00330 bytes = VorbisProcessData(data, (char *)sample->Buffer + sample->Pos + sample->Len);
00331 if (bytes > 0) {
00332 sample->Len += bytes;
00333 } else {
00334 break;
00335 }
00336 }
00337
00338 if (sample->Len < len) {
00339 len = sample->Len;
00340 }
00341
00342 memcpy(buf, sample->Buffer + sample->Pos, len);
00343 sample->Pos += len;
00344 sample->Len -= len;
00345
00346 return len;
00347 }
00348
00349 int CSampleVorbisStream::Read(void *buf, int len)
00350 {
00351 return VorbisStreamRead(this, &this->Data, buf, len);
00352 }
00353
00354 CSampleVorbisStream::~CSampleVorbisStream()
00355 {
00356 if (this->Data.File) {
00357 this->Data.File->close();
00358 delete this->Data.File;
00359 }
00360 OggFree(&this->Data);
00361
00362 delete[] this->Buffer;
00363 }
00364
00365 int CSampleVorbis::Read(void *buf, int len)
00366 {
00367 if (len > this->Len) {
00368 len = this->Len;
00369 }
00370
00371 memcpy(buf, this->Buffer + this->Pos, len);
00372 this->Pos += len;
00373 this->Len -= len;
00374
00375 return len;
00376 }
00377
00378 CSampleVorbis::~CSampleVorbis()
00379 {
00380 delete[] this->Buffer;
00381 }
00382
00383
00392 CSample *LoadVorbis(const std::string &name, int flags)
00393 {
00394 CSample *sample;
00395 OggData *data;
00396 CFile *f;
00397 vorbis_info *info;
00398
00399 f = new CFile;
00400 if (f->open(name.c_str(), CL_OPEN_READ) == -1) {
00401 fprintf(stderr, "Can't open file `%s'\n", name.c_str());
00402 delete f;
00403 return NULL;
00404 }
00405
00406 if (flags & PlayAudioStream) {
00407 sample = new CSampleVorbisStream;
00408 data = &((CSampleVorbisStream *)sample)->Data;
00409 } else {
00410 sample = new CSampleVorbis;
00411 data = &((CSampleVorbis *)sample)->Data;
00412 }
00413 memset(data, 0, sizeof(*data));
00414
00415 if (OggInit(f, data) || !data->audio) {
00416 delete sample;
00417 f->close();
00418 delete f;
00419 return NULL;
00420 }
00421
00422 info = &data->vinfo;
00423
00424 sample->Channels = info->channels;
00425 sample->SampleSize = 16;
00426 sample->Frequency = info->rate;
00427
00428 sample->Len = 0;
00429 sample->Pos = 0;
00430 data->File = f;
00431
00432 if (flags & PlayAudioStream) {
00433 sample->Buffer = new unsigned char[SOUND_BUFFER_SIZE];
00434 } else {
00435 unsigned char *buf;
00436 int pos;
00437 int ret;
00438 int total;
00439 ogg_page pg;
00440 ogg_sync_state sync;
00441 int i;
00442
00443
00444 pos = f->tell();
00445 ogg_sync_init(&sync);
00446 for (i = 0; ; ++i) {
00447 f->seek(-1 * i * 4096, SEEK_END);
00448 buf = (unsigned char *)ogg_sync_buffer(&sync, 4096);
00449 f->read(buf, 8192);
00450 ogg_sync_wrote(&sync, 8192);
00451 if (ogg_sync_pageout(&sync, &pg) == 1 && ogg_page_eos(&pg)) {
00452 total = (int)(ogg_page_granulepos(&pg) * 2 * sample->Channels);
00453 break;
00454 }
00455 }
00456 f->seek(pos, SEEK_SET);
00457
00458 sample->Buffer = new unsigned char[total];
00459 pos = 0;
00460
00461 while ((ret = VorbisStreamRead(sample, &((CSampleVorbis *)sample)->Data,
00462 sample->Buffer + pos, 8192))) {
00463 pos += ret;
00464 }
00465
00466 sample->Len = total;
00467 sample->Pos = 0;
00468
00469 f->close();
00470 delete f;
00471 OggFree(data);
00472 }
00473
00474 return sample;
00475 }
00476
00477 #endif // USE_VORBIS
00478