ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

mp3track.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/mutex.h"
00024 #include "audio/mixer.h"
00025 #include "audio/audiostream.h"
00026 #include "audio/decoders/mp3.h"
00027 #include "engines/grim/debug.h"
00028 #include "engines/grim/resource.h"
00029 #include "engines/grim/textsplit.h"
00030 #include "engines/grim/emi/sound/mp3track.h"
00031 
00032 namespace Grim {
00033 
00038 class EMISubLoopingAudioStream : public Audio::AudioStream {
00039 public:
00040     EMISubLoopingAudioStream(Audio::SeekableAudioStream *stream, uint loops,
00041         const Audio::Timestamp start,
00042         const Audio::Timestamp loopStart,
00043         const Audio::Timestamp loopEnd,
00044         DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES)
00045         : _parent(stream, disposeAfterUse),
00046         _pos(convertTimeToStreamPos(start, getRate(), isStereo())),
00047         _loopStart(convertTimeToStreamPos(loopStart, getRate(), isStereo())),
00048         _loopEnd(convertTimeToStreamPos(loopEnd, getRate(), isStereo())),
00049         _done(false), _hasLooped(false) {
00050         assert(loopStart < loopEnd);
00051 
00052         if (!_parent->seek(_pos))
00053             _done = true;
00054     }
00055 
00056     int readBuffer(int16 *buffer, const int numSamples) {
00057         if (_done)
00058             return 0;
00059 
00060         int framesLeft = MIN(_loopEnd.frameDiff(_pos), numSamples);
00061         int framesRead = _parent->readBuffer(buffer, framesLeft);
00062         _pos = _pos.addFrames(framesRead);
00063 
00064         if (framesRead < framesLeft && _parent->endOfData()) {
00065             // TODO: Proper error indication.
00066             _done = true;
00067             return framesRead;
00068         }
00069         else if (_pos == _loopEnd) {
00070             if (!_parent->seek(_loopStart)) {
00071                 // TODO: Proper error indication.
00072                 _done = true;
00073                 return framesRead;
00074             }
00075 
00076             _pos = _loopStart;
00077             framesLeft = numSamples - framesLeft;
00078             _hasLooped = true;
00079             return framesRead + readBuffer(buffer + framesRead, framesLeft);
00080         }
00081         else {
00082             return framesRead;
00083         }
00084     }
00085 
00086     bool hasLooped() const { return _hasLooped; }
00087     bool endOfData() const { return _done; }
00088 
00089     bool isStereo() const { return _parent->isStereo(); }
00090     int getRate() const { return _parent->getRate(); }
00091     Audio::Timestamp getPos() const { return _pos; }
00092 
00093 private:
00094     Common::DisposablePtr<Audio::SeekableAudioStream> _parent;
00095 
00096     Audio::Timestamp _pos;
00097     Audio::Timestamp _loopStart, _loopEnd;
00098 
00099     bool _done;
00100     bool _hasLooped;
00101 };
00102 
00103 void MP3Track::parseRIFFHeader(Common::SeekableReadStream *data) {
00104     uint32 tag = data->readUint32BE();
00105     if (tag == MKTAG('R','I','F','F')) {
00106         _endFlag = false;
00107         data->seek(18, SEEK_CUR);
00108         _channels = data->readByte();
00109         data->readByte();
00110         _freq = data->readUint32LE();
00111         data->seek(6, SEEK_CUR);
00112         _bits = data->readByte();
00113         data->seek(5, SEEK_CUR);
00114         _regionLength = data->readUint32LE();
00115         _headerSize = 44;
00116     } else {
00117         error("Unknown file header");
00118     }
00119 }
00120 
00121 MP3Track::JMMCuePoints MP3Track::parseJMMFile(const Common::String &filename) {
00122     JMMCuePoints cuePoints;
00123     Common::SeekableReadStream *stream = g_resourceloader->openNewStreamFile(filename);
00124     if (stream) {
00125         TextSplitter ts(filename, stream);
00126         float startMs = 0.0f;
00127         float loopStartMs = 0.0f, loopEndMs = 0.0f;
00128 
00129         ts.scanString(".start %f", 1, &startMs);
00130         if (ts.checkString(".jump"))
00131             ts.scanString(".jump %f %f", 2, &loopEndMs, &loopStartMs);
00132 
00133         // Use microsecond precision for the timestamps.
00134         cuePoints._start = Audio::Timestamp(startMs / 1000, (int)(startMs * 1000) % 1000000, 1000000);
00135         cuePoints._loopStart = Audio::Timestamp(loopStartMs / 1000, (int)(loopStartMs * 1000) % 1000000, 1000000);
00136         cuePoints._loopEnd = Audio::Timestamp(loopEndMs / 1000, (int)(loopEndMs * 1000) % 1000000, 1000000);
00137     }
00138     delete stream;
00139     return cuePoints;
00140 }
00141 
00142 MP3Track::MP3Track(Audio::Mixer::SoundType soundType) {
00143     _soundType = soundType;
00144     _headerSize = 0;
00145     _regionLength = 0;
00146     _freq = 0;
00147     _bits = 0,
00148     _channels = 0;
00149     _endFlag = false;
00150     _looping = false;
00151 }
00152 
00153 MP3Track::~MP3Track() {
00154     stop();
00155     if (_handle) {
00156         g_system->getMixer()->stopHandle(*_handle);
00157         delete _handle;
00158     }
00159 }
00160 
00161 bool MP3Track::openSound(const Common::String &filename, const Common::String &soundName, const Audio::Timestamp *start) {
00162     Common::SeekableReadStream *file = g_resourceloader->openNewStreamFile(filename);
00163     if (!file) {
00164         Debug::debug(Debug::Sound, "Stream for %s not open", soundName.c_str());
00165         return false;
00166     }
00167     _soundName = soundName;
00168 #ifndef USE_MAD
00169     warning("Cannot open %s, MP3 support not enabled", soundName.c_str());
00170     return true;
00171 #else
00172     parseRIFFHeader(file);
00173     
00174     MP3Track::JMMCuePoints cuePoints;
00175     if (soundName.size() > 4) {
00176         cuePoints = parseJMMFile(Common::String(filename.c_str(), filename.size() - 4) + ".jmm");
00177     }
00178 
00179     if (start)
00180         cuePoints._start = *start;
00181 
00182     Audio::SeekableAudioStream *mp3Stream = Audio::makeMP3Stream(file, DisposeAfterUse::YES);
00183 
00184     if (cuePoints._loopEnd <= cuePoints._loopStart) {
00185         _stream = mp3Stream;
00186         mp3Stream->seek(cuePoints._start);
00187         _looping = false;
00188     } else {
00189         _stream = new EMISubLoopingAudioStream(mp3Stream, 0, cuePoints._start, cuePoints._loopStart, cuePoints._loopEnd);
00190         _looping = true;
00191     }
00192     _handle = new Audio::SoundHandle();
00193     return true;
00194 #endif
00195 }
00196 
00197 bool MP3Track::hasLooped() {
00198     if (!_stream || !_looping)
00199         return false;
00200     EMISubLoopingAudioStream *las = static_cast<EMISubLoopingAudioStream*>(_stream);
00201     return las->hasLooped();
00202 }
00203 
00204 bool MP3Track::isPlaying() {
00205     if (!_handle)
00206         return false;
00207 
00208     return g_system->getMixer()->isSoundHandleActive(*_handle);
00209 }
00210 
00211 Audio::Timestamp MP3Track::getPos() {
00212     if (!_stream)
00213         return Audio::Timestamp(0);
00214     if (_looping) {
00215         EMISubLoopingAudioStream *slas = static_cast<EMISubLoopingAudioStream*>(_stream);
00216         return slas->getPos();
00217     } else {
00218         return g_system->getMixer()->getSoundElapsedTime(*_handle);
00219     }
00220 }
00221 
00222 } // end of namespace Grim


Generated on Sat May 25 2019 05:00:50 for ResidualVM by doxygen 1.7.1
curved edge   curved edge