ALL: synced with ScummVM

This commit is contained in:
Pawel Kolodziejski 2015-08-16 08:49:02 +02:00
parent fc33643a38
commit 11b457122b
34 changed files with 7815 additions and 863 deletions

View file

@ -927,18 +927,20 @@ static void createLookupTable() {
//
////////////////////////////////////////
class MidiDriver_ADLIB : public MidiDriver_Emulated {
class MidiDriver_ADLIB : public MidiDriver {
friend class AdLibPart;
friend class AdLibPercussionChannel;
public:
MidiDriver_ADLIB(Audio::Mixer *mixer);
MidiDriver_ADLIB();
int open();
void close();
void send(uint32 b);
void send(byte channel, uint32 b); // Supports higher than channel 15
uint32 property(int prop, uint32 param);
bool isOpen() const { return _isOpen; }
uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; }
void setPitchBendRange(byte channel, uint range);
void sysEx_customInstrument(byte channel, uint32 type, const byte *instr);
@ -946,10 +948,7 @@ public:
MidiChannel *allocateChannel();
MidiChannel *getPercussionChannel() { return &_percussion; } // Percussion partially supported
// AudioStream API
bool isStereo() const { return _opl->isStereo(); }
int getRate() const { return _mixer->getOutputRate(); }
virtual void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc);
private:
bool _scummSmallHeader; // FIXME: This flag controls a special mode for SCUMM V3 games
@ -963,6 +962,9 @@ private:
byte *_regCacheSecondary;
#endif
Common::TimerManager::TimerProc _adlibTimerProc;
void *_adlibTimerParam;
int _timerCounter;
uint16 _channelTable2[9];
@ -974,7 +976,8 @@ private:
AdLibPart _parts[32];
AdLibPercussionChannel _percussion;
void generateSamples(int16 *buf, int len);
bool _isOpen;
void onTimer();
void partKeyOn(AdLibPart *part, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan);
void partKeyOff(AdLibPart *part, byte note);
@ -1376,8 +1379,7 @@ void AdLibPercussionChannel::sysEx_customInstrument(uint32 type, const byte *ins
// MidiDriver method implementations
MidiDriver_ADLIB::MidiDriver_ADLIB(Audio::Mixer *mixer)
: MidiDriver_Emulated(mixer) {
MidiDriver_ADLIB::MidiDriver_ADLIB() {
uint i;
_scummSmallHeader = false;
@ -1403,13 +1405,16 @@ MidiDriver_ADLIB::MidiDriver_ADLIB(Audio::Mixer *mixer)
_timerIncrease = 0xD69;
_timerThreshold = 0x411B;
_opl = 0;
_adlibTimerProc = 0;
_adlibTimerParam = 0;
_isOpen = false;
}
int MidiDriver_ADLIB::open() {
if (_isOpen)
return MERR_ALREADY_OPEN;
MidiDriver_Emulated::open();
_isOpen = true;
int i;
AdLibVoice *voice;
@ -1434,7 +1439,7 @@ int MidiDriver_ADLIB::open() {
_opl3Mode = false;
}
#endif
_opl->init(getRate());
_opl->init();
_regCache = (byte *)calloc(256, 1);
@ -1452,8 +1457,7 @@ int MidiDriver_ADLIB::open() {
}
#endif
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
_opl->start(new Common::Functor0Mem<void, MidiDriver_ADLIB>(this, &MidiDriver_ADLIB::onTimer));
return 0;
}
@ -1462,7 +1466,8 @@ void MidiDriver_ADLIB::close() {
return;
_isOpen = false;
_mixer->stopHandle(_mixerSoundHandle);
// Stop the OPL timer
_opl->stop();
uint i;
for (i = 0; i < ARRAYSIZE(_voices); ++i) {
@ -1616,14 +1621,10 @@ void MidiDriver_ADLIB::adlibWriteSecondary(byte reg, byte value) {
}
#endif
void MidiDriver_ADLIB::generateSamples(int16 *data, int len) {
if (_opl->isStereo()) {
len *= 2;
}
_opl->readBuffer(data, len);
}
void MidiDriver_ADLIB::onTimer() {
if (_adlibTimerProc)
(*_adlibTimerProc)(_adlibTimerParam);
_timerCounter += _timerIncrease;
while (_timerCounter >= _timerThreshold) {
_timerCounter -= _timerThreshold;
@ -1655,6 +1656,11 @@ void MidiDriver_ADLIB::onTimer() {
}
}
void MidiDriver_ADLIB::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
_adlibTimerProc = timerProc;
_adlibTimerParam = timerParam;
}
void MidiDriver_ADLIB::mcOff(AdLibVoice *voice) {
AdLibVoice *tmp;
@ -2300,7 +2306,7 @@ MusicDevices AdLibEmuMusicPlugin::getDevices() const {
}
Common::Error AdLibEmuMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const {
*mididriver = new MidiDriver_ADLIB(g_system->getMixer());
*mididriver = new MidiDriver_ADLIB();
return Common::kNoError;
}

349
audio/alsa_opl.cpp Normal file
View file

@ -0,0 +1,349 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
/* OPL implementation for hardware OPL using ALSA Direct FM API.
*
* Caveats and limitations:
* - Pretends to be a softsynth (emitting silence).
* - Dual OPL2 mode requires OPL3 hardware.
* - Every register write leads to a series of register writes on the hardware,
* due to the lack of direct register access in the ALSA Direct FM API.
* - No timers
*/
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "common/scummsys.h"
#include "common/debug.h"
#include "common/str.h"
#include "audio/fmopl.h"
#include <sys/ioctl.h>
#include <alsa/asoundlib.h>
#include <sound/asound_fm.h>
namespace OPL {
namespace ALSA {
class OPL : public ::OPL::RealOPL {
private:
enum {
kOpl2Voices = 9,
kVoices = 18,
kOpl2Operators = 18,
kOperators = 36
};
Config::OplType _type;
int _iface;
snd_hwdep_t *_opl;
snd_dm_fm_voice _oper[kOperators];
snd_dm_fm_note _voice[kVoices];
snd_dm_fm_params _params;
int index[2];
static const int voiceToOper0[kVoices];
static const int regOffsetToOper[0x20];
void writeOplReg(int c, int r, int v);
void clear();
public:
OPL(Config::OplType type);
~OPL();
bool init();
void reset();
void write(int a, int v);
byte read(int a);
void writeReg(int r, int v);
};
const int OPL::voiceToOper0[OPL::kVoices] =
{ 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 };
const int OPL::regOffsetToOper[0x20] =
{ 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1,
12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
OPL::OPL(Config::OplType type) : _type(type), _opl(nullptr), _iface(0) {
}
OPL::~OPL() {
stop();
if (_opl) {
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr);
snd_hwdep_close(_opl);
}
}
void OPL::clear() {
index[0] = index[1] = 0;
memset(_oper, 0, sizeof(_oper));
memset(_voice, 0, sizeof(_voice));
memset(&_params, 0, sizeof(_params));
for (int i = 0; i < kOperators; ++i) {
_oper[i].op = (i / 3) % 2;
_oper[i].voice = (i / 6) * 3 + (i % 3);
}
for (int i = 0; i < kVoices; ++i)
_voice[i].voice = i;
// For OPL3 hardware we need to set up the panning in OPL2 modes
if (_iface == SND_HWDEP_IFACE_OPL3) {
if (_type == Config::kDualOpl2) {
for (int i = 0; i < kOpl2Operators; ++i)
_oper[i].left = 1; // FIXME below
for (int i = kOpl2Operators; i < kOperators; ++i)
_oper[i].right = 1;
} else if (_type == Config::kOpl2) {
for (int i = 0; i < kOpl2Operators; ++i) {
_oper[i].left = 1;
_oper[i].right = 1;
}
}
}
}
bool OPL::init() {
clear();
int card = -1;
snd_ctl_t *ctl;
snd_hwdep_info_t *info;
snd_hwdep_info_alloca(&info);
int iface = SND_HWDEP_IFACE_OPL3;
if (_type == Config::kOpl2)
iface = SND_HWDEP_IFACE_OPL2;
// Look for OPL hwdep interface
while (!snd_card_next(&card) && card >= 0) {
int dev = -1;
Common::String name = Common::String::format("hw:%d", card);
if (snd_ctl_open(&ctl, name.c_str(), 0) < 0)
continue;
while (!snd_ctl_hwdep_next_device(ctl, &dev) && dev >= 0) {
name = Common::String::format("hw:%d,%d", card, dev);
if (snd_hwdep_open(&_opl, name.c_str(), SND_HWDEP_OPEN_WRITE) < 0)
continue;
if (!snd_hwdep_info(_opl, info)) {
int found = snd_hwdep_info_get_iface(info);
// OPL3 can be used for (Dual) OPL2 mode
if (found == iface || found == SND_HWDEP_IFACE_OPL3) {
snd_ctl_close(ctl);
_iface = found;
reset();
return true;
}
}
// Wrong interface, try next device
snd_hwdep_close(_opl);
_opl = nullptr;
}
snd_ctl_close(ctl);
}
return false;
}
void OPL::reset() {
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr);
if (_iface == SND_HWDEP_IFACE_OPL3)
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_MODE, (void *)SNDRV_DM_FM_MODE_OPL3);
clear();
// Sync up with the hardware
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kVoices : kOpl2Voices); ++i)
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[i]);
for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kOperators : kOpl2Operators); ++i)
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[i]);
}
void OPL::write(int port, int val) {
val &= 0xff;
int chip = (port & 2) >> 1;
if (port & 1) {
switch(_type) {
case Config::kOpl2:
writeOplReg(0, index[0], val);
break;
case Config::kDualOpl2:
if (port & 8) {
writeOplReg(0, index[0], val);
writeOplReg(1, index[1], val);
} else
writeOplReg(chip, index[chip], val);
break;
case Config::kOpl3:
writeOplReg(chip, index[chip], val);
}
} else {
switch(_type) {
case Config::kOpl2:
index[0] = val;
break;
case Config::kDualOpl2:
if (port & 8) {
index[0] = val;
index[1] = val;
} else
index[chip] = val;
break;
case Config::kOpl3:
index[chip] = val;
}
}
}
byte OPL::read(int port) {
return 0;
}
void OPL::writeReg(int r, int v) {
switch (_type) {
case Config::kOpl2:
writeOplReg(0, r, v);
break;
case Config::kDualOpl2:
writeOplReg(0, r, v);
writeOplReg(1, r, v);
break;
case Config::kOpl3:
writeOplReg(r >= 0x100, r & 0xff, v);
}
}
void OPL::writeOplReg(int c, int r, int v) {
if (r == 0x04 && c == 1 && _type == Config::kOpl3) {
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_CONNECTION, reinterpret_cast<void *>(v & 0x3f));
} else if (r == 0x08 && c == 0) {
_params.kbd_split = (v >> 6) & 0x1;
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
} else if (r == 0xbd && c == 0) {
_params.hihat = v & 0x1;
_params.cymbal = (v >> 1) & 0x1;
_params.tomtom = (v >> 2) & 0x1;
_params.snare = (v >> 3) & 0x1;
_params.bass = (v >> 4) & 0x1;
_params.rhythm = (v >> 5) & 0x1;
_params.vib_depth = (v >> 6) & 0x1;
_params.am_depth = (v >> 7) & 0x1;
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
} else if (r < 0xa0 || r >= 0xe0) {
// Operator
int idx = regOffsetToOper[r & 0x1f];
if (idx == -1)
return;
if (c == 1)
idx += kOpl2Operators;
switch (r & 0xf0) {
case 0x20:
case 0x30:
_oper[idx].harmonic = v & 0xf;
_oper[idx].kbd_scale = (v >> 4) & 0x1;
_oper[idx].do_sustain = (v >> 5) & 0x1;
_oper[idx].vibrato = (v >> 6) & 0x1;
_oper[idx].am = (v >> 7) & 0x1;
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
break;
case 0x40:
case 0x50:
_oper[idx].volume = ~v & 0x3f;
_oper[idx].scale_level = (v >> 6) & 0x3;
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
break;
case 0x60:
case 0x70:
_oper[idx].decay = v & 0xf;
_oper[idx].attack = (v >> 4) & 0xf;
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
break;
case 0x80:
case 0x90:
_oper[idx].release = v & 0xf;
_oper[idx].sustain = (v >> 4) & 0xf;
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
break;
case 0xe0:
case 0xf0:
_oper[idx].waveform = v & (_type == Config::kOpl3 ? 0x7 : 0x3);
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
}
} else {
// Voice
int idx = r & 0xf;
if (idx >= kOpl2Voices)
return;
if (c == 1)
idx += kOpl2Voices;
int opIdx = voiceToOper0[idx];
switch (r & 0xf0) {
case 0xa0:
_voice[idx].fnum = (_voice[idx].fnum & 0x300) | (v & 0xff);
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]);
break;
case 0xb0:
_voice[idx].fnum = ((v << 8) & 0x300) | (_voice[idx].fnum & 0xff);
_voice[idx].octave = (v >> 2) & 0x7;
_voice[idx].key_on = (v >> 5) & 0x1;
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]);
break;
case 0xc0:
_oper[opIdx].connection = _oper[opIdx + 3].connection = v & 0x1;
_oper[opIdx].feedback = _oper[opIdx + 3].feedback = (v >> 1) & 0x7;
if (_type == Config::kOpl3) {
_oper[opIdx].left = _oper[opIdx + 3].left = (v >> 4) & 0x1;
_oper[opIdx].right = _oper[opIdx + 3].right = (v >> 5) & 0x1;
}
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[opIdx]);
}
}
}
OPL *create(Config::OplType type) {
return new OPL(type);
}
} // End of namespace ALSA
} // End of namespace OPL

343
audio/decoders/3do.cpp Normal file
View file

@ -0,0 +1,343 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/textconsole.h"
#include "common/stream.h"
#include "common/util.h"
#include "audio/decoders/3do.h"
#include "audio/decoders/raw.h"
#include "audio/decoders/adpcm_intern.h"
namespace Audio {
// Reuses ADPCM table
#define audio_3DO_ADP4_stepSizeTable Ima_ADPCMStream::_imaTable
#define audio_3DO_ADP4_stepSizeIndex ADPCMStream::_stepAdjustTable
RewindableAudioStream *make3DO_ADP4AudioStream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, uint32 *audioLengthMSecsPtr, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace) {
if (stereo) {
warning("make3DO_ADP4Stream(): stereo currently not supported");
return 0;
}
if (audioLengthMSecsPtr) {
// Caller requires the milliseconds of audio
uint32 audioLengthMSecs = stream->size() * 2 * 1000 / sampleRate; // 1 byte == 2 16-bit sample
if (stereo) {
audioLengthMSecs /= 2;
}
*audioLengthMSecsPtr = audioLengthMSecs;
}
return new Audio3DO_ADP4_Stream(stream, sampleRate, stereo, disposeAfterUse, persistentSpace);
}
Audio3DO_ADP4_Stream::Audio3DO_ADP4_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace)
: _sampleRate(sampleRate), _stereo(stereo),
_stream(stream, disposeAfterUse) {
_callerDecoderData = persistentSpace;
memset(&_initialDecoderData, 0, sizeof(_initialDecoderData));
_initialRead = true;
reset();
}
void Audio3DO_ADP4_Stream::reset() {
memcpy(&_curDecoderData, &_initialDecoderData, sizeof(_curDecoderData));
_streamBytesLeft = _stream->size();
_stream->seek(0);
}
bool Audio3DO_ADP4_Stream::rewind() {
reset();
return true;
}
int16 Audio3DO_ADP4_Stream::decodeSample(byte compressedNibble) {
int16 currentStep = audio_3DO_ADP4_stepSizeTable[_curDecoderData.stepIndex];
int32 decodedSample = _curDecoderData.lastSample;
int16 delta = currentStep >> 3;
if (compressedNibble & 1)
delta += currentStep >> 2;
if (compressedNibble & 2)
delta += currentStep >> 1;
if (compressedNibble & 4)
delta += currentStep;
if (compressedNibble & 8) {
decodedSample -= delta;
} else {
decodedSample += delta;
}
_curDecoderData.lastSample = CLIP<int32>(decodedSample, -32768, 32767);
_curDecoderData.stepIndex += audio_3DO_ADP4_stepSizeIndex[compressedNibble & 0x07];
_curDecoderData.stepIndex = CLIP<int16>(_curDecoderData.stepIndex, 0, ARRAYSIZE(audio_3DO_ADP4_stepSizeTable) - 1);
return _curDecoderData.lastSample;
}
// Writes the requested amount (or less) of samples into buffer and returns the amount of samples, that got written
int Audio3DO_ADP4_Stream::readBuffer(int16 *buffer, const int numSamples) {
int8 byteCache[AUDIO_3DO_CACHE_SIZE];
int8 *byteCachePtr = NULL;
int byteCacheSize = 0;
int requestedBytesLeft = 0;
int decodedSamplesCount = 0;
int8 compressedByte = 0;
if (endOfData())
return 0; // no more bytes left
if (_callerDecoderData) {
// copy caller decoder data over
memcpy(&_curDecoderData, _callerDecoderData, sizeof(_curDecoderData));
if (_initialRead) {
_initialRead = false;
memcpy(&_initialDecoderData, &_curDecoderData, sizeof(_initialDecoderData));
}
}
requestedBytesLeft = numSamples >> 1; // 1 byte for 2 16-bit sample
if (requestedBytesLeft > _streamBytesLeft)
requestedBytesLeft = _streamBytesLeft; // not enough bytes left
// in case caller requests an uneven amount of samples, we will return an even amount
// buffering, so that direct decoding of files and such runs way faster
while (requestedBytesLeft) {
if (requestedBytesLeft > AUDIO_3DO_CACHE_SIZE) {
byteCacheSize = AUDIO_3DO_CACHE_SIZE;
} else {
byteCacheSize = requestedBytesLeft;
}
requestedBytesLeft -= byteCacheSize;
_streamBytesLeft -= byteCacheSize;
// Fill our byte cache
_stream->read(byteCache, byteCacheSize);
byteCachePtr = byteCache;
// Mono
while (byteCacheSize) {
compressedByte = *byteCachePtr++;
byteCacheSize--;
buffer[decodedSamplesCount] = decodeSample(compressedByte >> 4);
decodedSamplesCount++;
buffer[decodedSamplesCount] = decodeSample(compressedByte & 0x0f);
decodedSamplesCount++;
}
}
if (_callerDecoderData) {
// copy caller decoder data back
memcpy(_callerDecoderData, &_curDecoderData, sizeof(_curDecoderData));
}
return decodedSamplesCount;
}
// ============================================================================
static int16 audio_3DO_SDX2_SquareTable[256] = {
-32768,-32258,-31752,-31250,-30752,-30258,-29768,-29282,-28800,-28322,
-27848,-27378,-26912,-26450,-25992,-25538,-25088,-24642,-24200,-23762,
-23328,-22898,-22472,-22050,-21632,-21218,-20808,-20402,-20000,-19602,
-19208,-18818,-18432,-18050,-17672,-17298,-16928,-16562,-16200,-15842,
-15488,-15138,-14792,-14450,-14112,-13778,-13448,-13122,-12800,-12482,
-12168,-11858,-11552,-11250,-10952,-10658,-10368,-10082, -9800, -9522,
-9248, -8978, -8712, -8450, -8192, -7938, -7688, -7442, -7200, -6962,
-6728, -6498, -6272, -6050, -5832, -5618, -5408, -5202, -5000, -4802,
-4608, -4418, -4232, -4050, -3872, -3698, -3528, -3362, -3200, -3042,
-2888, -2738, -2592, -2450, -2312, -2178, -2048, -1922, -1800, -1682,
-1568, -1458, -1352, -1250, -1152, -1058, -968, -882, -800, -722,
-648, -578, -512, -450, -392, -338, -288, -242, -200, -162,
-128, -98, -72, -50, -32, -18, -8, -2, 0, 2,
8, 18, 32, 50, 72, 98, 128, 162, 200, 242,
288, 338, 392, 450, 512, 578, 648, 722, 800, 882,
968, 1058, 1152, 1250, 1352, 1458, 1568, 1682, 1800, 1922,
2048, 2178, 2312, 2450, 2592, 2738, 2888, 3042, 3200, 3362,
3528, 3698, 3872, 4050, 4232, 4418, 4608, 4802, 5000, 5202,
5408, 5618, 5832, 6050, 6272, 6498, 6728, 6962, 7200, 7442,
7688, 7938, 8192, 8450, 8712, 8978, 9248, 9522, 9800, 10082,
10368, 10658, 10952, 11250, 11552, 11858, 12168, 12482, 12800, 13122,
13448, 13778, 14112, 14450, 14792, 15138, 15488, 15842, 16200, 16562,
16928, 17298, 17672, 18050, 18432, 18818, 19208, 19602, 20000, 20402,
20808, 21218, 21632, 22050, 22472, 22898, 23328, 23762, 24200, 24642,
25088, 25538, 25992, 26450, 26912, 27378, 27848, 28322, 28800, 29282,
29768, 30258, 30752, 31250, 31752, 32258
};
Audio3DO_SDX2_Stream::Audio3DO_SDX2_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpace)
: _sampleRate(sampleRate), _stereo(stereo),
_stream(stream, disposeAfterUse) {
_callerDecoderData = persistentSpace;
memset(&_initialDecoderData, 0, sizeof(_initialDecoderData));
_initialRead = true;
reset();
}
void Audio3DO_SDX2_Stream::reset() {
memcpy(&_curDecoderData, &_initialDecoderData, sizeof(_curDecoderData));
_streamBytesLeft = _stream->size();
_stream->seek(0);
}
bool Audio3DO_SDX2_Stream::rewind() {
reset();
return true;
}
// Writes the requested amount (or less) of samples into buffer and returns the amount of samples, that got written
int Audio3DO_SDX2_Stream::readBuffer(int16 *buffer, const int numSamples) {
int8 byteCache[AUDIO_3DO_CACHE_SIZE];
int8 *byteCachePtr = NULL;
int byteCacheSize = 0;
int requestedBytesLeft = numSamples; // 1 byte per 16-bit sample
int decodedSamplesCount = 0;
int8 compressedByte = 0;
uint8 squareTableOffset = 0;
int16 decodedSample = 0;
if (endOfData())
return 0; // no more bytes left
if (_stereo) {
// We expect numSamples to be even in case of Stereo audio
assert((numSamples & 1) == 0);
}
if (_callerDecoderData) {
// copy caller decoder data over
memcpy(&_curDecoderData, _callerDecoderData, sizeof(_curDecoderData));
if (_initialRead) {
_initialRead = false;
memcpy(&_initialDecoderData, &_curDecoderData, sizeof(_initialDecoderData));
}
}
requestedBytesLeft = numSamples;
if (requestedBytesLeft > _streamBytesLeft)
requestedBytesLeft = _streamBytesLeft; // not enough bytes left
// buffering, so that direct decoding of files and such runs way faster
while (requestedBytesLeft) {
if (requestedBytesLeft > AUDIO_3DO_CACHE_SIZE) {
byteCacheSize = AUDIO_3DO_CACHE_SIZE;
} else {
byteCacheSize = requestedBytesLeft;
}
requestedBytesLeft -= byteCacheSize;
_streamBytesLeft -= byteCacheSize;
// Fill our byte cache
_stream->read(byteCache, byteCacheSize);
byteCachePtr = byteCache;
if (!_stereo) {
// Mono
while (byteCacheSize) {
compressedByte = *byteCachePtr++;
byteCacheSize--;
squareTableOffset = compressedByte + 128;
if (!(compressedByte & 1))
_curDecoderData.lastSample1 = 0;
decodedSample = _curDecoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset];
_curDecoderData.lastSample1 = decodedSample;
buffer[decodedSamplesCount] = decodedSample;
decodedSamplesCount++;
}
} else {
// Stereo
while (byteCacheSize) {
compressedByte = *byteCachePtr++;
byteCacheSize--;
squareTableOffset = compressedByte + 128;
if (!(decodedSamplesCount & 1)) {
// First channel
if (!(compressedByte & 1))
_curDecoderData.lastSample1 = 0;
decodedSample = _curDecoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset];
_curDecoderData.lastSample1 = decodedSample;
} else {
// Second channel
if (!(compressedByte & 1))
_curDecoderData.lastSample2 = 0;
decodedSample = _curDecoderData.lastSample2 + audio_3DO_SDX2_SquareTable[squareTableOffset];
_curDecoderData.lastSample2 = decodedSample;
}
buffer[decodedSamplesCount] = decodedSample;
decodedSamplesCount++;
}
}
}
if (_callerDecoderData) {
// copy caller decoder data back
memcpy(_callerDecoderData, &_curDecoderData, sizeof(_curDecoderData));
}
return decodedSamplesCount;
}
RewindableAudioStream *make3DO_SDX2AudioStream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, uint32 *audioLengthMSecsPtr, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpace) {
if (stereo) {
if (stream->size() & 1) {
warning("make3DO_SDX2Stream(): stereo data is uneven size");
return 0;
}
}
if (audioLengthMSecsPtr) {
// Caller requires the milliseconds of audio
uint32 audioLengthMSecs = stream->size() * 1000 / sampleRate; // 1 byte == 1 16-bit sample
if (stereo) {
audioLengthMSecs /= 2;
}
*audioLengthMSecsPtr = audioLengthMSecs;
}
return new Audio3DO_SDX2_Stream(stream, sampleRate, stereo, disposeAfterUse, persistentSpace);
}
} // End of namespace Audio

158
audio/decoders/3do.h Normal file
View file

@ -0,0 +1,158 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
/**
* @file
* Sound decoder used in engines:
* - sherlock (3DO version of Serrated Scalpel)
*/
#ifndef AUDIO_3DO_SDX2_H
#define AUDIO_3DO_SDX2_H
#include "common/scummsys.h"
#include "common/types.h"
#include "common/substream.h"
#include "audio/audiostream.h"
#include "audio/decoders/raw.h"
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class SeekableAudioStream;
// amount of bytes to be used within the decoder classes as buffers
#define AUDIO_3DO_CACHE_SIZE 1024
// persistent spaces
struct audio_3DO_ADP4_PersistentSpace {
int16 lastSample;
int16 stepIndex;
};
struct audio_3DO_SDX2_PersistentSpace {
int16 lastSample1;
int16 lastSample2;
};
class Audio3DO_ADP4_Stream : public RewindableAudioStream {
public:
Audio3DO_ADP4_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace);
protected:
const uint16 _sampleRate;
const bool _stereo;
Common::DisposablePtr<Common::SeekableReadStream> _stream;
int32 _streamBytesLeft;
void reset();
bool rewind();
bool endOfData() const { return (_stream->pos() >= _stream->size()); }
bool isStereo() const { return _stereo; }
int getRate() const { return _sampleRate; }
int readBuffer(int16 *buffer, const int numSamples);
bool _initialRead;
audio_3DO_ADP4_PersistentSpace *_callerDecoderData;
audio_3DO_ADP4_PersistentSpace _initialDecoderData;
audio_3DO_ADP4_PersistentSpace _curDecoderData;
private:
int16 decodeSample(byte compressedNibble);
};
class Audio3DO_SDX2_Stream : public RewindableAudioStream {
public:
Audio3DO_SDX2_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpacePtr);
protected:
const uint16 _sampleRate;
const bool _stereo;
Common::DisposablePtr<Common::SeekableReadStream> _stream;
int32 _streamBytesLeft;
void reset();
bool rewind();
bool endOfData() const { return (_stream->pos() >= _stream->size()); }
bool isStereo() const { return _stereo; }
int getRate() const { return _sampleRate; }
int readBuffer(int16 *buffer, const int numSamples);
bool _initialRead;
audio_3DO_SDX2_PersistentSpace *_callerDecoderData;
audio_3DO_SDX2_PersistentSpace _initialDecoderData;
audio_3DO_SDX2_PersistentSpace _curDecoderData;
};
/**
* Try to decode 3DO ADP4 data from the given seekable stream and create a SeekableAudioStream
* from that data.
*
* @param stream the SeekableReadStream from which to read the 3DO SDX2 data
* @sampleRate sample rate
* @stereo if it's stereo or mono
* @audioLengthMSecsPtr pointer to a uint32 variable, that is supposed to get the length of the audio in milliseconds
* @disposeAfterUse disposeAfterUse whether to delete the stream after use
* @persistentSpacePtr pointer to the persistent space structure
* @return a new SeekableAudioStream, or NULL, if an error occurred
*/
RewindableAudioStream *make3DO_ADP4AudioStream(
Common::SeekableReadStream *stream,
uint16 sampleRate,
bool stereo,
uint32 *audioLengthMSecsPtr = NULL, // returns the audio length in milliseconds
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES,
audio_3DO_ADP4_PersistentSpace *persistentSpacePtr = NULL
);
/**
* Try to decode 3DO SDX2 data from the given seekable stream and create a SeekableAudioStream
* from that data.
*
* @param stream the SeekableReadStream from which to read the 3DO SDX2 data
* @sampleRate sample rate
* @stereo if it's stereo or mono
* @audioLengthMSecsPtr pointer to a uint32 variable, that is supposed to get the length of the audio in milliseconds
* @disposeAfterUse disposeAfterUse whether to delete the stream after use
* @persistentSpacePtr pointer to the persistent space structure
* @return a new SeekableAudioStream, or NULL, if an error occurred
*/
RewindableAudioStream *make3DO_SDX2AudioStream(
Common::SeekableReadStream *stream,
uint16 sampleRate,
bool stereo,
uint32 *audioLengthMSecsPtr = NULL, // returns the audio length in milliseconds
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES,
audio_3DO_SDX2_PersistentSpace *persistentSpacePtr = NULL
);
} // End of namespace Audio
#endif

View file

@ -24,16 +24,19 @@
* The code in this file is based on information found at
* http://www.borg.com/~jglatt/tech/aiff.htm
*
* We currently only implement uncompressed AIFF. If we ever need AIFF-C, SoX
* (http://sox.sourceforge.net) may be a good place to start from.
* Also partially based on libav's aiffdec.c
*/
#include "common/debug.h"
#include "common/endian.h"
#include "common/stream.h"
#include "common/substream.h"
#include "common/textconsole.h"
#include "audio/audiostream.h"
#include "audio/decoders/aiff.h"
#include "audio/decoders/raw.h"
#include "audio/decoders/3do.h"
namespace Audio {
@ -62,23 +65,34 @@ uint32 readExtended(Common::SeekableReadStream &stream) {
return mantissa;
}
bool loadAIFFFromStream(Common::SeekableReadStream &stream, int &size, int &rate, byte &flags) {
byte buf[4];
// AIFF versions
static const uint32 kVersionAIFF = MKTAG('A', 'I', 'F', 'F');
static const uint32 kVersionAIFC = MKTAG('A', 'I', 'F', 'C');
stream.read(buf, 4);
if (memcmp(buf, "FORM", 4) != 0) {
warning("loadAIFFFromStream: No 'FORM' header");
return false;
// Codecs
static const uint32 kCodecPCM = MKTAG('N', 'O', 'N', 'E'); // very original
RewindableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
if (stream->readUint32BE() != MKTAG('F', 'O', 'R', 'M')) {
warning("makeAIFFStream: No 'FORM' header");
if (disposeAfterUse == DisposeAfterUse::YES)
delete stream;
return 0;
}
stream.readUint32BE();
stream->readUint32BE(); // file size
// This could be AIFC, but we don't handle that case.
uint32 version = stream->readUint32BE();
stream.read(buf, 4);
if (memcmp(buf, "AIFF", 4) != 0) {
warning("loadAIFFFromStream: No 'AIFF' header");
return false;
if (version != kVersionAIFF && version != kVersionAIFC) {
warning("makeAIFFStream: No 'AIFF' or 'AIFC' header");
if (disposeAfterUse == DisposeAfterUse::YES)
delete stream;
return 0;
}
// From here on, we only care about the COMM and SSND chunks, which are
@ -87,95 +101,131 @@ bool loadAIFFFromStream(Common::SeekableReadStream &stream, int &size, int &rate
bool foundCOMM = false;
bool foundSSND = false;
uint16 numChannels = 0, bitsPerSample = 0;
uint32 numSampleFrames = 0, offset = 0, blockSize = 0, soundOffset = 0;
uint16 channels = 0, bitsPerSample = 0;
uint32 blockAlign = 0, rate = 0;
uint32 codec = kCodecPCM; // AIFF default
Common::SeekableReadStream *dataStream = 0;
while (!(foundCOMM && foundSSND) && !stream.err() && !stream.eos()) {
uint32 length, pos;
while (!(foundCOMM && foundSSND) && !stream->err() && !stream->eos()) {
uint32 tag = stream->readUint32BE();
uint32 length = stream->readUint32BE();
uint32 pos = stream->pos();
stream.read(buf, 4);
length = stream.readUint32BE();
pos = stream.pos();
if (stream->eos() || stream->err())
break;
if (memcmp(buf, "COMM", 4) == 0) {
switch (tag) {
case MKTAG('C', 'O', 'M', 'M'):
foundCOMM = true;
numChannels = stream.readUint16BE();
numSampleFrames = stream.readUint32BE();
bitsPerSample = stream.readUint16BE();
rate = readExtended(stream);
size = numSampleFrames * numChannels * (bitsPerSample / 8);
} else if (memcmp(buf, "SSND", 4) == 0) {
channels = stream->readUint16BE();
/* frameCount = */ stream->readUint32BE();
bitsPerSample = stream->readUint16BE();
rate = readExtended(*stream);
if (version == kVersionAIFC)
codec = stream->readUint32BE();
break;
case MKTAG('S', 'S', 'N', 'D'):
foundSSND = true;
offset = stream.readUint32BE();
blockSize = stream.readUint32BE();
soundOffset = stream.pos();
/* uint32 offset = */ stream->readUint32BE();
blockAlign = stream->readUint32BE();
dataStream = new Common::SeekableSubReadStream(stream, stream->pos(), stream->pos() + length - 8, disposeAfterUse);
break;
case MKTAG('F', 'V', 'E', 'R'):
switch (stream->readUint32BE()) {
case 0:
version = kVersionAIFF;
break;
case 0xA2805140:
version = kVersionAIFC;
break;
default:
warning("Unknown AIFF version chunk version");
break;
}
break;
case MKTAG('w', 'a', 'v', 'e'):
warning("Found unhandled AIFF-C extra data chunk");
if (!dataStream && disposeAfterUse == DisposeAfterUse::YES)
delete stream;
delete dataStream;
return 0;
default:
debug(1, "Skipping AIFF '%s' chunk", tag2str(tag));
break;
}
stream.seek(pos + length);
stream->seek(pos + length + (length & 1)); // ensure we're also word-aligned
}
if (!foundCOMM) {
warning("loadAIFFFromStream: Cound not find 'COMM' chunk");
return false;
warning("makeAIFFStream: Cound not find 'COMM' chunk");
if (!dataStream && disposeAfterUse == DisposeAfterUse::YES)
delete stream;
delete dataStream;
return 0;
}
if (!foundSSND) {
warning("loadAIFFFromStream: Cound not find 'SSND' chunk");
return false;
warning("makeAIFFStream: Cound not find 'SSND' chunk");
if (disposeAfterUse == DisposeAfterUse::YES)
delete stream;
return 0;
}
// We only implement a subset of the AIFF standard.
if (numChannels < 1 || numChannels > 2) {
warning("loadAIFFFromStream: Only 1 or 2 channels are supported, not %d", numChannels);
return false;
}
if (bitsPerSample != 8 && bitsPerSample != 16) {
warning("loadAIFFFromStream: Only 8 or 16 bits per sample are supported, not %d", bitsPerSample);
return false;
}
if (offset != 0 || blockSize != 0) {
warning("loadAIFFFromStream: Block-aligned data is not supported");
return false;
}
// Samples are always signed, and big endian.
flags = 0;
if (bitsPerSample == 16)
flags |= Audio::FLAG_16BITS;
if (numChannels == 2)
flags |= Audio::FLAG_STEREO;
stream.seek(soundOffset);
// Stream now points at the sample data
return true;
}
SeekableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse) {
int size, rate;
byte *data, flags;
if (!loadAIFFFromStream(*stream, size, rate, flags)) {
if (disposeAfterUse == DisposeAfterUse::YES)
delete stream;
if (channels < 1 || channels > 2) {
warning("makeAIFFStream: Only 1 or 2 channels are supported, not %d", channels);
delete dataStream;
return 0;
}
data = (byte *)malloc(size);
assert(data);
stream->read(data, size);
// Seek to the start of dataStream, required for at least FileStream
dataStream->seek(0);
if (disposeAfterUse == DisposeAfterUse::YES)
delete stream;
switch (codec) {
case kCodecPCM:
case MKTAG('t', 'w', 'o', 's'):
case MKTAG('s', 'o', 'w', 't'): {
// PCM samples are always signed.
byte rawFlags = 0;
if (bitsPerSample == 16)
rawFlags |= Audio::FLAG_16BITS;
if (channels == 2)
rawFlags |= Audio::FLAG_STEREO;
if (codec == MKTAG('s', 'o', 'w', 't'))
rawFlags |= Audio::FLAG_LITTLE_ENDIAN;
// Since we allocated our own buffer for the data, we must specify DisposeAfterUse::YES.
return makeRawStream(data, size, rate, flags);
return makeRawStream(dataStream, rate, rawFlags);
}
case MKTAG('i', 'm', 'a', '4'):
// TODO: Use QT IMA ADPCM
warning("Unhandled AIFF-C QT IMA ADPCM compression");
break;
case MKTAG('Q', 'D', 'M', '2'):
// TODO: Need to figure out how to integrate this
// (But hopefully never needed)
warning("Unhandled AIFF-C QDM2 compression");
break;
case MKTAG('A', 'D', 'P', '4'):
// ADP4 on 3DO
return make3DO_ADP4AudioStream(dataStream, rate, channels == 2);
case MKTAG('S', 'D', 'X', '2'):
// SDX2 on 3DO
return make3DO_SDX2AudioStream(dataStream, rate, channels == 2);
default:
warning("Unhandled AIFF-C compression tag '%s'", tag2str(codec));
}
delete dataStream;
return 0;
}
} // End of namespace Audio

View file

@ -23,6 +23,7 @@
/**
* @file
* Sound decoder used in engines:
* - bbvs
* - pegasus
* - saga
* - sci
@ -41,28 +42,17 @@ class SeekableReadStream;
namespace Audio {
class SeekableAudioStream;
/**
* Try to load an AIFF from the given seekable stream. Returns true if
* successful. In that case, the stream's seek position will be set to the
* start of the audio data, and size, rate and flags contain information
* necessary for playback. Currently this function only supports uncompressed
* raw PCM.
*/
extern bool loadAIFFFromStream(Common::SeekableReadStream &stream, int &size, int &rate, byte &flags);
class RewindableAudioStream;
/**
* Try to load an AIFF from the given seekable stream and create an AudioStream
* from that data.
*
* This function uses loadAIFFFromStream() internally.
*
* @param stream the SeekableReadStream from which to read the AIFF data
* @param disposeAfterUse whether to delete the stream after use
* @return a new SeekableAudioStream, or NULL, if an error occurred
*/
SeekableAudioStream *makeAIFFStream(
RewindableAudioStream *makeAIFFStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse);

View file

@ -23,15 +23,25 @@
/**
* @file
* Sound decoder used in engines:
* - access
* - agos
* - cge
* - cge2
* - fullpipe
* - gob
* - hopkins
* - mohawk
* - prince
* - saga
* - sci
* - scumm
* - sherlock
* - sword1
* - sword2
* - tony
* - tucker
* - wintermute
* - zvision
*/
#ifndef AUDIO_WAVE_H

View file

@ -22,21 +22,33 @@
#include "audio/fmopl.h"
#include "audio/mixer.h"
#include "audio/softsynth/opl/dosbox.h"
#include "audio/softsynth/opl/mame.h"
#include "common/config-manager.h"
#include "common/system.h"
#include "common/textconsole.h"
#include "common/timer.h"
#include "common/translation.h"
namespace OPL {
// Factory functions
#ifdef USE_ALSA
namespace ALSA {
OPL *create(Config::OplType type);
} // End of namespace ALSA
#endif // USE_ALSA
// Config implementation
enum OplEmulator {
kAuto = 0,
kMame = 1,
kDOSBox = 2
kDOSBox = 2,
kALSA = 3
};
OPL::OPL() {
@ -50,6 +62,9 @@ const Config::EmulatorDescription Config::_drivers[] = {
{ "mame", _s("MAME OPL emulator"), kMame, kFlagOpl2 },
#ifndef DISABLE_DOSBOX_OPL
{ "db", _s("DOSBox OPL emulator"), kDOSBox, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 },
#endif
#ifdef USE_ALSA
{ "alsa", _s("ALSA Direct FM"), kALSA, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 },
#endif
{ 0, 0, 0, 0 }
};
@ -63,6 +78,15 @@ Config::DriverId Config::parse(const Common::String &name) {
return -1;
}
const Config::EmulatorDescription *Config::findDriver(DriverId id) {
for (int i = 0; _drivers[i].name; ++i) {
if (_drivers[i].id == id)
return &_drivers[i];
}
return 0;
}
Config::DriverId Config::detect(OplType type) {
uint32 flags = 0;
switch (type) {
@ -80,12 +104,21 @@ Config::DriverId Config::detect(OplType type) {
}
DriverId drv = parse(ConfMan.get("opl_driver"));
if (drv == kAuto) {
// Since the "auto" can be explicitly set for a game, and this
// driver shows up in the GUI as "<default>", check if there is
// a global setting for it before resorting to auto-detection.
drv = parse(ConfMan.get("opl_driver", Common::ConfigManager::kApplicationDomain));
}
// When a valid driver is selected, check whether it supports
// the requested OPL chip.
if (drv != -1 && drv != kAuto) {
const EmulatorDescription *driverDesc = findDriver(drv);
// If the chip is supported, just use the driver.
if ((flags & _drivers[drv].flags)) {
if (!driverDesc) {
warning("The selected OPL driver %d could not be found", drv);
} else if ((flags & driverDesc->flags)) {
return drv;
} else {
// Else we will output a warning and just
@ -145,6 +178,11 @@ OPL *Config::create(DriverId driver, OplType type) {
return new DOSBox::OPL(type);
#endif
#ifdef USE_ALSA
case kALSA:
return ALSA::create(type);
#endif
default:
warning("Unsupported OPL emulator %d", driver);
// TODO: Maybe we should add some dummy emulator too, which just outputs
@ -153,43 +191,143 @@ OPL *Config::create(DriverId driver, OplType type) {
}
}
void OPL::start(TimerCallback *callback, int timerFrequency) {
_callback.reset(callback);
startCallbacks(timerFrequency);
}
void OPL::stop() {
stopCallbacks();
_callback.reset();
}
bool OPL::_hasInstance = false;
RealOPL::RealOPL() : _baseFreq(0), _remainingTicks(0) {
}
RealOPL::~RealOPL() {
// Stop callbacks, just in case. If it's still playing at this
// point, there's probably a bigger issue, though. The subclass
// needs to call stop() or the pointer can still use be used in
// the mixer thread at the same time.
stop();
}
void RealOPL::setCallbackFrequency(int timerFrequency) {
stopCallbacks();
startCallbacks(timerFrequency);
}
void RealOPL::startCallbacks(int timerFrequency) {
_baseFreq = timerFrequency;
assert(_baseFreq > 0);
// We can't request more a timer faster than 100Hz. We'll handle this by calling
// the proc multiple times in onTimer() later on.
if (timerFrequency > kMaxFreq)
timerFrequency = kMaxFreq;
_remainingTicks = 0;
g_system->getTimerManager()->installTimerProc(timerProc, 1000000 / timerFrequency, this, "RealOPL");
}
void RealOPL::stopCallbacks() {
g_system->getTimerManager()->removeTimerProc(timerProc);
_baseFreq = 0;
_remainingTicks = 0;
}
void RealOPL::timerProc(void *refCon) {
static_cast<RealOPL *>(refCon)->onTimer();
}
void RealOPL::onTimer() {
uint callbacks = 1;
if (_baseFreq > kMaxFreq) {
// We run faster than our max, so run the callback multiple
// times to approximate the actual timer callback frequency.
uint totalTicks = _baseFreq + _remainingTicks;
callbacks = totalTicks / kMaxFreq;
_remainingTicks = totalTicks % kMaxFreq;
}
// Call the callback multiple times. The if is on the inside of the
// loop in case the callback removes itself.
for (uint i = 0; i < callbacks; i++)
if (_callback && _callback->isValid())
(*_callback)();
}
EmulatedOPL::EmulatedOPL() :
_nextTick(0),
_samplesPerTick(0),
_baseFreq(0),
_handle(new Audio::SoundHandle()) {
}
EmulatedOPL::~EmulatedOPL() {
// Stop callbacks, just in case. If it's still playing at this
// point, there's probably a bigger issue, though. The subclass
// needs to call stop() or the pointer can still use be used in
// the mixer thread at the same time.
stop();
delete _handle;
}
int EmulatedOPL::readBuffer(int16 *buffer, const int numSamples) {
const int stereoFactor = isStereo() ? 2 : 1;
int len = numSamples / stereoFactor;
int step;
do {
step = len;
if (step > (_nextTick >> FIXP_SHIFT))
step = (_nextTick >> FIXP_SHIFT);
generateSamples(buffer, step * stereoFactor);
_nextTick -= step << FIXP_SHIFT;
if (!(_nextTick >> FIXP_SHIFT)) {
if (_callback && _callback->isValid())
(*_callback)();
_nextTick += _samplesPerTick;
}
buffer += step * stereoFactor;
len -= step;
} while (len);
return numSamples;
}
int EmulatedOPL::getRate() const {
return g_system->getMixer()->getOutputRate();
}
void EmulatedOPL::startCallbacks(int timerFrequency) {
setCallbackFrequency(timerFrequency);
g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, _handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
}
void EmulatedOPL::stopCallbacks() {
g_system->getMixer()->stopHandle(*_handle);
}
void EmulatedOPL::setCallbackFrequency(int timerFrequency) {
_baseFreq = timerFrequency;
assert(_baseFreq != 0);
int d = getRate() / _baseFreq;
int r = getRate() % _baseFreq;
// This is equivalent to (getRate() << FIXP_SHIFT) / BASE_FREQ
// but less prone to arithmetic overflow.
_samplesPerTick = (d << FIXP_SHIFT) + (r << FIXP_SHIFT) / _baseFreq;
}
} // End of namespace OPL
void OPLDestroy(FM_OPL *OPL) {
delete OPL;
}
void OPLResetChip(FM_OPL *OPL) {
OPL->reset();
}
void OPLWrite(FM_OPL *OPL, int a, int v) {
OPL->write(a, v);
}
unsigned char OPLRead(FM_OPL *OPL, int a) {
return OPL->read(a);
}
void OPLWriteReg(FM_OPL *OPL, int r, int v) {
OPL->writeReg(r, v);
}
void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length) {
OPL->readBuffer(buffer, length);
}
FM_OPL *makeAdLibOPL(int rate) {
FM_OPL *opl = OPL::Config::create();
if (opl) {
if (!opl->init(rate)) {
delete opl;
opl = 0;
}
}
return opl;
}

View file

@ -23,8 +23,16 @@
#ifndef AUDIO_FMOPL_H
#define AUDIO_FMOPL_H
#include "audio/audiostream.h"
#include "common/func.h"
#include "common/ptr.h"
#include "common/scummsys.h"
namespace Audio {
class SoundHandle;
}
namespace Common {
class String;
}
@ -70,6 +78,12 @@ public:
*/
static DriverId parse(const Common::String &name);
/**
* @return The driver description for the given id or 0 in case it is not
* available.
*/
static const EmulatorDescription *findDriver(DriverId id);
/**
* Detects a driver for the specific type.
*
@ -92,6 +106,14 @@ private:
static const EmulatorDescription _drivers[];
};
/**
* The type of the OPL timer callback functor.
*/
typedef Common::Functor0<void> TimerCallback;
/**
* A representation of a Yamaha OPL chip.
*/
class OPL {
private:
static bool _hasInstance;
@ -102,10 +124,9 @@ public:
/**
* Initializes the OPL emulator.
*
* @param rate output sample rate
* @return true on success, false on failure
*/
virtual bool init(int rate) = 0;
virtual bool init() = 0;
/**
* Reinitializes the OPL emulator
@ -139,6 +160,101 @@ public:
*/
virtual void writeReg(int r, int v) = 0;
/**
* Start the OPL with callbacks.
*/
void start(TimerCallback *callback, int timerFrequency = kDefaultCallbackFrequency);
/**
* Stop the OPL
*/
void stop();
/**
* Change the callback frequency. This must only be called from a
* timer proc.
*/
virtual void setCallbackFrequency(int timerFrequency) = 0;
enum {
/**
* The default callback frequency that start() uses
*/
kDefaultCallbackFrequency = 250
};
protected:
/**
* Start the callbacks.
*/
virtual void startCallbacks(int timerFrequency) = 0;
/**
* Stop the callbacks.
*/
virtual void stopCallbacks() = 0;
/**
* The functor for callbacks.
*/
Common::ScopedPtr<TimerCallback> _callback;
};
/**
* An OPL that represents a real OPL, as opposed to an emulated one.
*
* This will use an actual timer instead of using one calculated from
* the number of samples in an AudioStream::readBuffer call.
*/
class RealOPL : public OPL {
public:
RealOPL();
virtual ~RealOPL();
// OPL API
void setCallbackFrequency(int timerFrequency);
protected:
// OPL API
void startCallbacks(int timerFrequency);
void stopCallbacks();
private:
static void timerProc(void *refCon);
void onTimer();
uint _baseFreq;
uint _remainingTicks;
enum {
kMaxFreq = 100
};
};
/**
* An OPL that represents an emulated OPL.
*
* This will send callbacks based on the number of samples
* decoded in readBuffer().
*/
class EmulatedOPL : public OPL, protected Audio::AudioStream {
public:
EmulatedOPL();
virtual ~EmulatedOPL();
// OPL API
void setCallbackFrequency(int timerFrequency);
// AudioStream API
int readBuffer(int16 *buffer, const int numSamples);
int getRate() const;
bool endOfData() const { return false; }
protected:
// OPL API
void startCallbacks(int timerFrequency);
void stopCallbacks();
/**
* Read up to 'length' samples.
*
@ -149,33 +265,21 @@ public:
* So if you request 4 samples from a stereo OPL, you will get
* a total of two left channel and two right channel samples.
*/
virtual void readBuffer(int16 *buffer, int length) = 0;
virtual void generateSamples(int16 *buffer, int numSamples) = 0;
/**
* Returns whether the setup OPL mode is stereo or not
*/
virtual bool isStereo() const = 0;
private:
int _baseFreq;
enum {
FIXP_SHIFT = 16
};
int _nextTick;
int _samplesPerTick;
Audio::SoundHandle *_handle;
};
} // End of namespace OPL
// Legacy API
// !You should not write any new code using the legacy API!
typedef OPL::OPL FM_OPL;
void OPLDestroy(FM_OPL *OPL);
void OPLResetChip(FM_OPL *OPL);
void OPLWrite(FM_OPL *OPL, int a, int v);
unsigned char OPLRead(FM_OPL *OPL, int a);
void OPLWriteReg(FM_OPL *OPL, int r, int v);
void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length);
/**
* Legacy factory to create an AdLib (OPL2) chip.
*
* !You should not write any new code using the legacy API!
*/
FM_OPL *makeAdLibOPL(int rate);
#endif

View file

@ -370,6 +370,7 @@ public:
public:
typedef void (*XMidiCallbackProc)(byte eventData, void *refCon);
typedef void (*XMidiNewTimbreListProc)(MidiDriver_BASE *driver, const byte *timbreListPtr, uint32 timbreListSize);
MidiParser();
virtual ~MidiParser() { allNotesOff(); }
@ -395,7 +396,7 @@ public:
static void defaultXMidiCallback(byte eventData, void *refCon);
static MidiParser *createParser_SMF();
static MidiParser *createParser_XMIDI(XMidiCallbackProc proc = defaultXMidiCallback, void *refCon = 0);
static MidiParser *createParser_XMIDI(XMidiCallbackProc proc = defaultXMidiCallback, void *refCon = 0, XMidiNewTimbreListProc newTimbreListProc = NULL, MidiDriver_BASE *newTimbreListDriver = NULL);
static MidiParser *createParser_QT();
static void timerCallback(void *data) { ((MidiParser *) data)->onTimer(); }
};

83
audio/miles.h Normal file
View file

@ -0,0 +1,83 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef AUDIO_MILES_MIDIDRIVER_H
#define AUDIO_MILES_MIDIDRIVER_H
#include "audio/mididrv.h"
#include "common/error.h"
#include "common/stream.h"
namespace Audio {
#define MILES_MIDI_CHANNEL_COUNT 16
// Miles Audio supported controllers for control change messages
#define MILES_CONTROLLER_SELECT_PATCH_BANK 114
#define MILES_CONTROLLER_PROTECT_VOICE 112
#define MILES_CONTROLLER_PROTECT_TIMBRE 113
#define MILES_CONTROLLER_MODULATION 1
#define MILES_CONTROLLER_VOLUME 7
#define MILES_CONTROLLER_EXPRESSION 11
#define MILES_CONTROLLER_PANNING 10
#define MILES_CONTROLLER_SUSTAIN 64
#define MILES_CONTROLLER_PITCH_RANGE 6
#define MILES_CONTROLLER_RESET_ALL 121
#define MILES_CONTROLLER_ALL_NOTES_OFF 123
#define MILES_CONTROLLER_PATCH_REVERB 59
#define MILES_CONTROLLER_PATCH_BENDER 60
#define MILES_CONTROLLER_REVERB_MODE 61
#define MILES_CONTROLLER_REVERB_TIME 62
#define MILES_CONTROLLER_REVERB_LEVEL 63
#define MILES_CONTROLLER_RHYTHM_KEY_TIMBRE 58
// 3 SysEx controllers, each range 5
// 32-36 for 1st queue
// 37-41 for 2nd queue
// 42-46 for 3rd queue
#define MILES_CONTROLLER_SYSEX_RANGE_BEGIN 32
#define MILES_CONTROLLER_SYSEX_RANGE_END 46
#define MILES_CONTROLLER_SYSEX_QUEUE_COUNT 3
#define MILES_CONTROLLER_SYSEX_QUEUE_SIZE 32
#define MILES_CONTROLLER_SYSEX_COMMAND_ADDRESS1 0
#define MILES_CONTROLLER_SYSEX_COMMAND_ADDRESS2 1
#define MILES_CONTROLLER_SYSEX_COMMAND_ADDRESS3 2
#define MILES_CONTROLLER_SYSEX_COMMAND_DATA 3
#define MILES_CONTROLLER_SYSEX_COMMAND_SEND 4
#define MILES_CONTROLLER_XMIDI_RANGE_BEGIN 110
#define MILES_CONTROLLER_XMIDI_RANGE_END 120
// Miles Audio actually used 0x4000, because they didn't shift the 2 bytes properly
#define MILES_PITCHBENDER_DEFAULT 0x2000
extern MidiDriver *MidiDriver_Miles_AdLib_create(const Common::String &filenameAdLib, const Common::String &filenameOPL3, Common::SeekableReadStream *streamAdLib = nullptr, Common::SeekableReadStream *streamOPL3 = nullptr);
extern MidiDriver *MidiDriver_Miles_MT32_create(const Common::String &instrumentDataFilename);
extern void MidiDriver_Miles_MT32_processXMIDITimbreChunk(MidiDriver_BASE *driver, const byte *timbreListPtr, uint32 timbreListSize);
} // End of namespace Audio
#endif // AUDIO_MILES_MIDIDRIVER_H

1274
audio/miles_adlib.cpp Normal file

File diff suppressed because it is too large Load diff

912
audio/miles_mt32.cpp Normal file
View file

@ -0,0 +1,912 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "audio/miles.h"
#include "common/config-manager.h"
#include "common/file.h"
#include "common/mutex.h"
#include "common/system.h"
#include "common/textconsole.h"
namespace Audio {
// Miles Audio MT32 driver
//
#define MILES_MT32_PATCHES_COUNT 128
#define MILES_MT32_CUSTOMTIMBRE_COUNT 64
#define MILES_MT32_TIMBREBANK_STANDARD_ROLAND 0
#define MILES_MT32_TIMBREBANK_MELODIC_MODULE 127
#define MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE 14
#define MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE 58
#define MILES_MT32_PATCHDATA_PARTIALPARAMETERS_COUNT 4
#define MILES_MT32_PATCHDATA_TOTAL_SIZE (MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE + (MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE * MILES_MT32_PATCHDATA_PARTIALPARAMETERS_COUNT))
#define MILES_MT32_SYSEX_TERMINATOR 0xFF
struct MilesMT32InstrumentEntry {
byte bankId;
byte patchId;
byte commonParameter[MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE + 1];
byte partialParameters[MILES_MT32_PATCHDATA_PARTIALPARAMETERS_COUNT][MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE + 1];
};
const byte milesMT32SysExResetParameters[] = {
0x01, MILES_MT32_SYSEX_TERMINATOR
};
const byte milesMT32SysExChansSetup[] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, MILES_MT32_SYSEX_TERMINATOR
};
const byte milesMT32SysExPartialReserveTable[] = {
0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x04, MILES_MT32_SYSEX_TERMINATOR
};
const byte milesMT32SysExInitReverb[] = {
0x00, 0x03, 0x02, MILES_MT32_SYSEX_TERMINATOR // Reverb mode 0, reverb time 3, reverb level 2
};
class MidiDriver_Miles_MT32 : public MidiDriver {
public:
MidiDriver_Miles_MT32(MilesMT32InstrumentEntry *instrumentTablePtr, uint16 instrumentTableCount);
virtual ~MidiDriver_Miles_MT32();
// MidiDriver
int open();
void close();
bool isOpen() const { return _isOpen; }
void send(uint32 b);
MidiChannel *allocateChannel() {
if (_driver)
return _driver->allocateChannel();
return NULL;
}
MidiChannel *getPercussionChannel() {
if (_driver)
return _driver->getPercussionChannel();
return NULL;
}
void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) {
if (_driver)
_driver->setTimerCallback(timer_param, timer_proc);
}
uint32 getBaseTempo() {
if (_driver) {
return _driver->getBaseTempo();
}
return 1000000 / _baseFreq;
}
protected:
Common::Mutex _mutex;
MidiDriver *_driver;
bool _MT32;
bool _nativeMT32;
bool _isOpen;
int _baseFreq;
public:
void processXMIDITimbreChunk(const byte *timbreListPtr, uint32 timbreListSize);
private:
void resetMT32();
void MT32SysEx(const uint32 targetAddress, const byte *dataPtr);
uint32 calculateSysExTargetAddress(uint32 baseAddress, uint32 index);
void writeRhythmSetup(byte note, byte customTimbreId);
void writePatchTimbre(byte patchId, byte timbreGroup, byte timbreId);
void writePatchByte(byte patchId, byte index, byte patchValue);
void writeToSystemArea(byte index, byte value);
void controlChange(byte midiChannel, byte controllerNumber, byte controllerValue);
void programChange(byte midiChannel, byte patchId);
const MilesMT32InstrumentEntry *searchCustomInstrument(byte patchBank, byte patchId);
int16 searchCustomTimbre(byte patchBank, byte patchId);
void setupPatch(byte patchBank, byte patchId);
int16 installCustomTimbre(byte patchBank, byte patchId);
private:
struct MidiChannelEntry {
byte currentPatchBank;
byte currentPatchId;
bool usingCustomTimbre;
byte currentCustomTimbreId;
MidiChannelEntry() : currentPatchBank(0),
currentPatchId(0),
usingCustomTimbre(false),
currentCustomTimbreId(0) { }
};
struct MidiCustomTimbreEntry {
bool used;
bool protectionEnabled;
byte currentPatchBank;
byte currentPatchId;
uint32 lastUsedNoteCounter;
MidiCustomTimbreEntry() : used(false),
protectionEnabled(false),
currentPatchBank(0),
currentPatchId(0),
lastUsedNoteCounter(0) {}
};
struct MilesMT32SysExQueueEntry {
uint32 targetAddress;
byte dataPos;
byte data[MILES_CONTROLLER_SYSEX_QUEUE_SIZE + 1]; // 1 extra byte for terminator
MilesMT32SysExQueueEntry() : targetAddress(0),
dataPos(0) {
memset(data, 0, sizeof(data));
}
};
// stores information about all MIDI channels
MidiChannelEntry _midiChannels[MILES_MIDI_CHANNEL_COUNT];
// stores information about all custom timbres
MidiCustomTimbreEntry _customTimbres[MILES_MT32_CUSTOMTIMBRE_COUNT];
byte _patchesBank[MILES_MT32_PATCHES_COUNT];
// holds all instruments
MilesMT32InstrumentEntry *_instrumentTablePtr;
uint16 _instrumentTableCount;
uint32 _noteCounter; // used to figure out, which timbres are outdated
// SysEx Queues
MilesMT32SysExQueueEntry _sysExQueues[MILES_CONTROLLER_SYSEX_QUEUE_COUNT];
};
MidiDriver_Miles_MT32::MidiDriver_Miles_MT32(MilesMT32InstrumentEntry *instrumentTablePtr, uint16 instrumentTableCount) {
_instrumentTablePtr = instrumentTablePtr;
_instrumentTableCount = instrumentTableCount;
_driver = NULL;
_isOpen = false;
_MT32 = false;
_nativeMT32 = false;
_baseFreq = 250;
_noteCounter = 0;
memset(_patchesBank, 0, sizeof(_patchesBank));
}
MidiDriver_Miles_MT32::~MidiDriver_Miles_MT32() {
Common::StackLock lock(_mutex);
if (_driver) {
_driver->setTimerCallback(0, 0);
_driver->close();
delete _driver;
}
_driver = NULL;
}
int MidiDriver_Miles_MT32::open() {
assert(!_driver);
// Setup midi driver
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_PREFER_MT32);
MusicType musicType = MidiDriver::getMusicType(dev);
switch (musicType) {
case MT_MT32:
_nativeMT32 = true;
break;
case MT_GM:
if (ConfMan.getBool("native_mt32")) {
_nativeMT32 = true;
}
break;
default:
break;
}
if (!_nativeMT32) {
error("MILES-MT32: non-mt32 currently not supported!");
}
_driver = MidiDriver::createMidi(dev);
if (!_driver)
return 255;
if (_nativeMT32)
_driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
int ret = _driver->open();
if (ret)
return ret;
if (_nativeMT32) {
_driver->sendMT32Reset();
resetMT32();
}
return 0;
}
void MidiDriver_Miles_MT32::close() {
if (_driver) {
_driver->close();
}
}
void MidiDriver_Miles_MT32::resetMT32() {
// reset all internal parameters / patches
MT32SysEx(0x7F0000, milesMT32SysExResetParameters);
// init part/channel assignments
MT32SysEx(0x10000D, milesMT32SysExChansSetup);
// partial reserve table
MT32SysEx(0x100004, milesMT32SysExPartialReserveTable);
// init reverb
MT32SysEx(0x100001, milesMT32SysExInitReverb);
}
void MidiDriver_Miles_MT32::MT32SysEx(const uint32 targetAddress, const byte *dataPtr) {
byte sysExMessage[270];
uint16 sysExPos = 0;
byte sysExByte = 0;
uint16 sysExChecksum = 0;
memset(&sysExMessage, 0, sizeof(sysExMessage));
sysExMessage[0] = 0x41; // Roland
sysExMessage[1] = 0x10;
sysExMessage[2] = 0x16; // Model MT32
sysExMessage[3] = 0x12; // Command DT1
sysExChecksum = 0;
sysExMessage[4] = (targetAddress >> 16) & 0xFF;
sysExMessage[5] = (targetAddress >> 8) & 0xFF;
sysExMessage[6] = targetAddress & 0xFF;
for (byte targetAddressByte = 4; targetAddressByte < 7; targetAddressByte++) {
assert(sysExMessage[targetAddressByte] < 0x80); // security check
sysExChecksum -= sysExMessage[targetAddressByte];
}
sysExPos = 7;
while (1) {
sysExByte = *dataPtr++;
if (sysExByte == MILES_MT32_SYSEX_TERMINATOR)
break; // Message done
assert(sysExPos < sizeof(sysExMessage));
assert(sysExByte < 0x80); // security check
sysExMessage[sysExPos++] = sysExByte;
sysExChecksum -= sysExByte;
}
// Calculate checksum
assert(sysExPos < sizeof(sysExMessage));
sysExMessage[sysExPos++] = sysExChecksum & 0x7f;
// Send SysEx
_driver->sysEx(sysExMessage, sysExPos);
// Wait the time it takes to send the SysEx data
uint32 delay = (sysExPos + 2) * 1000 / 3125;
// Plus an additional delay for the MT-32 rev00
if (_nativeMT32)
delay += 40;
g_system->delayMillis(delay);
}
// MIDI messages can be found at http://www.midi.org/techspecs/midimessages.php
void MidiDriver_Miles_MT32::send(uint32 b) {
byte command = b & 0xf0;
byte midiChannel = b & 0xf;
byte op1 = (b >> 8) & 0xff;
byte op2 = (b >> 16) & 0xff;
switch (command) {
case 0x80: // note off
case 0x90: // note on
case 0xa0: // Polyphonic key pressure (aftertouch)
case 0xd0: // Channel pressure (aftertouch)
case 0xe0: // pitch bend change
_noteCounter++;
if (_midiChannels[midiChannel].usingCustomTimbre) {
// Remember that this timbre got used now
_customTimbres[_midiChannels[midiChannel].currentCustomTimbreId].lastUsedNoteCounter = _noteCounter;
}
_driver->send(b);
break;
case 0xb0: // Control change
controlChange(midiChannel, op1, op2);
break;
case 0xc0: // Program Change
programChange(midiChannel, op1);
break;
case 0xf0: // SysEx
warning("MILES-MT32: SysEx: %x", b);
break;
default:
warning("MILES-MT32: Unknown event %02x", command);
}
}
void MidiDriver_Miles_MT32::controlChange(byte midiChannel, byte controllerNumber, byte controllerValue) {
byte channelPatchId = 0;
byte channelCustomTimbreId = 0;
switch (controllerNumber) {
case MILES_CONTROLLER_SELECT_PATCH_BANK:
_midiChannels[midiChannel].currentPatchBank = controllerValue;
return;
case MILES_CONTROLLER_PATCH_REVERB:
channelPatchId = _midiChannels[midiChannel].currentPatchId;
writePatchByte(channelPatchId, 6, controllerValue);
_driver->send(0xC0 | midiChannel | (channelPatchId << 8)); // execute program change
return;
case MILES_CONTROLLER_PATCH_BENDER:
channelPatchId = _midiChannels[midiChannel].currentPatchId;
writePatchByte(channelPatchId, 4, controllerValue);
_driver->send(0xC0 | midiChannel | (channelPatchId << 8)); // execute program change
return;
case MILES_CONTROLLER_REVERB_MODE:
writeToSystemArea(1, controllerValue);
return;
case MILES_CONTROLLER_REVERB_TIME:
writeToSystemArea(2, controllerValue);
return;
case MILES_CONTROLLER_REVERB_LEVEL:
writeToSystemArea(3, controllerValue);
return;
case MILES_CONTROLLER_RHYTHM_KEY_TIMBRE:
if (_midiChannels[midiChannel].usingCustomTimbre) {
// custom timbre is set on current channel
writeRhythmSetup(controllerValue, _midiChannels[midiChannel].currentCustomTimbreId);
}
return;
case MILES_CONTROLLER_PROTECT_TIMBRE:
if (_midiChannels[midiChannel].usingCustomTimbre) {
// custom timbre set on current channel
channelCustomTimbreId = _midiChannels[midiChannel].currentCustomTimbreId;
if (controllerValue >= 64) {
// enable protection
_customTimbres[channelCustomTimbreId].protectionEnabled = true;
} else {
// disable protection
_customTimbres[channelCustomTimbreId].protectionEnabled = false;
}
}
return;
default:
break;
}
if ((controllerNumber >= MILES_CONTROLLER_SYSEX_RANGE_BEGIN) && (controllerNumber <= MILES_CONTROLLER_SYSEX_RANGE_END)) {
// send SysEx
byte sysExQueueNr = 0;
// figure out which queue is accessed
controllerNumber -= MILES_CONTROLLER_SYSEX_RANGE_BEGIN;
while (controllerNumber > MILES_CONTROLLER_SYSEX_COMMAND_SEND) {
sysExQueueNr++;
controllerNumber -= (MILES_CONTROLLER_SYSEX_COMMAND_SEND + 1);
}
assert(sysExQueueNr < MILES_CONTROLLER_SYSEX_QUEUE_COUNT);
byte sysExPos = _sysExQueues[sysExQueueNr].dataPos;
bool sysExSend = false;
switch(controllerNumber) {
case MILES_CONTROLLER_SYSEX_COMMAND_ADDRESS1:
_sysExQueues[sysExQueueNr].targetAddress &= 0x00FFFF;
_sysExQueues[sysExQueueNr].targetAddress |= (controllerValue << 16);
break;
case MILES_CONTROLLER_SYSEX_COMMAND_ADDRESS2:
_sysExQueues[sysExQueueNr].targetAddress &= 0xFF00FF;
_sysExQueues[sysExQueueNr].targetAddress |= (controllerValue << 8);
break;
case MILES_CONTROLLER_SYSEX_COMMAND_ADDRESS3:
_sysExQueues[sysExQueueNr].targetAddress &= 0xFFFF00;
_sysExQueues[sysExQueueNr].targetAddress |= controllerValue;
break;
case MILES_CONTROLLER_SYSEX_COMMAND_DATA:
if (sysExPos < MILES_CONTROLLER_SYSEX_QUEUE_SIZE) {
// Space left? put current byte into queue
_sysExQueues[sysExQueueNr].data[sysExPos] = controllerValue;
sysExPos++;
_sysExQueues[sysExQueueNr].dataPos = sysExPos;
if (sysExPos >= MILES_CONTROLLER_SYSEX_QUEUE_SIZE) {
// overflow? -> send it now
sysExSend = true;
}
}
break;
case MILES_CONTROLLER_SYSEX_COMMAND_SEND:
sysExSend = true;
break;
default:
assert(0);
}
if (sysExSend) {
if (sysExPos > 0) {
// data actually available? -> send it
_sysExQueues[sysExQueueNr].data[sysExPos] = MILES_MT32_SYSEX_TERMINATOR; // put terminator
// Execute SysEx
MT32SysEx(_sysExQueues[sysExQueueNr].targetAddress, _sysExQueues[sysExQueueNr].data);
// adjust target address to point at the end of the current data
_sysExQueues[sysExQueueNr].targetAddress += sysExPos;
// reset queue data buffer
_sysExQueues[sysExQueueNr].dataPos = 0;
}
}
return;
}
if ((controllerNumber >= MILES_CONTROLLER_XMIDI_RANGE_BEGIN) && (controllerNumber <= MILES_CONTROLLER_XMIDI_RANGE_END)) {
// XMIDI controllers? ignore those
return;
}
_driver->send(0xB0 | midiChannel | (controllerNumber << 8) | (controllerValue << 16));
}
void MidiDriver_Miles_MT32::programChange(byte midiChannel, byte patchId) {
byte channelPatchBank = _midiChannels[midiChannel].currentPatchBank;
byte activePatchBank = _patchesBank[patchId];
//warning("patch channel %d, patch %x, bank %x", midiChannel, patchId, channelPatchBank);
// remember patch id for the current MIDI-channel
_midiChannels[midiChannel].currentPatchId = patchId;
if (channelPatchBank != activePatchBank) {
// associate patch with timbre
setupPatch(channelPatchBank, patchId);
}
// If this is a custom patch, remember customTimbreId
int16 customTimbre = searchCustomTimbre(channelPatchBank, patchId);
if (customTimbre >= 0) {
_midiChannels[midiChannel].usingCustomTimbre = true;
_midiChannels[midiChannel].currentCustomTimbreId = customTimbre;
} else {
_midiChannels[midiChannel].usingCustomTimbre = false;
}
// Finally send program change to MT32
_driver->send(0xC0 | midiChannel | (patchId << 8));
}
int16 MidiDriver_Miles_MT32::searchCustomTimbre(byte patchBank, byte patchId) {
byte customTimbreId = 0;
for (customTimbreId = 0; customTimbreId < MILES_MT32_CUSTOMTIMBRE_COUNT; customTimbreId++) {
if (_customTimbres[customTimbreId].used) {
if ((_customTimbres[customTimbreId].currentPatchBank == patchBank) && (_customTimbres[customTimbreId].currentPatchId == patchId)) {
return customTimbreId;
}
}
}
return -1;
}
const MilesMT32InstrumentEntry *MidiDriver_Miles_MT32::searchCustomInstrument(byte patchBank, byte patchId) {
const MilesMT32InstrumentEntry *instrumentPtr = _instrumentTablePtr;
for (uint16 instrumentNr = 0; instrumentNr < _instrumentTableCount; instrumentNr++) {
if ((instrumentPtr->bankId == patchBank) && (instrumentPtr->patchId == patchId))
return instrumentPtr;
instrumentPtr++;
}
return NULL;
}
void MidiDriver_Miles_MT32::setupPatch(byte patchBank, byte patchId) {
_patchesBank[patchId] = patchBank;
if (patchBank) {
// non-built-in bank
int16 customTimbreId = searchCustomTimbre(patchBank, patchId);
if (customTimbreId >= 0) {
// now available? -> use this timbre
writePatchTimbre(patchId, 2, customTimbreId); // Group MEMORY
return;
}
}
// for built-in bank (or timbres, that are not available) use default MT32 timbres
byte timbreId = patchId & 0x3F;
if (!(patchId & 0x40)) {
writePatchTimbre(patchId, 0, timbreId); // Group A
} else {
writePatchTimbre(patchId, 1, timbreId); // Group B
}
}
void MidiDriver_Miles_MT32::processXMIDITimbreChunk(const byte *timbreListPtr, uint32 timbreListSize) {
uint16 timbreCount = 0;
uint32 expectedSize = 0;
const byte *timbreListSeeker = timbreListPtr;
if (timbreListSize < 2) {
warning("MILES-MT32: XMIDI-TIMB chunk - not enough bytes in chunk");
return;
}
timbreCount = READ_LE_UINT16(timbreListPtr);
expectedSize = timbreCount * 2;
if (expectedSize > timbreListSize) {
warning("MILES-MT32: XMIDI-TIMB chunk - size mismatch");
return;
}
timbreListSeeker += 2;
while (timbreCount) {
const byte patchId = *timbreListSeeker++;
const byte patchBank = *timbreListSeeker++;
int16 customTimbreId = 0;
switch (patchBank) {
case MILES_MT32_TIMBREBANK_STANDARD_ROLAND:
case MILES_MT32_TIMBREBANK_MELODIC_MODULE:
// ignore those 2 banks
break;
default:
// Check, if this timbre was already loaded
customTimbreId = searchCustomTimbre(patchBank, patchId);
if (customTimbreId < 0) {
// currently not loaded, try to install it
installCustomTimbre(patchBank, patchId);
}
}
timbreCount--;
}
}
//
int16 MidiDriver_Miles_MT32::installCustomTimbre(byte patchBank, byte patchId) {
switch(patchBank) {
case MILES_MT32_TIMBREBANK_STANDARD_ROLAND: // Standard Roland MT32 bank
case MILES_MT32_TIMBREBANK_MELODIC_MODULE: // Reserved for melodic mode
return -1;
default:
break;
}
// Original driver did a search for custom timbre here
// and in case it was found, it would call setup_patch()
// we are called from within setup_patch(), so this isn't needed
int16 customTimbreId = -1;
int16 leastUsedTimbreId = -1;
uint32 leastUsedTimbreNoteCounter = _noteCounter;
const MilesMT32InstrumentEntry *instrumentPtr = NULL;
// Check, if requested instrument is actually available
instrumentPtr = searchCustomInstrument(patchBank, patchId);
if (!instrumentPtr) {
warning("MILES-MT32: instrument not found during installCustomTimbre()");
return -1; // not found -> bail out
}
// Look for an empty timbre slot
// or get the least used non-protected slot
for (byte customTimbreNr = 0; customTimbreNr < MILES_MT32_CUSTOMTIMBRE_COUNT; customTimbreNr++) {
if (!_customTimbres[customTimbreNr].used) {
// found an empty slot -> use this one
customTimbreId = customTimbreNr;
break;
} else {
// used slot
if (!_customTimbres[customTimbreNr].protectionEnabled) {
// not protected
uint32 customTimbreNoteCounter = _customTimbres[customTimbreNr].lastUsedNoteCounter;
if (customTimbreNoteCounter < leastUsedTimbreNoteCounter) {
leastUsedTimbreId = customTimbreNr;
leastUsedTimbreNoteCounter = customTimbreNoteCounter;
}
}
}
}
if (customTimbreId < 0) {
// no empty slot found, check if we got a least used non-protected slot
if (leastUsedTimbreId < 0) {
// everything is protected, bail out
warning("MILES-MT32: no non-protected timbre slots available during installCustomTimbre()");
return -1;
}
customTimbreId = leastUsedTimbreId;
}
// setup timbre slot
_customTimbres[customTimbreId].used = true;
_customTimbres[customTimbreId].currentPatchBank = patchBank;
_customTimbres[customTimbreId].currentPatchId = patchId;
_customTimbres[customTimbreId].lastUsedNoteCounter = _noteCounter;
_customTimbres[customTimbreId].protectionEnabled = false;
uint32 targetAddress = 0x080000 | (customTimbreId << 9);
uint32 targetAddressCommon = targetAddress + 0x000000;
uint32 targetAddressPartial1 = targetAddress + 0x00000E;
uint32 targetAddressPartial2 = targetAddress + 0x000048;
uint32 targetAddressPartial3 = targetAddress + 0x000102;
uint32 targetAddressPartial4 = targetAddress + 0x00013C;
#if 0
byte parameterData[MILES_MT32_PATCHDATA_TOTAL_SIZE + 1];
uint16 parameterDataPos = 0;
memcpy(parameterData, instrumentPtr->commonParameter, MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE);
parameterDataPos += MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE;
memcpy(parameterData + parameterDataPos, instrumentPtr->partialParameters[0], MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE);
parameterDataPos += MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE;
memcpy(parameterData + parameterDataPos, instrumentPtr->partialParameters[1], MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE);
parameterDataPos += MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE;
memcpy(parameterData + parameterDataPos, instrumentPtr->partialParameters[2], MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE);
parameterDataPos += MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE;
memcpy(parameterData + parameterDataPos, instrumentPtr->partialParameters[3], MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE);
parameterDataPos += MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE;
parameterData[parameterDataPos] = MILES_MT32_SYSEX_TERMINATOR;
MT32SysEx(targetAddressCommon, parameterData);
#endif
// upload common parameter data
MT32SysEx(targetAddressCommon, instrumentPtr->commonParameter);
// upload partial parameter data
MT32SysEx(targetAddressPartial1, instrumentPtr->partialParameters[0]);
MT32SysEx(targetAddressPartial2, instrumentPtr->partialParameters[1]);
MT32SysEx(targetAddressPartial3, instrumentPtr->partialParameters[2]);
MT32SysEx(targetAddressPartial4, instrumentPtr->partialParameters[3]);
setupPatch(patchBank, patchId);
return customTimbreId;
}
uint32 MidiDriver_Miles_MT32::calculateSysExTargetAddress(uint32 baseAddress, uint32 index) {
uint16 targetAddressLSB = baseAddress & 0xFF;
uint16 targetAddressKSB = (baseAddress >> 8) & 0xFF;
uint16 targetAddressMSB = (baseAddress >> 16) & 0xFF;
// add index to it, but use 7-bit of the index for each byte
targetAddressLSB += (index & 0x7F);
targetAddressKSB += ((index >> 7) & 0x7F);
targetAddressMSB += ((index >> 14) & 0x7F);
// adjust bytes, so that none of them is above or equal 0x80
while (targetAddressLSB >= 0x80) {
targetAddressLSB -= 0x80;
targetAddressKSB++;
}
while (targetAddressKSB >= 0x80) {
targetAddressKSB -= 0x80;
targetAddressMSB++;
}
assert(targetAddressMSB < 0x80);
// put everything together
return targetAddressLSB | (targetAddressKSB << 8) | (targetAddressMSB << 16);
}
void MidiDriver_Miles_MT32::writeRhythmSetup(byte note, byte customTimbreId) {
byte sysExData[2];
uint32 targetAddress = 0;
targetAddress = calculateSysExTargetAddress(0x030110, ((note - 24) << 2));
sysExData[0] = customTimbreId;
sysExData[1] = MILES_MT32_SYSEX_TERMINATOR; // terminator
MT32SysEx(targetAddress, sysExData);
}
void MidiDriver_Miles_MT32::writePatchTimbre(byte patchId, byte timbreGroup, byte timbreId) {
byte sysExData[3];
uint32 targetAddress = 0;
// write to patch memory (starts at 0x050000, each entry is 8 bytes)
targetAddress = calculateSysExTargetAddress(0x050000, patchId << 3);
sysExData[0] = timbreGroup; // 0 - group A, 1 - group B, 2 - memory, 3 - rhythm
sysExData[1] = timbreId; // timbre number (0-63)
sysExData[2] = MILES_MT32_SYSEX_TERMINATOR; // terminator
MT32SysEx(targetAddress, sysExData);
}
void MidiDriver_Miles_MT32::writePatchByte(byte patchId, byte index, byte patchValue) {
byte sysExData[2];
uint32 targetAddress = 0;
targetAddress = calculateSysExTargetAddress(0x050000, (patchId << 3) + index);
sysExData[0] = patchValue;
sysExData[1] = MILES_MT32_SYSEX_TERMINATOR; // terminator
MT32SysEx(targetAddress, sysExData);
}
void MidiDriver_Miles_MT32::writeToSystemArea(byte index, byte value) {
byte sysExData[2];
uint32 targetAddress = 0;
targetAddress = calculateSysExTargetAddress(0x100000, index);
sysExData[0] = value;
sysExData[1] = MILES_MT32_SYSEX_TERMINATOR; // terminator
MT32SysEx(targetAddress, sysExData);
}
MidiDriver *MidiDriver_Miles_MT32_create(const Common::String &instrumentDataFilename) {
MilesMT32InstrumentEntry *instrumentTablePtr = NULL;
uint16 instrumentTableCount = 0;
if (!instrumentDataFilename.empty()) {
// Load MT32 instrument data from file SAMPLE.MT
Common::File *fileStream = new Common::File();
uint32 fileSize = 0;
byte *fileDataPtr = NULL;
uint32 fileDataOffset = 0;
uint32 fileDataLeft = 0;
byte curBankId = 0;
byte curPatchId = 0;
MilesMT32InstrumentEntry *instrumentPtr = NULL;
uint32 instrumentOffset = 0;
uint16 instrumentDataSize = 0;
if (!fileStream->open(instrumentDataFilename))
error("MILES-MT32: could not open instrument file '%s'", instrumentDataFilename.c_str());
fileSize = fileStream->size();
fileDataPtr = new byte[fileSize];
if (fileStream->read(fileDataPtr, fileSize) != fileSize)
error("MILES-MT32: error while reading instrument file");
fileStream->close();
delete fileStream;
// File is like this:
// [patch:BYTE] [bank:BYTE] [patchoffset:UINT32]
// ...
// until patch + bank are both 0xFF, which signals end of header
// First we check how many entries there are
fileDataOffset = 0;
fileDataLeft = fileSize;
while (1) {
if (fileDataLeft < 6)
error("MILES-MT32: unexpected EOF in instrument file");
curPatchId = fileDataPtr[fileDataOffset++];
curBankId = fileDataPtr[fileDataOffset++];
if ((curBankId == 0xFF) && (curPatchId == 0xFF))
break;
fileDataOffset += 4; // skip over offset
instrumentTableCount++;
}
if (instrumentTableCount == 0)
error("MILES-MT32: no instruments in instrument file");
// Allocate space for instruments
instrumentTablePtr = new MilesMT32InstrumentEntry[instrumentTableCount];
// Now actually read all entries
instrumentPtr = instrumentTablePtr;
fileDataOffset = 0;
fileDataLeft = fileSize;
while (1) {
curPatchId = fileDataPtr[fileDataOffset++];
curBankId = fileDataPtr[fileDataOffset++];
if ((curBankId == 0xFF) && (curPatchId == 0xFF))
break;
instrumentOffset = READ_LE_UINT32(fileDataPtr + fileDataOffset);
fileDataOffset += 4;
instrumentPtr->bankId = curBankId;
instrumentPtr->patchId = curPatchId;
instrumentDataSize = READ_LE_UINT16(fileDataPtr + instrumentOffset);
if (instrumentDataSize != (MILES_MT32_PATCHDATA_TOTAL_SIZE + 2))
error("MILES-MT32: unsupported instrument size");
instrumentOffset += 2;
// Copy common parameter data
memcpy(instrumentPtr->commonParameter, fileDataPtr + instrumentOffset, MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE);
instrumentPtr->commonParameter[MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE] = MILES_MT32_SYSEX_TERMINATOR; // Terminator
instrumentOffset += MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE;
// Copy partial parameter data
for (byte partialNr = 0; partialNr < MILES_MT32_PATCHDATA_PARTIALPARAMETERS_COUNT; partialNr++) {
memcpy(&instrumentPtr->partialParameters[partialNr], fileDataPtr + instrumentOffset, MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE);
instrumentPtr->partialParameters[partialNr][MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE] = MILES_MT32_SYSEX_TERMINATOR; // Terminator
instrumentOffset += MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE;
}
// Instrument read, next instrument please
instrumentPtr++;
}
// Free instrument file data
delete[] fileDataPtr;
}
return new MidiDriver_Miles_MT32(instrumentTablePtr, instrumentTableCount);
}
void MidiDriver_Miles_MT32_processXMIDITimbreChunk(MidiDriver_BASE *driver, const byte *timbreListPtr, uint32 timbreListSize) {
MidiDriver_Miles_MT32 *driverMT32 = dynamic_cast<MidiDriver_Miles_MT32 *>(driver);
if (driverMT32) {
driverMT32->processXMIDITimbreChunk(timbreListPtr, timbreListSize);
}
}
} // End of namespace Audio

View file

@ -1,16 +1,20 @@
MODULE := audio
MODULE_OBJS := \
adlib.o \
audiostream.o \
fmopl.o \
mididrv.o \
midiparser.o \
midiparser_qt.o \
midiparser.o \
miles_adlib.o \
miles_mt32.o \
mixer.o \
mpu401.o \
musicplugin.o \
null.o \
timestamp.o \
decoders/3do.o \
decoders/aac.o \
decoders/adpcm.o \
decoders/aiff.o \
@ -27,7 +31,6 @@ MODULE_OBJS := \
decoders/wave.o \
decoders/wma.o \
decoders/xa.o \
softsynth/adlib.o \
softsynth/cms.o \
softsynth/opl/dbopl.o \
softsynth/opl/dosbox.o \
@ -35,6 +38,11 @@ MODULE_OBJS := \
softsynth/fluidsynth.o \
softsynth/mt32.o
ifdef USE_ALSA
MODULE_OBJS += \
alsa_opl.o
endif
ifndef USE_ARM_SOUND_ASM
MODULE_OBJS += \
rate.o

View file

@ -32,6 +32,7 @@
#include "dosbox.h"
#include "dbopl.h"
#include "audio/mixer.h"
#include "common/system.h"
#include "common/scummsys.h"
#include "common/util.h"
@ -148,6 +149,7 @@ OPL::OPL(Config::OplType type) : _type(type), _rate(0), _emulator(0) {
}
OPL::~OPL() {
stop();
free();
}
@ -156,7 +158,7 @@ void OPL::free() {
_emulator = 0;
}
bool OPL::init(int rate) {
bool OPL::init() {
free();
memset(&_reg, 0, sizeof(_reg));
@ -167,19 +169,19 @@ bool OPL::init(int rate) {
return false;
DBOPL::InitTables();
_emulator->Setup(rate);
_rate = g_system->getMixer()->getOutputRate();
_emulator->Setup(_rate);
if (_type == Config::kDualOpl2) {
// Setup opl3 mode in the hander
_emulator->WriteReg(0x105, 1);
}
_rate = rate;
return true;
}
void OPL::reset() {
init(_rate);
init();
}
void OPL::write(int port, int val) {
@ -307,7 +309,7 @@ void OPL::dualWrite(uint8 index, uint8 reg, uint8 val) {
_emulator->WriteReg(fullReg, val);
}
void OPL::readBuffer(int16 *buffer, int length) {
void OPL::generateSamples(int16 *buffer, int length) {
// For stereo OPL cards, we divide the sample count by 2,
// to match stereo AudioStream behavior.
if (_type != Config::kOpl2)

View file

@ -69,7 +69,7 @@ namespace DBOPL {
struct Chip;
} // end of namespace DBOPL
class OPL : public ::OPL::OPL {
class OPL : public ::OPL::EmulatedOPL {
private:
Config::OplType _type;
uint _rate;
@ -87,7 +87,7 @@ public:
OPL(Config::OplType type);
~OPL();
bool init(int rate);
bool init();
void reset();
void write(int a, int v);
@ -95,8 +95,10 @@ public:
void writeReg(int r, int v);
void readBuffer(int16 *buffer, int length);
bool isStereo() const { return _type != Config::kOpl2; }
protected:
void generateSamples(int16 *buffer, int length);
};
} // End of namespace DOSBox

View file

@ -31,6 +31,8 @@
#include "mame.h"
#include "audio/mixer.h"
#include "common/system.h"
#include "common/textconsole.h"
#include "common/util.h"
@ -46,15 +48,19 @@ namespace OPL {
namespace MAME {
OPL::~OPL() {
stop();
MAME::OPLDestroy(_opl);
_opl = 0;
}
bool OPL::init(int rate) {
if (_opl)
bool OPL::init() {
if (_opl) {
stopCallbacks();
MAME::OPLDestroy(_opl);
}
_opl = MAME::makeAdLibOPL(g_system->getMixer()->getOutputRate());
_opl = MAME::makeAdLibOPL(rate);
return (_opl != 0);
}
@ -74,7 +80,7 @@ void OPL::writeReg(int r, int v) {
MAME::OPLWriteReg(_opl, r, v);
}
void OPL::readBuffer(int16 *buffer, int length) {
void OPL::generateSamples(int16 *buffer, int length) {
MAME::YM3812UpdateOne(_opl, buffer, length);
}

View file

@ -174,14 +174,14 @@ void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length);
FM_OPL *makeAdLibOPL(int rate);
// OPL API implementation
class OPL : public ::OPL::OPL {
class OPL : public ::OPL::EmulatedOPL {
private:
FM_OPL *_opl;
public:
OPL() : _opl(0) {}
~OPL();
bool init(int rate);
bool init();
void reset();
void write(int a, int v);
@ -189,8 +189,10 @@ public:
void writeReg(int r, int v);
void readBuffer(int16 *buffer, int length);
bool isStereo() const { return false; }
protected:
void generateSamples(int16 *buffer, int length);
};
} // End of namespace MAME

2318
b/audio/adlib.cpp Normal file

File diff suppressed because it is too large Load diff

349
b/audio/alsa_opl.cpp Normal file
View file

@ -0,0 +1,349 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
/* OPL implementation for hardware OPL using ALSA Direct FM API.
*
* Caveats and limitations:
* - Pretends to be a softsynth (emitting silence).
* - Dual OPL2 mode requires OPL3 hardware.
* - Every register write leads to a series of register writes on the hardware,
* due to the lack of direct register access in the ALSA Direct FM API.
* - No timers
*/
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "common/scummsys.h"
#include "common/debug.h"
#include "common/str.h"
#include "audio/fmopl.h"
#include <sys/ioctl.h>
#include <alsa/asoundlib.h>
#include <sound/asound_fm.h>
namespace OPL {
namespace ALSA {
class OPL : public ::OPL::RealOPL {
private:
enum {
kOpl2Voices = 9,
kVoices = 18,
kOpl2Operators = 18,
kOperators = 36
};
Config::OplType _type;
int _iface;
snd_hwdep_t *_opl;
snd_dm_fm_voice _oper[kOperators];
snd_dm_fm_note _voice[kVoices];
snd_dm_fm_params _params;
int index[2];
static const int voiceToOper0[kVoices];
static const int regOffsetToOper[0x20];
void writeOplReg(int c, int r, int v);
void clear();
public:
OPL(Config::OplType type);
~OPL();
bool init();
void reset();
void write(int a, int v);
byte read(int a);
void writeReg(int r, int v);
};
const int OPL::voiceToOper0[OPL::kVoices] =
{ 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 };
const int OPL::regOffsetToOper[0x20] =
{ 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1,
12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
OPL::OPL(Config::OplType type) : _type(type), _opl(nullptr), _iface(0) {
}
OPL::~OPL() {
stop();
if (_opl) {
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr);
snd_hwdep_close(_opl);
}
}
void OPL::clear() {
index[0] = index[1] = 0;
memset(_oper, 0, sizeof(_oper));
memset(_voice, 0, sizeof(_voice));
memset(&_params, 0, sizeof(_params));
for (int i = 0; i < kOperators; ++i) {
_oper[i].op = (i / 3) % 2;
_oper[i].voice = (i / 6) * 3 + (i % 3);
}
for (int i = 0; i < kVoices; ++i)
_voice[i].voice = i;
// For OPL3 hardware we need to set up the panning in OPL2 modes
if (_iface == SND_HWDEP_IFACE_OPL3) {
if (_type == Config::kDualOpl2) {
for (int i = 0; i < kOpl2Operators; ++i)
_oper[i].left = 1; // FIXME below
for (int i = kOpl2Operators; i < kOperators; ++i)
_oper[i].right = 1;
} else if (_type == Config::kOpl2) {
for (int i = 0; i < kOpl2Operators; ++i) {
_oper[i].left = 1;
_oper[i].right = 1;
}
}
}
}
bool OPL::init() {
clear();
int card = -1;
snd_ctl_t *ctl;
snd_hwdep_info_t *info;
snd_hwdep_info_alloca(&info);
int iface = SND_HWDEP_IFACE_OPL3;
if (_type == Config::kOpl2)
iface = SND_HWDEP_IFACE_OPL2;
// Look for OPL hwdep interface
while (!snd_card_next(&card) && card >= 0) {
int dev = -1;
Common::String name = Common::String::format("hw:%d", card);
if (snd_ctl_open(&ctl, name.c_str(), 0) < 0)
continue;
while (!snd_ctl_hwdep_next_device(ctl, &dev) && dev >= 0) {
name = Common::String::format("hw:%d,%d", card, dev);
if (snd_hwdep_open(&_opl, name.c_str(), SND_HWDEP_OPEN_WRITE) < 0)
continue;
if (!snd_hwdep_info(_opl, info)) {
int found = snd_hwdep_info_get_iface(info);
// OPL3 can be used for (Dual) OPL2 mode
if (found == iface || found == SND_HWDEP_IFACE_OPL3) {
snd_ctl_close(ctl);
_iface = found;
reset();
return true;
}
}
// Wrong interface, try next device
snd_hwdep_close(_opl);
_opl = nullptr;
}
snd_ctl_close(ctl);
}
return false;
}
void OPL::reset() {
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr);
if (_iface == SND_HWDEP_IFACE_OPL3)
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_MODE, (void *)SNDRV_DM_FM_MODE_OPL3);
clear();
// Sync up with the hardware
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kVoices : kOpl2Voices); ++i)
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[i]);
for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kOperators : kOpl2Operators); ++i)
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[i]);
}
void OPL::write(int port, int val) {
val &= 0xff;
int chip = (port & 2) >> 1;
if (port & 1) {
switch(_type) {
case Config::kOpl2:
writeOplReg(0, index[0], val);
break;
case Config::kDualOpl2:
if (port & 8) {
writeOplReg(0, index[0], val);
writeOplReg(1, index[1], val);
} else
writeOplReg(chip, index[chip], val);
break;
case Config::kOpl3:
writeOplReg(chip, index[chip], val);
}
} else {
switch(_type) {
case Config::kOpl2:
index[0] = val;
break;
case Config::kDualOpl2:
if (port & 8) {
index[0] = val;
index[1] = val;
} else
index[chip] = val;
break;
case Config::kOpl3:
index[chip] = val;
}
}
}
byte OPL::read(int port) {
return 0;
}
void OPL::writeReg(int r, int v) {
switch (_type) {
case Config::kOpl2:
writeOplReg(0, r, v);
break;
case Config::kDualOpl2:
writeOplReg(0, r, v);
writeOplReg(1, r, v);
break;
case Config::kOpl3:
writeOplReg(r >= 0x100, r & 0xff, v);
}
}
void OPL::writeOplReg(int c, int r, int v) {
if (r == 0x04 && c == 1 && _type == Config::kOpl3) {
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_CONNECTION, reinterpret_cast<void *>(v & 0x3f));
} else if (r == 0x08 && c == 0) {
_params.kbd_split = (v >> 6) & 0x1;
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
} else if (r == 0xbd && c == 0) {
_params.hihat = v & 0x1;
_params.cymbal = (v >> 1) & 0x1;
_params.tomtom = (v >> 2) & 0x1;
_params.snare = (v >> 3) & 0x1;
_params.bass = (v >> 4) & 0x1;
_params.rhythm = (v >> 5) & 0x1;
_params.vib_depth = (v >> 6) & 0x1;
_params.am_depth = (v >> 7) & 0x1;
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
} else if (r < 0xa0 || r >= 0xe0) {
// Operator
int idx = regOffsetToOper[r & 0x1f];
if (idx == -1)
return;
if (c == 1)
idx += kOpl2Operators;
switch (r & 0xf0) {
case 0x20:
case 0x30:
_oper[idx].harmonic = v & 0xf;
_oper[idx].kbd_scale = (v >> 4) & 0x1;
_oper[idx].do_sustain = (v >> 5) & 0x1;
_oper[idx].vibrato = (v >> 6) & 0x1;
_oper[idx].am = (v >> 7) & 0x1;
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
break;
case 0x40:
case 0x50:
_oper[idx].volume = ~v & 0x3f;
_oper[idx].scale_level = (v >> 6) & 0x3;
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
break;
case 0x60:
case 0x70:
_oper[idx].decay = v & 0xf;
_oper[idx].attack = (v >> 4) & 0xf;
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
break;
case 0x80:
case 0x90:
_oper[idx].release = v & 0xf;
_oper[idx].sustain = (v >> 4) & 0xf;
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
break;
case 0xe0:
case 0xf0:
_oper[idx].waveform = v & (_type == Config::kOpl3 ? 0x7 : 0x3);
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
}
} else {
// Voice
int idx = r & 0xf;
if (idx >= kOpl2Voices)
return;
if (c == 1)
idx += kOpl2Voices;
int opIdx = voiceToOper0[idx];
switch (r & 0xf0) {
case 0xa0:
_voice[idx].fnum = (_voice[idx].fnum & 0x300) | (v & 0xff);
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]);
break;
case 0xb0:
_voice[idx].fnum = ((v << 8) & 0x300) | (_voice[idx].fnum & 0xff);
_voice[idx].octave = (v >> 2) & 0x7;
_voice[idx].key_on = (v >> 5) & 0x1;
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]);
break;
case 0xc0:
_oper[opIdx].connection = _oper[opIdx + 3].connection = v & 0x1;
_oper[opIdx].feedback = _oper[opIdx + 3].feedback = (v >> 1) & 0x7;
if (_type == Config::kOpl3) {
_oper[opIdx].left = _oper[opIdx + 3].left = (v >> 4) & 0x1;
_oper[opIdx].right = _oper[opIdx + 3].right = (v >> 5) & 0x1;
}
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[opIdx]);
}
}
}
OPL *create(Config::OplType type) {
return new OPL(type);
}
} // End of namespace ALSA
} // End of namespace OPL

343
b/audio/decoders/3do.cpp Normal file
View file

@ -0,0 +1,343 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/textconsole.h"
#include "common/stream.h"
#include "common/util.h"
#include "audio/decoders/3do.h"
#include "audio/decoders/raw.h"
#include "audio/decoders/adpcm_intern.h"
namespace Audio {
// Reuses ADPCM table
#define audio_3DO_ADP4_stepSizeTable Ima_ADPCMStream::_imaTable
#define audio_3DO_ADP4_stepSizeIndex ADPCMStream::_stepAdjustTable
RewindableAudioStream *make3DO_ADP4AudioStream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, uint32 *audioLengthMSecsPtr, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace) {
if (stereo) {
warning("make3DO_ADP4Stream(): stereo currently not supported");
return 0;
}
if (audioLengthMSecsPtr) {
// Caller requires the milliseconds of audio
uint32 audioLengthMSecs = stream->size() * 2 * 1000 / sampleRate; // 1 byte == 2 16-bit sample
if (stereo) {
audioLengthMSecs /= 2;
}
*audioLengthMSecsPtr = audioLengthMSecs;
}
return new Audio3DO_ADP4_Stream(stream, sampleRate, stereo, disposeAfterUse, persistentSpace);
}
Audio3DO_ADP4_Stream::Audio3DO_ADP4_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace)
: _sampleRate(sampleRate), _stereo(stereo),
_stream(stream, disposeAfterUse) {
_callerDecoderData = persistentSpace;
memset(&_initialDecoderData, 0, sizeof(_initialDecoderData));
_initialRead = true;
reset();
}
void Audio3DO_ADP4_Stream::reset() {
memcpy(&_curDecoderData, &_initialDecoderData, sizeof(_curDecoderData));
_streamBytesLeft = _stream->size();
_stream->seek(0);
}
bool Audio3DO_ADP4_Stream::rewind() {
reset();
return true;
}
int16 Audio3DO_ADP4_Stream::decodeSample(byte compressedNibble) {
int16 currentStep = audio_3DO_ADP4_stepSizeTable[_curDecoderData.stepIndex];
int32 decodedSample = _curDecoderData.lastSample;
int16 delta = currentStep >> 3;
if (compressedNibble & 1)
delta += currentStep >> 2;
if (compressedNibble & 2)
delta += currentStep >> 1;
if (compressedNibble & 4)
delta += currentStep;
if (compressedNibble & 8) {
decodedSample -= delta;
} else {
decodedSample += delta;
}
_curDecoderData.lastSample = CLIP<int32>(decodedSample, -32768, 32767);
_curDecoderData.stepIndex += audio_3DO_ADP4_stepSizeIndex[compressedNibble & 0x07];
_curDecoderData.stepIndex = CLIP<int16>(_curDecoderData.stepIndex, 0, ARRAYSIZE(audio_3DO_ADP4_stepSizeTable) - 1);
return _curDecoderData.lastSample;
}
// Writes the requested amount (or less) of samples into buffer and returns the amount of samples, that got written
int Audio3DO_ADP4_Stream::readBuffer(int16 *buffer, const int numSamples) {
int8 byteCache[AUDIO_3DO_CACHE_SIZE];
int8 *byteCachePtr = NULL;
int byteCacheSize = 0;
int requestedBytesLeft = 0;
int decodedSamplesCount = 0;
int8 compressedByte = 0;
if (endOfData())
return 0; // no more bytes left
if (_callerDecoderData) {
// copy caller decoder data over
memcpy(&_curDecoderData, _callerDecoderData, sizeof(_curDecoderData));
if (_initialRead) {
_initialRead = false;
memcpy(&_initialDecoderData, &_curDecoderData, sizeof(_initialDecoderData));
}
}
requestedBytesLeft = numSamples >> 1; // 1 byte for 2 16-bit sample
if (requestedBytesLeft > _streamBytesLeft)
requestedBytesLeft = _streamBytesLeft; // not enough bytes left
// in case caller requests an uneven amount of samples, we will return an even amount
// buffering, so that direct decoding of files and such runs way faster
while (requestedBytesLeft) {
if (requestedBytesLeft > AUDIO_3DO_CACHE_SIZE) {
byteCacheSize = AUDIO_3DO_CACHE_SIZE;
} else {
byteCacheSize = requestedBytesLeft;
}
requestedBytesLeft -= byteCacheSize;
_streamBytesLeft -= byteCacheSize;
// Fill our byte cache
_stream->read(byteCache, byteCacheSize);
byteCachePtr = byteCache;
// Mono
while (byteCacheSize) {
compressedByte = *byteCachePtr++;
byteCacheSize--;
buffer[decodedSamplesCount] = decodeSample(compressedByte >> 4);
decodedSamplesCount++;
buffer[decodedSamplesCount] = decodeSample(compressedByte & 0x0f);
decodedSamplesCount++;
}
}
if (_callerDecoderData) {
// copy caller decoder data back
memcpy(_callerDecoderData, &_curDecoderData, sizeof(_curDecoderData));
}
return decodedSamplesCount;
}
// ============================================================================
static int16 audio_3DO_SDX2_SquareTable[256] = {
-32768,-32258,-31752,-31250,-30752,-30258,-29768,-29282,-28800,-28322,
-27848,-27378,-26912,-26450,-25992,-25538,-25088,-24642,-24200,-23762,
-23328,-22898,-22472,-22050,-21632,-21218,-20808,-20402,-20000,-19602,
-19208,-18818,-18432,-18050,-17672,-17298,-16928,-16562,-16200,-15842,
-15488,-15138,-14792,-14450,-14112,-13778,-13448,-13122,-12800,-12482,
-12168,-11858,-11552,-11250,-10952,-10658,-10368,-10082, -9800, -9522,
-9248, -8978, -8712, -8450, -8192, -7938, -7688, -7442, -7200, -6962,
-6728, -6498, -6272, -6050, -5832, -5618, -5408, -5202, -5000, -4802,
-4608, -4418, -4232, -4050, -3872, -3698, -3528, -3362, -3200, -3042,
-2888, -2738, -2592, -2450, -2312, -2178, -2048, -1922, -1800, -1682,
-1568, -1458, -1352, -1250, -1152, -1058, -968, -882, -800, -722,
-648, -578, -512, -450, -392, -338, -288, -242, -200, -162,
-128, -98, -72, -50, -32, -18, -8, -2, 0, 2,
8, 18, 32, 50, 72, 98, 128, 162, 200, 242,
288, 338, 392, 450, 512, 578, 648, 722, 800, 882,
968, 1058, 1152, 1250, 1352, 1458, 1568, 1682, 1800, 1922,
2048, 2178, 2312, 2450, 2592, 2738, 2888, 3042, 3200, 3362,
3528, 3698, 3872, 4050, 4232, 4418, 4608, 4802, 5000, 5202,
5408, 5618, 5832, 6050, 6272, 6498, 6728, 6962, 7200, 7442,
7688, 7938, 8192, 8450, 8712, 8978, 9248, 9522, 9800, 10082,
10368, 10658, 10952, 11250, 11552, 11858, 12168, 12482, 12800, 13122,
13448, 13778, 14112, 14450, 14792, 15138, 15488, 15842, 16200, 16562,
16928, 17298, 17672, 18050, 18432, 18818, 19208, 19602, 20000, 20402,
20808, 21218, 21632, 22050, 22472, 22898, 23328, 23762, 24200, 24642,
25088, 25538, 25992, 26450, 26912, 27378, 27848, 28322, 28800, 29282,
29768, 30258, 30752, 31250, 31752, 32258
};
Audio3DO_SDX2_Stream::Audio3DO_SDX2_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpace)
: _sampleRate(sampleRate), _stereo(stereo),
_stream(stream, disposeAfterUse) {
_callerDecoderData = persistentSpace;
memset(&_initialDecoderData, 0, sizeof(_initialDecoderData));
_initialRead = true;
reset();
}
void Audio3DO_SDX2_Stream::reset() {
memcpy(&_curDecoderData, &_initialDecoderData, sizeof(_curDecoderData));
_streamBytesLeft = _stream->size();
_stream->seek(0);
}
bool Audio3DO_SDX2_Stream::rewind() {
reset();
return true;
}
// Writes the requested amount (or less) of samples into buffer and returns the amount of samples, that got written
int Audio3DO_SDX2_Stream::readBuffer(int16 *buffer, const int numSamples) {
int8 byteCache[AUDIO_3DO_CACHE_SIZE];
int8 *byteCachePtr = NULL;
int byteCacheSize = 0;
int requestedBytesLeft = numSamples; // 1 byte per 16-bit sample
int decodedSamplesCount = 0;
int8 compressedByte = 0;
uint8 squareTableOffset = 0;
int16 decodedSample = 0;
if (endOfData())
return 0; // no more bytes left
if (_stereo) {
// We expect numSamples to be even in case of Stereo audio
assert((numSamples & 1) == 0);
}
if (_callerDecoderData) {
// copy caller decoder data over
memcpy(&_curDecoderData, _callerDecoderData, sizeof(_curDecoderData));
if (_initialRead) {
_initialRead = false;
memcpy(&_initialDecoderData, &_curDecoderData, sizeof(_initialDecoderData));
}
}
requestedBytesLeft = numSamples;
if (requestedBytesLeft > _streamBytesLeft)
requestedBytesLeft = _streamBytesLeft; // not enough bytes left
// buffering, so that direct decoding of files and such runs way faster
while (requestedBytesLeft) {
if (requestedBytesLeft > AUDIO_3DO_CACHE_SIZE) {
byteCacheSize = AUDIO_3DO_CACHE_SIZE;
} else {
byteCacheSize = requestedBytesLeft;
}
requestedBytesLeft -= byteCacheSize;
_streamBytesLeft -= byteCacheSize;
// Fill our byte cache
_stream->read(byteCache, byteCacheSize);
byteCachePtr = byteCache;
if (!_stereo) {
// Mono
while (byteCacheSize) {
compressedByte = *byteCachePtr++;
byteCacheSize--;
squareTableOffset = compressedByte + 128;
if (!(compressedByte & 1))
_curDecoderData.lastSample1 = 0;
decodedSample = _curDecoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset];
_curDecoderData.lastSample1 = decodedSample;
buffer[decodedSamplesCount] = decodedSample;
decodedSamplesCount++;
}
} else {
// Stereo
while (byteCacheSize) {
compressedByte = *byteCachePtr++;
byteCacheSize--;
squareTableOffset = compressedByte + 128;
if (!(decodedSamplesCount & 1)) {
// First channel
if (!(compressedByte & 1))
_curDecoderData.lastSample1 = 0;
decodedSample = _curDecoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset];
_curDecoderData.lastSample1 = decodedSample;
} else {
// Second channel
if (!(compressedByte & 1))
_curDecoderData.lastSample2 = 0;
decodedSample = _curDecoderData.lastSample2 + audio_3DO_SDX2_SquareTable[squareTableOffset];
_curDecoderData.lastSample2 = decodedSample;
}
buffer[decodedSamplesCount] = decodedSample;
decodedSamplesCount++;
}
}
}
if (_callerDecoderData) {
// copy caller decoder data back
memcpy(_callerDecoderData, &_curDecoderData, sizeof(_curDecoderData));
}
return decodedSamplesCount;
}
RewindableAudioStream *make3DO_SDX2AudioStream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, uint32 *audioLengthMSecsPtr, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpace) {
if (stereo) {
if (stream->size() & 1) {
warning("make3DO_SDX2Stream(): stereo data is uneven size");
return 0;
}
}
if (audioLengthMSecsPtr) {
// Caller requires the milliseconds of audio
uint32 audioLengthMSecs = stream->size() * 1000 / sampleRate; // 1 byte == 1 16-bit sample
if (stereo) {
audioLengthMSecs /= 2;
}
*audioLengthMSecsPtr = audioLengthMSecs;
}
return new Audio3DO_SDX2_Stream(stream, sampleRate, stereo, disposeAfterUse, persistentSpace);
}
} // End of namespace Audio

158
b/audio/decoders/3do.h Normal file
View file

@ -0,0 +1,158 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
/**
* @file
* Sound decoder used in engines:
* - sherlock (3DO version of Serrated Scalpel)
*/
#ifndef AUDIO_3DO_SDX2_H
#define AUDIO_3DO_SDX2_H
#include "common/scummsys.h"
#include "common/types.h"
#include "common/substream.h"
#include "audio/audiostream.h"
#include "audio/decoders/raw.h"
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class SeekableAudioStream;
// amount of bytes to be used within the decoder classes as buffers
#define AUDIO_3DO_CACHE_SIZE 1024
// persistent spaces
struct audio_3DO_ADP4_PersistentSpace {
int16 lastSample;
int16 stepIndex;
};
struct audio_3DO_SDX2_PersistentSpace {
int16 lastSample1;
int16 lastSample2;
};
class Audio3DO_ADP4_Stream : public RewindableAudioStream {
public:
Audio3DO_ADP4_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace);
protected:
const uint16 _sampleRate;
const bool _stereo;
Common::DisposablePtr<Common::SeekableReadStream> _stream;
int32 _streamBytesLeft;
void reset();
bool rewind();
bool endOfData() const { return (_stream->pos() >= _stream->size()); }
bool isStereo() const { return _stereo; }
int getRate() const { return _sampleRate; }
int readBuffer(int16 *buffer, const int numSamples);
bool _initialRead;
audio_3DO_ADP4_PersistentSpace *_callerDecoderData;
audio_3DO_ADP4_PersistentSpace _initialDecoderData;
audio_3DO_ADP4_PersistentSpace _curDecoderData;
private:
int16 decodeSample(byte compressedNibble);
};
class Audio3DO_SDX2_Stream : public RewindableAudioStream {
public:
Audio3DO_SDX2_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpacePtr);
protected:
const uint16 _sampleRate;
const bool _stereo;
Common::DisposablePtr<Common::SeekableReadStream> _stream;
int32 _streamBytesLeft;
void reset();
bool rewind();
bool endOfData() const { return (_stream->pos() >= _stream->size()); }
bool isStereo() const { return _stereo; }
int getRate() const { return _sampleRate; }
int readBuffer(int16 *buffer, const int numSamples);
bool _initialRead;
audio_3DO_SDX2_PersistentSpace *_callerDecoderData;
audio_3DO_SDX2_PersistentSpace _initialDecoderData;
audio_3DO_SDX2_PersistentSpace _curDecoderData;
};
/**
* Try to decode 3DO ADP4 data from the given seekable stream and create a SeekableAudioStream
* from that data.
*
* @param stream the SeekableReadStream from which to read the 3DO SDX2 data
* @sampleRate sample rate
* @stereo if it's stereo or mono
* @audioLengthMSecsPtr pointer to a uint32 variable, that is supposed to get the length of the audio in milliseconds
* @disposeAfterUse disposeAfterUse whether to delete the stream after use
* @persistentSpacePtr pointer to the persistent space structure
* @return a new SeekableAudioStream, or NULL, if an error occurred
*/
RewindableAudioStream *make3DO_ADP4AudioStream(
Common::SeekableReadStream *stream,
uint16 sampleRate,
bool stereo,
uint32 *audioLengthMSecsPtr = NULL, // returns the audio length in milliseconds
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES,
audio_3DO_ADP4_PersistentSpace *persistentSpacePtr = NULL
);
/**
* Try to decode 3DO SDX2 data from the given seekable stream and create a SeekableAudioStream
* from that data.
*
* @param stream the SeekableReadStream from which to read the 3DO SDX2 data
* @sampleRate sample rate
* @stereo if it's stereo or mono
* @audioLengthMSecsPtr pointer to a uint32 variable, that is supposed to get the length of the audio in milliseconds
* @disposeAfterUse disposeAfterUse whether to delete the stream after use
* @persistentSpacePtr pointer to the persistent space structure
* @return a new SeekableAudioStream, or NULL, if an error occurred
*/
RewindableAudioStream *make3DO_SDX2AudioStream(
Common::SeekableReadStream *stream,
uint16 sampleRate,
bool stereo,
uint32 *audioLengthMSecsPtr = NULL, // returns the audio length in milliseconds
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES,
audio_3DO_SDX2_PersistentSpace *persistentSpacePtr = NULL
);
} // End of namespace Audio
#endif

View file

@ -30,17 +30,15 @@ namespace Common {
class DecompressorDCL {
public:
bool unpack(ReadStream *src, byte *dest, uint32 nPacked, uint32 nUnpacked);
bool unpack(SeekableReadStream *sourceStream, WriteStream *targetStream, uint32 targetSize, bool targetFixedSize);
protected:
/**
* Initialize decompressor.
* @param src source stream to read from
* @param dest destination stream to write to
* @param nPacked size of packed data
* @param nUnpacked size of unpacked data
* @param sourceStream source stream to read from
* @param targetStream target memory stream to write to
*/
void init(ReadStream *src, byte *dest, uint32 nPacked, uint32 nUnpacked);
void init(SeekableReadStream *sourceStream, WriteStream *targetStream, uint32 targetSize, bool targetFixedSize);
/**
* Get a number of bits from _src stream, starting with the least
@ -68,34 +66,36 @@ protected:
uint32 _dwBits; ///< bits buffer
byte _nBits; ///< number of unread bits in _dwBits
uint32 _szPacked; ///< size of the compressed data
uint32 _szUnpacked; ///< size of the decompressed data
uint32 _dwRead; ///< number of bytes read from _src
uint32 _dwWrote; ///< number of bytes written to _dest
ReadStream *_src;
byte *_dest;
uint32 _sourceSize; ///< size of the source stream
uint32 _targetSize; ///< size of the target stream (if fixed)
bool _targetFixedSize; ///< if target stream is fixed size or dynamic size
uint32 _bytesRead; ///< number of bytes read from _sourceStream
uint32 _bytesWritten; ///< number of bytes written to _targetStream
SeekableReadStream *_sourceStream;
WriteStream *_targetStream;
};
void DecompressorDCL::init(ReadStream *src, byte *dest, uint32 nPacked, uint32 nUnpacked) {
_src = src;
_dest = dest;
_szPacked = nPacked;
_szUnpacked = nUnpacked;
void DecompressorDCL::init(SeekableReadStream *sourceStream, WriteStream *targetStream, uint32 targetSize, bool targetFixedSize) {
_sourceStream = sourceStream;
_targetStream = targetStream;
_sourceSize = sourceStream->size();
_targetSize = targetSize;
_targetFixedSize = targetFixedSize;
_nBits = 0;
_dwRead = _dwWrote = 0;
_bytesRead = _bytesWritten = 0;
_dwBits = 0;
}
void DecompressorDCL::fetchBitsLSB() {
while (_nBits <= 24) {
_dwBits |= ((uint32)_src->readByte()) << _nBits;
_dwBits |= ((uint32)_sourceStream->readByte()) << _nBits;
_nBits += 8;
_dwRead++;
_bytesRead++;
}
}
uint32 DecompressorDCL::getBitsLSB(int n) {
// fetching more data to buffer if needed
// Fetching more data to buffer if needed
if (_nBits < n)
fetchBitsLSB();
uint32 ret = (_dwBits & ~((~0) << n));
@ -109,7 +109,8 @@ byte DecompressorDCL::getByteLSB() {
}
void DecompressorDCL::putByte(byte b) {
_dest[_dwWrote++] = b;
_targetStream->writeByte(b);
_bytesWritten++;
}
#define HUFFMAN_LEAF 0x40000000
@ -331,97 +332,189 @@ int DecompressorDCL::huffman_lookup(const int *tree) {
#define DCL_BINARY_MODE 0
#define DCL_ASCII_MODE 1
bool DecompressorDCL::unpack(ReadStream *src, byte *dest, uint32 nPacked, uint32 nUnpacked) {
init(src, dest, nPacked, nUnpacked);
#define MIDI_SETUP_BUNDLE_FILE_MAXIMUM_DICTIONARY_SIZE 4096
bool DecompressorDCL::unpack(SeekableReadStream *sourceStream, WriteStream *targetStream, uint32 targetSize, bool targetFixedSize) {
byte dictionary[MIDI_SETUP_BUNDLE_FILE_MAXIMUM_DICTIONARY_SIZE];
uint16 dictionaryPos = 0;
uint16 dictionarySize = 0;
uint16 dictionaryMask = 0;
int value;
uint32 val_distance, val_length;
uint16 tokenOffset = 0;
uint16 tokenLength = 0;
int mode = getByteLSB();
int length_param = getByteLSB();
init(sourceStream, targetStream, targetSize, targetFixedSize);
byte mode = getByteLSB();
byte dictionaryType = getByteLSB();
if (mode != DCL_BINARY_MODE && mode != DCL_ASCII_MODE) {
warning("DCL-INFLATE: Error: Encountered mode %02x, expected 00 or 01", mode);
return false;
}
if (length_param < 3 || length_param > 6)
warning("Unexpected length_param value %d (expected in [3,6])", length_param);
// TODO: original code supported 3 as well???
// Was this an accident or on purpose? And the original code did just give out a warning
// and didn't error out at all
switch (dictionaryType) {
case 4:
dictionarySize = 1024;
break;
case 5:
dictionarySize = 2048;
break;
case 6:
dictionarySize = 4096;
break;
default:
warning("DCL-INFLATE: Error: unsupported dictionary type %02x", dictionaryType);
return false;
}
dictionaryMask = dictionarySize - 1;
while (_dwWrote < _szUnpacked) {
while ((!targetFixedSize) || (_bytesWritten < _targetSize)) {
if (getBitsLSB(1)) { // (length,distance) pair
value = huffman_lookup(length_tree);
if (value < 8)
val_length = value + 2;
tokenLength = value + 2;
else
val_length = 8 + (1 << (value - 7)) + getBitsLSB(value - 7);
tokenLength = 8 + (1 << (value - 7)) + getBitsLSB(value - 7);
if (tokenLength == 519)
break; // End of stream signal
debug(8, " | ");
value = huffman_lookup(distance_tree);
if (val_length == 2)
val_distance = (value << 2) | getBitsLSB(2);
if (tokenLength == 2)
tokenOffset = (value << 2) | getBitsLSB(2);
else
val_distance = (value << length_param) | getBitsLSB(length_param);
val_distance ++;
tokenOffset = (value << dictionaryType) | getBitsLSB(dictionaryType);
tokenOffset++;
debug(8, "\nCOPY(%d from %d)\n", val_length, val_distance);
debug(8, "\nCOPY(%d from %d)\n", tokenLength, tokenOffset);
if (val_length + _dwWrote > _szUnpacked) {
if (_targetFixedSize) {
if (tokenLength + _bytesWritten > _targetSize) {
warning("DCL-INFLATE Error: Write out of bounds while copying %d bytes (declared unpacked size is %d bytes, current is %d + %d bytes)",
val_length, _szUnpacked, _dwWrote, val_length);
tokenLength, _targetSize, _bytesWritten, tokenLength);
return false;
}
}
if (_dwWrote < val_distance) {
if (_bytesWritten < tokenOffset) {
warning("DCL-INFLATE Error: Attempt to copy from before beginning of input stream (declared unpacked size is %d bytes, current is %d bytes)",
_szUnpacked, _dwWrote);
_targetSize, _bytesWritten);
return false;
}
while (val_length) {
uint32 copy_length = (val_length > val_distance) ? val_distance : val_length;
assert(val_distance >= copy_length);
uint32 pos = _dwWrote - val_distance;
for (uint32 i = 0; i < copy_length; i++)
putByte(dest[pos + i]);
uint16 dictionaryBaseIndex = (dictionaryPos - tokenOffset) & dictionaryMask;
uint16 dictionaryIndex = dictionaryBaseIndex;
uint16 dictionaryNextIndex = dictionaryPos;
for (uint32 i = 0; i < copy_length; i++)
debug(9, "\33[32;31m%02x\33[37;37m ", dest[pos + i]);
debug(9, "\n");
while (tokenLength) {
// Write byte from dictionary
putByte(dictionary[dictionaryIndex]);
debug(9, "\33[32;31m%02x\33[37;37m ", dictionary[dictionaryIndex]);
val_length -= copy_length;
val_distance += copy_length;
dictionary[dictionaryNextIndex] = dictionary[dictionaryIndex];
dictionaryNextIndex = (dictionaryNextIndex + 1) & dictionaryMask;
dictionaryIndex = (dictionaryIndex + 1) & dictionaryMask;
if (dictionaryIndex == dictionaryPos)
dictionaryIndex = dictionaryBaseIndex;
if (dictionaryNextIndex == dictionarySize)
dictionaryNextIndex = 0;
tokenLength--;
}
dictionaryPos = dictionaryNextIndex;
debug(9, "\n");
} else { // Copy byte verbatim
value = (mode == DCL_ASCII_MODE) ? huffman_lookup(ascii_tree) : getByteLSB();
putByte(value);
// Also remember it inside dictionary
dictionary[dictionaryPos] = value;
dictionaryPos++;
if (dictionaryPos >= dictionarySize)
dictionaryPos = 0;
debug(9, "\33[32;31m%02x \33[37;37m", value);
}
}
return _dwWrote == _szUnpacked;
if (_targetFixedSize) {
return _bytesWritten == _targetSize;
}
return true; // For targets featuring dynamic size we always succeed
}
bool decompressDCL(ReadStream *src, byte *dest, uint32 packedSize, uint32 unpackedSize) {
bool success = false;
DecompressorDCL dcl;
if (!src || !dest)
return false;
DecompressorDCL dcl;
return dcl.unpack(src, dest, packedSize, unpackedSize);
byte *sourceBufferPtr = (byte *)malloc(packedSize);
if (!sourceBufferPtr)
return false;
// Read source into memory
src->read(sourceBufferPtr, packedSize);
Common::MemoryReadStream *sourceStream = new MemoryReadStream(sourceBufferPtr, packedSize, DisposeAfterUse::NO);
Common::MemoryWriteStream *targetStream = new MemoryWriteStream(dest, unpackedSize);
success = dcl.unpack(sourceStream, targetStream, unpackedSize, true);
delete sourceStream;
delete targetStream;
return success;
}
SeekableReadStream *decompressDCL(ReadStream *src, uint32 packedSize, uint32 unpackedSize) {
byte *data = (byte *)malloc(unpackedSize);
SeekableReadStream *decompressDCL(SeekableReadStream *sourceStream, uint32 packedSize, uint32 unpackedSize) {
bool success = false;
byte *targetPtr = nullptr;
Common::MemoryWriteStream *targetStream;
DecompressorDCL dcl;
if (decompressDCL(src, data, packedSize, unpackedSize))
return new MemoryReadStream(data, unpackedSize, DisposeAfterUse::YES);
targetPtr = (byte *)malloc(unpackedSize);
if (!targetPtr)
return nullptr;
free(data);
return 0;
targetStream = new MemoryWriteStream(targetPtr, unpackedSize);
success = dcl.unpack(sourceStream, targetStream, unpackedSize, true);
delete targetStream;
if (!success) {
free(targetPtr);
return nullptr;
}
return new MemoryReadStream(targetPtr, unpackedSize, DisposeAfterUse::YES);
}
// This one figures out the unpacked size by itself
// Needed for at least Simon 2, because the unpacked size is not stored anywhere
SeekableReadStream *decompressDCL(SeekableReadStream *sourceStream) {
Common::MemoryWriteStreamDynamic *targetStream;
DecompressorDCL dcl;
targetStream = new MemoryWriteStreamDynamic(DisposeAfterUse::NO);
if (dcl.unpack(sourceStream, targetStream, 0, false)) {
byte *targetPtr = targetStream->getData();
uint32 unpackedSize = targetStream->size();
delete targetStream;
return new MemoryReadStream(targetPtr, unpackedSize, DisposeAfterUse::YES);
}
delete targetStream;
return nullptr;
}
} // End of namespace Common

View file

@ -22,7 +22,8 @@
/**
* @file
* PKWARE DCL ("explode") decompressor used in engines:
* PKWARE DCL ("explode") ("PKWARE data compression library") decompressor used in engines:
* - agos (exclusively for Simon 2 setup.shr file)
* - mohawk
* - sci
*/
@ -38,16 +39,22 @@ class ReadStream;
class SeekableReadStream;
/**
* Try to decompress a PKWARE DCL compressed stream. Returns true if
* Try to decompress a PKWARE DCL (PKWARE data compression library) compressed stream. Returns true if
* successful.
*/
bool decompressDCL(ReadStream *src, byte *dest, uint32 packedSize, uint32 unpackedSize);
bool decompressDCL(ReadStream *sourceStream, byte *dest, uint32 packedSize, uint32 unpackedSize);
/**
* Try to decompress a PKWARE DCL compressed stream. Returns a valid pointer
* Try to decompress a PKWARE DCL (PKWARE data compression library) compressed stream. Returns a valid pointer
* if successful and 0 otherwise.
*/
SeekableReadStream *decompressDCL(ReadStream *src, uint32 packedSize, uint32 unpackedSize);
SeekableReadStream *decompressDCL(SeekableReadStream *sourceStream, uint32 packedSize, uint32 unpackedSize);
/**
* Try to decompress a PKWARE DCL (PKWARE data compression library) compressed stream. Returns a valid pointer
* if successful and 0 otherwise. This method is meant for cases, where the unpacked size is not known.
*/
SeekableReadStream *decompressDCL(SeekableReadStream *sourceStream);
} // End of namespace Common

View file

@ -56,6 +56,10 @@ FFT::FFT(int bits, int inverse) : _bits(bits), _inverse(inverse) {
}
FFT::~FFT() {
for (int i = 0; i < ARRAYSIZE(_cosTables); i++) {
delete _cosTables[i];
}
delete[] _revTab;
delete[] _expTab;
delete[] _tmpBuf;

927
configure vendored

File diff suppressed because it is too large Load diff

View file

@ -20,7 +20,7 @@
*
*/
//#define ENABLE_XCODE
#define ENABLE_XCODE
// HACK to allow building with the SDL backend on MinGW
// see bug #1800764 "TOOLS: MinGW tools building broken"
@ -336,7 +336,12 @@ int main(int argc, char *argv[]) {
setup.defines.splice(setup.defines.begin(), featureDefines);
// Windows only has support for the SDL backend, so we hardcode it here (along with winmm)
if (projectType != kProjectXcode) {
setup.defines.push_back("WIN32");
} else {
setup.defines.push_back("POSIX");
setup.defines.push_back("MACOSX"); // This will break iOS, but allows OS X to catch up on browser_osx.
}
setup.defines.push_back("SDL_BACKEND");
if (!useSDL2) {
cout << "\nLinking to SDL 1.2\n\n";
@ -410,7 +415,6 @@ int main(int argc, char *argv[]) {
globalWarnings.push_back("-Wwrite-strings");
// The following are not warnings at all... We should consider adding them to
// a different list of parameters.
globalWarnings.push_back("-fno-rtti");
globalWarnings.push_back("-fno-exceptions");
globalWarnings.push_back("-fcheck-new");
@ -572,7 +576,7 @@ int main(int argc, char *argv[]) {
globalWarnings.push_back("-fno-exceptions");
globalWarnings.push_back("-fcheck-new");
provider = new CreateProjectTool::XCodeProvider(globalWarnings, projectWarnings);
provider = new CreateProjectTool::XcodeProvider(globalWarnings, projectWarnings);
break;
}
@ -1039,7 +1043,7 @@ bool producesObjectFile(const std::string &fileName) {
std::string n, ext;
splitFilename(fileName, n, ext);
if (ext == "cpp" || ext == "c" || ext == "asm")
if (ext == "cpp" || ext == "c" || ext == "asm" || ext == "m" || ext == "mm")
return true;
else
return false;
@ -1279,8 +1283,9 @@ void ProjectProvider::createProject(BuildSetup &setup) {
for (UUIDMap::const_iterator i = _uuidMap.begin(); i != _uuidMap.end(); ++i) {
if (i->first == setup.projectName)
continue;
// Retain the files between engines if we're creating a single project
in.clear(); ex.clear();
const std::string moduleDir = setup.srcDir + targetFolder + i->first;
createModuleList(moduleDir, setup.defines, setup.testDirs, in, ex);
@ -1290,7 +1295,6 @@ void ProjectProvider::createProject(BuildSetup &setup) {
if (setup.tests) {
// Create the main project file.
in.clear(); ex.clear();
createModuleList(setup.srcDir + "/backends", setup.defines, setup.testDirs, in, ex);
createModuleList(setup.srcDir + "/backends/platform/sdl", setup.defines, setup.testDirs, in, ex);
createModuleList(setup.srcDir + "/base", setup.defines, setup.testDirs, in, ex);
@ -1305,7 +1309,6 @@ void ProjectProvider::createProject(BuildSetup &setup) {
} else if (!setup.devTools) {
// Last but not least create the main project file.
in.clear(); ex.clear();
// File list for the Project file
createModuleList(setup.srcDir + "/backends", setup.defines, setup.testDirs, in, ex);
createModuleList(setup.srcDir + "/backends/platform/sdl", setup.defines, setup.testDirs, in, ex);

View file

@ -30,6 +30,14 @@ namespace CreateProjectTool {
#define DEBUG_XCODE_HASH 0
#ifdef ENABLE_IOS
#define IOS_TARGET 0
#define OSX_TARGET 1
#define SIM_TARGET 2
#else
#define OSX_TARGET 0
#endif
#define ADD_DEFINE(defines, name) \
defines.push_back(name);
@ -54,39 +62,172 @@ namespace CreateProjectTool {
#define REMOVE_SETTING(config, key) \
config.settings.erase(key);
#define ADD_BUILD_FILE(id, name, comment) { \
#define ADD_BUILD_FILE(id, name, fileRefId, comment) { \
Object *buildFile = new Object(this, id, name, "PBXBuildFile", "PBXBuildFile", comment); \
buildFile->addProperty("fileRef", getHash(name), name, SettingsNoValue); \
buildFile->addProperty("fileRef", fileRefId, name, SettingsNoValue); \
_buildFile.add(buildFile); \
_buildFile.flags = SettingsSingleItem; \
}
#define ADD_FILE_REFERENCE(name, properties) { \
Object *fileRef = new Object(this, name, name, "PBXFileReference", "PBXFileReference", name); \
#define ADD_FILE_REFERENCE(id, name, properties) { \
Object *fileRef = new Object(this, id, name, "PBXFileReference", "PBXFileReference", name); \
if (!properties.fileEncoding.empty()) fileRef->addProperty("fileEncoding", properties.fileEncoding, "", SettingsNoValue); \
if (!properties.lastKnownFileType.empty()) fileRef->addProperty("lastKnownFileType", properties.lastKnownFileType, "", SettingsNoValue); \
if (!properties.fileName.empty()) fileRef->addProperty("name", properties.fileName, "", SettingsNoValue); \
if (!properties.filePath.empty()) fileRef->addProperty("path", properties.filePath, "", SettingsNoValue); \
if (!properties.lastKnownFileType.empty()) fileRef->addProperty("lastKnownFileType", properties.lastKnownFileType, "", SettingsNoValue|SettingsQuoteVariable); \
if (!properties.fileName.empty()) fileRef->addProperty("name", properties.fileName, "", SettingsNoValue|SettingsQuoteVariable); \
if (!properties.filePath.empty()) fileRef->addProperty("path", properties.filePath, "", SettingsNoValue|SettingsQuoteVariable); \
if (!properties.sourceTree.empty()) fileRef->addProperty("sourceTree", properties.sourceTree, "", SettingsNoValue); \
_fileReference.add(fileRef); \
_fileReference.flags = SettingsSingleItem; \
}
XCodeProvider::XCodeProvider(StringList &global_warnings, std::map<std::string, StringList> &project_warnings, const int version)
: ProjectProvider(global_warnings, project_warnings, version) {
bool producesObjectFileOnOSX(const std::string &fileName) {
std::string n, ext;
splitFilename(fileName, n, ext);
// Note that the difference between this and the general producesObjectFile is that
// this one adds Objective-C(++), and removes asm-support.
if (ext == "cpp" || ext == "c" || ext == "m" || ext == "mm")
return true;
else
return false;
}
void XCodeProvider::createWorkspace(const BuildSetup &setup) {
XcodeProvider::Group::Group(XcodeProvider *objectParent, const std::string &groupName, const std::string &uniqueName, const std::string &path) : Object(objectParent, uniqueName, groupName, "PBXGroup", "", groupName) {
addProperty("name", name, "", SettingsNoValue|SettingsQuoteVariable);
addProperty("sourceTree", "<group>", "", SettingsNoValue|SettingsQuoteVariable);
if (path != "") {
addProperty("path", path, "", SettingsNoValue|SettingsQuoteVariable);
}
_childOrder = 0;
_treeName = uniqueName;
}
void XcodeProvider::Group::ensureChildExists(const std::string &name) {
std::map<std::string, Group*>::iterator it = _childGroups.find(name);
if (it == _childGroups.end()) {
Group *child = new Group(parent, name, this->_treeName + '/' + name, name);
_childGroups[name] = child;
addChildGroup(child);
parent->_groups.add(child);
}
}
void XcodeProvider::Group::addChildInternal(const std::string &id, const std::string &comment) {
if (properties.find("children") == properties.end()) {
Property children;
children.hasOrder = true;
children.flags = SettingsAsList;
properties["children"] = children;
}
properties["children"].settings[id] = Setting("", comment + " in Sources", SettingsNoValue, 0, _childOrder++);
if (_childOrder == 1) {
// Force children to use () even when there is only 1 child.
// Also this enforces the use of "," after the single item, instead of ; (see writeProperty)
properties["children"].flags |= SettingsSingleItem;
} else {
properties["children"].flags ^= SettingsSingleItem;
}
}
void XcodeProvider::Group::addChildGroup(const Group* group) {
addChildInternal(parent->getHash(group->_treeName), group->_treeName);
}
void XcodeProvider::Group::addChildFile(const std::string &name) {
std::string id = "FileReference_" + _treeName + "/" + name;
addChildInternal(parent->getHash(id), name);
FileProperty property = FileProperty(name, name, name, "\"<group>\"");
parent->addFileReference(id, name, property);
if (producesObjectFileOnOSX(name)) {
parent->addBuildFile(_treeName + "/" + name, name, parent->getHash(id), name + " in Sources");
}
}
void XcodeProvider::Group::addChildByHash(const std::string &hash, const std::string &name) {
addChildInternal(hash, name);
}
XcodeProvider::Group *XcodeProvider::Group::getChildGroup(const std::string &name) {
std::map<std::string, Group*>::iterator it = _childGroups.find(name);
assert(it != _childGroups.end());
return it->second;
}
XcodeProvider::Group *XcodeProvider::touchGroupsForPath(const std::string &path) {
if (_rootSourceGroup == NULL) {
assert (path == _projectRoot);
_rootSourceGroup = new Group(this, "Sources", path, path);
_groups.add(_rootSourceGroup);
return _rootSourceGroup;
} else {
assert(path.find(_projectRoot) == 0);
std::string subPath = path.substr(_projectRoot.size() + 1);
Group *currentGroup = _rootSourceGroup;
size_t firstPathComponent = subPath.find_first_of('/');
// We assume here that all paths have trailing '/', otherwise this breaks.
while (firstPathComponent != std::string::npos) {
currentGroup->ensureChildExists(subPath.substr(0, firstPathComponent));
currentGroup = currentGroup->getChildGroup(subPath.substr(0, firstPathComponent));
subPath = subPath.substr(firstPathComponent + 1);
firstPathComponent = subPath.find_first_of('/');
}
return currentGroup;
}
}
void XcodeProvider::addFileReference(const std::string &id, const std::string &name, FileProperty properties) {
Object *fileRef = new Object(this, id, name, "PBXFileReference", "PBXFileReference", name);
if (!properties.fileEncoding.empty()) fileRef->addProperty("fileEncoding", properties.fileEncoding, "", SettingsNoValue);
if (!properties.lastKnownFileType.empty()) fileRef->addProperty("lastKnownFileType", properties.lastKnownFileType, "", SettingsNoValue|SettingsQuoteVariable);
if (!properties.fileName.empty()) fileRef->addProperty("name", properties.fileName, "", SettingsNoValue|SettingsQuoteVariable);
if (!properties.filePath.empty()) fileRef->addProperty("path", properties.filePath, "", SettingsNoValue|SettingsQuoteVariable);
if (!properties.sourceTree.empty()) fileRef->addProperty("sourceTree", properties.sourceTree, "", SettingsNoValue);
_fileReference.add(fileRef);
_fileReference.flags = SettingsSingleItem;
}
void XcodeProvider::addProductFileReference(const std::string &id, const std::string &name) {
Object *fileRef = new Object(this, id, name, "PBXFileReference", "PBXFileReference", name);
fileRef->addProperty("explicitFileType", "compiled.mach-o.executable", "", SettingsNoValue|SettingsQuoteVariable);
fileRef->addProperty("includeInIndex", "0", "", SettingsNoValue);
fileRef->addProperty("path", name, "", SettingsNoValue|SettingsQuoteVariable);
fileRef->addProperty("sourceTree", "BUILT_PRODUCTS_DIR", "", SettingsNoValue);
_fileReference.add(fileRef);
_fileReference.flags = SettingsSingleItem;
}
void XcodeProvider::addBuildFile(const std::string &id, const std::string &name, const std::string &fileRefId, const std::string &comment) {
Object *buildFile = new Object(this, id, name, "PBXBuildFile", "PBXBuildFile", comment);
buildFile->addProperty("fileRef", fileRefId, name, SettingsNoValue);
_buildFile.add(buildFile);
_buildFile.flags = SettingsSingleItem;
}
XcodeProvider::XcodeProvider(StringList &global_warnings, std::map<std::string, StringList> &project_warnings, const int version)
: ProjectProvider(global_warnings, project_warnings, version) {
_rootSourceGroup = NULL;
}
void XcodeProvider::createWorkspace(const BuildSetup &setup) {
// Create project folder
std::string workspace = setup.outputDir + '/' + PROJECT_NAME ".xcodeproj";
createDirectory(workspace);
_projectRoot = setup.srcDir;
touchGroupsForPath(_projectRoot);
// Setup global objects
setupDefines(setup);
#ifdef ENABLE_IOS
_targets.push_back(PROJECT_DESCRIPTION "-iPhone");
#endif
_targets.push_back(PROJECT_DESCRIPTION "-OS X");
#ifdef ENABLE_IOS
_targets.push_back(PROJECT_DESCRIPTION "-Simulator");
#endif
setupCopyFilesBuildPhase();
setupFrameworksBuildPhase();
setupNativeTarget();
@ -97,7 +238,7 @@ void XCodeProvider::createWorkspace(const BuildSetup &setup) {
// We are done with constructing all the object graph and we got through every project, output the main project file
// (this is kind of a hack since other providers use separate project files)
void XCodeProvider::createOtherBuildFiles(const BuildSetup &setup) {
void XcodeProvider::createOtherBuildFiles(const BuildSetup &setup) {
// This needs to be done at the end when all build files have been accounted for
setupSourcesBuildPhase();
@ -105,7 +246,7 @@ void XCodeProvider::createOtherBuildFiles(const BuildSetup &setup) {
}
// Store information about a project here, for use at the end
void XCodeProvider::createProjectFile(const std::string &, const std::string &, const BuildSetup &setup, const std::string &moduleDir,
void XcodeProvider::createProjectFile(const std::string &, const std::string &, const BuildSetup &setup, const std::string &moduleDir,
const StringList &includeList, const StringList &excludeList) {
std::string modulePath;
if (!moduleDir.compare(0, setup.srcDir.size(), setup.srcDir)) {
@ -124,7 +265,7 @@ void XCodeProvider::createProjectFile(const std::string &, const std::string &,
//////////////////////////////////////////////////////////////////////////
// Main Project file
//////////////////////////////////////////////////////////////////////////
void XCodeProvider::ouputMainProjectFile(const BuildSetup &setup) {
void XcodeProvider::ouputMainProjectFile(const BuildSetup &setup) {
std::ofstream project((setup.outputDir + '/' + PROJECT_NAME ".xcodeproj" + '/' + "project.pbxproj").c_str());
if (!project)
error("Could not open \"" + setup.outputDir + '/' + PROJECT_NAME ".xcodeproj" + '/' + "project.pbxproj\" for writing");
@ -164,92 +305,93 @@ void XCodeProvider::ouputMainProjectFile(const BuildSetup &setup) {
//////////////////////////////////////////////////////////////////////////
// Files
//////////////////////////////////////////////////////////////////////////
void XCodeProvider::writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation,
void XcodeProvider::writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation,
const StringList &duplicate, const std::string &objPrefix, const std::string &filePrefix) {
// Add comments for shared lists
_buildFile.comment = "PBXBuildFile";
_fileReference.comment = "PBXFileReference";
// Init root group
_groups.comment = "PBXGroup";
// Create group
std::string name = getLastPathComponent(dir.name);
Object *group = new Object(this, "PBXGroup_" + name , "PBXGroup", "PBXGroup", "", name);
// List of children
Property children;
children.hasOrder = true;
children.flags = SettingsAsList;
group->addProperty("name", name, "", SettingsNoValue|SettingsQuoteVariable);
group->addProperty("sourceTree", "<group>", "", SettingsNoValue|SettingsQuoteVariable);
int order = 0;
// Ensure that top-level groups are generated for i.e. engines/
Group *group = touchGroupsForPath(filePrefix);
for (FileNode::NodeList::const_iterator i = dir.children.begin(); i != dir.children.end(); ++i) {
const FileNode *node = *i;
std::string id = "FileReference_" + node->name;
FileProperty property = FileProperty(node->name, node->name, node->name, "<group>");
ADD_SETTING_ORDER_NOVALUE(children, getHash(id), node->name, order++);
ADD_BUILD_FILE(id, node->name, node->name + " in Sources");
ADD_FILE_REFERENCE(node->name, property);
// Iff it is a file, then add (build) file references. Since we're using Groups and not File References
// for folders, we shouldn't add folders as file references, obviously.
if (node->children.empty()) {
group->addChildFile(node->name);
}
// Process child nodes
if (!node->children.empty())
writeFileListToProject(*node, projectFile, indentation + 1, duplicate, objPrefix + node->name + '_', filePrefix + node->name + '/');
}
group->properties["children"] = children;
_groups.add(group);
}
//////////////////////////////////////////////////////////////////////////
// Setup functions
//////////////////////////////////////////////////////////////////////////
void XCodeProvider::setupCopyFilesBuildPhase() {
void XcodeProvider::setupCopyFilesBuildPhase() {
// Nothing to do here
}
#define DEF_SYSFRAMEWORK(framework) properties[framework".framework"] = FileProperty("wrapper.framework", framework".framework", "System/Library/Frameworks/" framework ".framework", "SDKROOT"); \
ADD_SETTING_ORDER_NOVALUE(children, getHash(framework".framework"), framework".framework", fwOrder++);
#define DEF_LOCALLIB_STATIC(lib) properties[lib".a"] = FileProperty("archive.ar", lib".a", "/opt/local/lib/" lib ".a", "\"<group>\""); \
ADD_SETTING_ORDER_NOVALUE(children, getHash(lib".a"), lib".a", fwOrder++);
/**
* Sets up the frameworks build phase.
*
* (each native target has different build rules)
*/
void XCodeProvider::setupFrameworksBuildPhase() {
void XcodeProvider::setupFrameworksBuildPhase() {
_frameworksBuildPhase.comment = "PBXFrameworksBuildPhase";
// Just use a hardcoded id for the Frameworks-group
Group *frameworksGroup = new Group(this, "Frameworks", "PBXGroup_CustomTemplate_Frameworks_", "");
Property children;
children.hasOrder = true;
children.flags = SettingsAsList;
// Setup framework file properties
std::map<std::string, FileProperty> properties;
int fwOrder = 0;
// Frameworks
properties["ApplicationServices.framework"] = FileProperty("wrapper.framework", "ApplicationServices.framework", "System/Library/Frameworks/ApplicationServices.framework", "SDKROOT");
properties["AudioToolbox.framework"] = FileProperty("wrapper.framework", "AudioToolbox.framework", "System/Library/Frameworks/AudioToolbox.framework", "SDKROOT");
properties["AudioUnit.framework"] = FileProperty("wrapper.framework", "AudioUnit.framework", "System/Library/Frameworks/AudioUnit.framework", "SDKROOT");
properties["Carbon.framework"] = FileProperty("wrapper.framework", "Carbon.framework", "System/Library/Frameworks/Carbon.framework", "SDKROOT");
properties["Cocoa.framework"] = FileProperty("wrapper.framework", "Cocoa.framework", "System/Library/Frameworks/Cocoa.framework", "SDKROOT");
properties["CoreAudio.framework"] = FileProperty("wrapper.framework", "CoreAudio.framework", "System/Library/Frameworks/CoreAudio.framework", "SDKROOT");
properties["CoreFoundation.framework"] = FileProperty("wrapper.framework", "CoreFoundation.framework", "System/Library/Frameworks/CoreFoundation.framework", "SDKROOT");
properties["CoreMIDI.framework"] = FileProperty("wrapper.framework", "CoreMIDI.framework", "System/Library/Frameworks/CoreMIDI.framework", "SDKROOT");
properties["Foundation.framework"] = FileProperty("wrapper.framework", "Foundation.framework", "System/Library/Frameworks/Foundation.framework", "SDKROOT");
properties["IOKit.framework"] = FileProperty("wrapper.framework", "IOKit.framework", "System/Library/Frameworks/IOKit.framework", "SDKROOT");
properties["OpenGLES.framework"] = FileProperty("wrapper.framework", "OpenGLES.framework", "System/Library/Frameworks/OpenGLES.framework", "SDKROOT");
properties["QuartzCore.framework"] = FileProperty("wrapper.framework", "QuartzCore.framework", "System/Library/Frameworks/QuartzCore.framework", "SDKROOT");
properties["QuickTime.framework"] = FileProperty("wrapper.framework", "QuickTime.framework", "System/Library/Frameworks/QuickTime.framework", "SDKROOT");
properties["UIKit.framework"] = FileProperty("wrapper.framework", "UIKit.framework", "System/Library/Frameworks/UIKit.framework", "SDKROOT");
DEF_SYSFRAMEWORK("ApplicationServices");
DEF_SYSFRAMEWORK("AudioToolbox");
DEF_SYSFRAMEWORK("AudioUnit");
DEF_SYSFRAMEWORK("Carbon");
DEF_SYSFRAMEWORK("Cocoa");
DEF_SYSFRAMEWORK("CoreAudio");
DEF_SYSFRAMEWORK("CoreFoundation");
DEF_SYSFRAMEWORK("CoreMIDI");
DEF_SYSFRAMEWORK("Foundation");
DEF_SYSFRAMEWORK("IOKit");
DEF_SYSFRAMEWORK("OpenGLES");
DEF_SYSFRAMEWORK("QuartzCore");
DEF_SYSFRAMEWORK("QuickTime");
DEF_SYSFRAMEWORK("UIKit");
// Optionals:
DEF_SYSFRAMEWORK("OpenGL");
// Local libraries
properties["libFLAC.a"] = FileProperty("archive.ar", "libFLAC.a", "lib/libFLAC.a", "\"<group>\"");
properties["libmad.a"] = FileProperty("archive.ar", "libmad.a", "lib/libmad.a", "\"<group>\"");
//properties["libmpeg2.a"] = FileProperty("archive.ar", "libmpeg2.a", "lib/libmpeg2.a", "\"<group>\"");
properties["libvorbisidec.a"] = FileProperty("archive.ar", "libvorbisidec.a", "lib/libvorbisidec.a", "\"<group>\"");
DEF_LOCALLIB_STATIC("libFLAC");
DEF_LOCALLIB_STATIC("libmad");
DEF_LOCALLIB_STATIC("libvorbisidec");
DEF_LOCALLIB_STATIC("libfreetype");
// DEF_LOCALLIB_STATIC("libmpeg2");
frameworksGroup->properties["children"] = children;
_groups.add(frameworksGroup);
// Force this to be added as a sub-group in the root.
_rootSourceGroup->addChildGroup(frameworksGroup);
// Declare this here, as it's used across the three targets
int order = 0;
#ifdef ENABLE_IOS
//////////////////////////////////////////////////////////////////////////
// iPhone
Object *framework_iPhone = new Object(this, "PBXFrameworksBuildPhase_" + _targets[0], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks");
Object *framework_iPhone = new Object(this, "PBXFrameworksBuildPhase_" + _targets[IOS_TARGET], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks");
framework_iPhone->addProperty("buildActionMask", "2147483647", "", SettingsNoValue);
framework_iPhone->addProperty("runOnlyForDeploymentPostprocessing", "0", "", SettingsNoValue);
@ -272,23 +414,22 @@ void XCodeProvider::setupFrameworksBuildPhase() {
frameworks_iPhone.push_back("libvorbisidec.a");
frameworks_iPhone.push_back("OpenGLES.framework");
int order = 0;
for (ValueList::iterator framework = frameworks_iPhone.begin(); framework != frameworks_iPhone.end(); framework++) {
std::string id = "Frameworks_" + *framework + "_iphone";
std::string comment = *framework + " in Frameworks";
ADD_SETTING_ORDER_NOVALUE(iPhone_files, getHash(id), comment, order++);
ADD_BUILD_FILE(id, *framework, comment);
ADD_FILE_REFERENCE(*framework, properties[*framework]);
ADD_BUILD_FILE(id, *framework, getHash(*framework), comment);
ADD_FILE_REFERENCE(*framework, *framework, properties[*framework]);
}
framework_iPhone->properties["files"] = iPhone_files;
_frameworksBuildPhase.add(framework_iPhone);
#endif
//////////////////////////////////////////////////////////////////////////
// ScummVM-OS X
Object *framework_OSX = new Object(this, "PBXFrameworksBuildPhase_" + _targets[1], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks");
Object *framework_OSX = new Object(this, "PBXFrameworksBuildPhase_" + _targets[OSX_TARGET], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks");
framework_OSX->addProperty("buildActionMask", "2147483647", "", SettingsNoValue);
framework_OSX->addProperty("runOnlyForDeploymentPostprocessing", "0", "", SettingsNoValue);
@ -311,6 +452,8 @@ void XCodeProvider::setupFrameworksBuildPhase() {
frameworks_osx.push_back("IOKit.framework");
frameworks_osx.push_back("Cocoa.framework");
frameworks_osx.push_back("AudioUnit.framework");
// Optionals:
frameworks_osx.push_back("OpenGL.framework");
order = 0;
for (ValueList::iterator framework = frameworks_osx.begin(); framework != frameworks_osx.end(); framework++) {
@ -318,17 +461,17 @@ void XCodeProvider::setupFrameworksBuildPhase() {
std::string comment = *framework + " in Frameworks";
ADD_SETTING_ORDER_NOVALUE(osx_files, getHash(id), comment, order++);
ADD_BUILD_FILE(id, *framework, comment);
ADD_FILE_REFERENCE(*framework, properties[*framework]);
ADD_BUILD_FILE(id, *framework, getHash(*framework), comment);
ADD_FILE_REFERENCE(*framework, *framework, properties[*framework]);
}
framework_OSX->properties["files"] = osx_files;
_frameworksBuildPhase.add(framework_OSX);
#ifdef ENABLE_IOS
//////////////////////////////////////////////////////////////////////////
// Simulator
Object *framework_simulator = new Object(this, "PBXFrameworksBuildPhase_" + _targets[2], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks");
Object *framework_simulator = new Object(this, "PBXFrameworksBuildPhase_" + _targets[SIM_TARGET], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks");
framework_simulator->addProperty("buildActionMask", "2147483647", "", SettingsNoValue);
framework_simulator->addProperty("runOnlyForDeploymentPostprocessing", "0", "", SettingsNoValue);
@ -353,20 +496,28 @@ void XCodeProvider::setupFrameworksBuildPhase() {
std::string comment = *framework + " in Frameworks";
ADD_SETTING_ORDER_NOVALUE(simulator_files, getHash(id), comment, order++);
ADD_BUILD_FILE(id, *framework, comment);
ADD_FILE_REFERENCE(*framework, properties[*framework]);
ADD_BUILD_FILE(id, *framework, getHash(*framework), comment);
ADD_FILE_REFERENCE(*framework, *framework, properties[*framework]);
}
framework_simulator->properties["files"] = simulator_files;
_frameworksBuildPhase.add(framework_simulator);
#endif
}
void XCodeProvider::setupNativeTarget() {
void XcodeProvider::setupNativeTarget() {
_nativeTarget.comment = "PBXNativeTarget";
// Just use a hardcoded id for the Products-group
Group *productsGroup = new Group(this, "Products", "PBXGroup_CustomTemplate_Products_" , "");
// Output native target section
for (unsigned int i = 0; i < _targets.size(); i++) {
#ifndef ENABLE_IOS
if (i != OSX_TARGET) { // TODO: Fix iOS-targets, for now just disable them.
continue;
}
#endif
Object *target = new Object(this, "PBXNativeTarget_" + _targets[i], "PBXNativeTarget", "PBXNativeTarget", "", _targets[i]);
target->addProperty("buildConfigurationList", getHash("XCConfigurationList_" + _targets[i]), "Build configuration list for PBXNativeTarget \"" + _targets[i] + "\"", SettingsNoValue);
@ -385,14 +536,18 @@ void XCodeProvider::setupNativeTarget() {
target->addProperty("name", _targets[i], "", SettingsNoValue|SettingsQuoteVariable);
target->addProperty("productName", PROJECT_NAME, "", SettingsNoValue);
addProductFileReference("PBXFileReference_" PROJECT_DESCRIPTION ".app_" + _targets[i], PROJECT_DESCRIPTION ".app");
productsGroup->addChildByHash(getHash("PBXFileReference_" PROJECT_DESCRIPTION ".app_" + _targets[i]), PROJECT_DESCRIPTION ".app");
target->addProperty("productReference", getHash("PBXFileReference_" PROJECT_DESCRIPTION ".app_" + _targets[i]), PROJECT_DESCRIPTION ".app", SettingsNoValue);
target->addProperty("productType", "com.apple.product-type.application", "", SettingsNoValue|SettingsQuoteVariable);
_nativeTarget.add(target);
}
_rootSourceGroup->addChildGroup(productsGroup);
_groups.add(productsGroup);
}
void XCodeProvider::setupProject() {
void XcodeProvider::setupProject() {
_project.comment = "PBXProject";
Object *project = new Object(this, "PBXProject", "PBXProject", "PBXProject", "", "Project object");
@ -411,22 +566,30 @@ void XCodeProvider::setupProject() {
ADD_SETTING_ORDER_NOVALUE(regions, "German", "", 3);
project->properties["knownRegions"] = regions;
project->addProperty("mainGroup", getHash("PBXGroup_CustomTemplate"), "CustomTemplate", SettingsNoValue);
project->addProperty("projectDirPath", "", "", SettingsNoValue|SettingsQuoteVariable);
project->addProperty("mainGroup", _rootSourceGroup->getHashRef(), "CustomTemplate", SettingsNoValue);
project->addProperty("projectDirPath", _projectRoot, "", SettingsNoValue|SettingsQuoteVariable);
project->addProperty("projectRoot", "", "", SettingsNoValue|SettingsQuoteVariable);
// List of targets
Property targets;
targets.flags = SettingsAsList;
targets.settings[getHash("PBXNativeTarget_" + _targets[0])] = Setting("", _targets[0], SettingsNoValue, 0, 0);
targets.settings[getHash("PBXNativeTarget_" + _targets[1])] = Setting("", _targets[1], SettingsNoValue, 0, 1);
targets.settings[getHash("PBXNativeTarget_" + _targets[2])] = Setting("", _targets[2], SettingsNoValue, 0, 2);
#ifdef ENABLE_IOS
targets.settings[getHash("PBXNativeTarget_" + _targets[IOS_TARGET])] = Setting("", _targets[IOS_TARGET], SettingsNoValue, 0, 0);
#endif
targets.settings[getHash("PBXNativeTarget_" + _targets[OSX_TARGET])] = Setting("", _targets[OSX_TARGET], SettingsNoValue, 0, 1);
#ifdef ENABLE_IOS
targets.settings[getHash("PBXNativeTarget_" + _targets[SIM_TARGET])] = Setting("", _targets[SIM_TARGET], SettingsNoValue, 0, 2);
#endif
project->properties["targets"] = targets;
#ifndef ENABLE_IOS
// Force list even when there is only a single target
project->properties["targets"].flags |= SettingsSingleItem;
#endif
_project.add(project);
}
void XCodeProvider::setupResourcesBuildPhase() {
void XcodeProvider::setupResourcesBuildPhase() {
_resourcesBuildPhase.comment = "PBXResourcesBuildPhase";
// Setup resource file properties
@ -483,7 +646,7 @@ void XCodeProvider::setupResourcesBuildPhase() {
ADD_SETTING_ORDER_NOVALUE(files, getHash(id), comment, order++);
// TODO Fix crash when adding build file for data
//ADD_BUILD_FILE(id, *file, comment);
ADD_FILE_REFERENCE(*file, properties[*file]);
ADD_FILE_REFERENCE(*file, *file, properties[*file]);
}
// Add custom files depending on the target
@ -503,12 +666,41 @@ void XCodeProvider::setupResourcesBuildPhase() {
}
}
void XCodeProvider::setupSourcesBuildPhase() {
// TODO
void XcodeProvider::setupSourcesBuildPhase() {
_sourcesBuildPhase.comment = "PBXSourcesBuildPhase";
// Setup source file properties
std::map<std::string, FileProperty> properties;
// Same as for containers: a rule for each native target
for (unsigned int i = 0; i < _targets.size(); i++) {
Object *source = new Object(this, "PBXSourcesBuildPhase_" + _targets[i], "PBXSourcesBuildPhase", "PBXSourcesBuildPhase", "", "Sources");
source->addProperty("buildActionMask", "2147483647", "", SettingsNoValue);
Property files;
files.hasOrder = true;
files.flags = SettingsAsList;
int order = 0;
for (std::vector<Object*>::iterator file = _buildFile.objects.begin(); file !=_buildFile.objects.end(); ++file) {
if (!producesObjectFileOnOSX((*file)->name)) {
continue;
}
std::string comment = (*file)->name + " in Sources";
ADD_SETTING_ORDER_NOVALUE(files, getHash((*file)->id), comment, order++);
}
source->properties["files"] = files;
source->addProperty("runOnlyForDeploymentPostprocessing", "0", "", SettingsNoValue);
_sourcesBuildPhase.add(source);
}
}
// Setup all build configurations
void XCodeProvider::setupBuildConfiguration() {
void XcodeProvider::setupBuildConfiguration() {
_buildConfiguration.comment = "XCBuildConfiguration";
_buildConfiguration.flags = SettingsAsList;
@ -516,9 +708,9 @@ void XCodeProvider::setupBuildConfiguration() {
///****************************************
// * iPhone
// ****************************************/
#ifdef ENABLE_IOS
// Debug
Object *iPhone_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-iPhone_Debug", _targets[0] /* ScummVM-iPhone */, "XCBuildConfiguration", "PBXNativeTarget", "Debug");
Object *iPhone_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-iPhone_Debug", _targets[IOS_TARGET] /* ScummVM-iPhone */, "XCBuildConfiguration", "PBXNativeTarget", "Debug");
Property iPhone_Debug;
ADD_SETTING_QUOTE(iPhone_Debug, "ARCHS", "$(ARCHS_UNIVERSAL_IPHONE_OS)");
ADD_SETTING_QUOTE(iPhone_Debug, "CODE_SIGN_IDENTITY", "iPhone Developer");
@ -539,10 +731,10 @@ void XCodeProvider::setupBuildConfiguration() {
ADD_SETTING(iPhone_Debug, "GCC_THUMB_SUPPORT", "NO");
ADD_SETTING(iPhone_Debug, "GCC_UNROLL_LOOPS", "YES");
ValueList iPhone_HeaderSearchPaths;
iPhone_HeaderSearchPaths.push_back("../../engines/");
iPhone_HeaderSearchPaths.push_back("../../");
iPhone_HeaderSearchPaths.push_back("$(SRCROOT)/engines/");
iPhone_HeaderSearchPaths.push_back("$(SRCROOT)");
iPhone_HeaderSearchPaths.push_back("include/");
ADD_SETTING_LIST(iPhone_Debug, "HEADER_SEARCH_PATHS", iPhone_HeaderSearchPaths, SettingsAsList|SettingsNoQuote, 5);
ADD_SETTING_LIST(iPhone_Debug, "HEADER_SEARCH_PATHS", iPhone_HeaderSearchPaths, SettingsAsList|SettingsQuoteVariable, 5);
ADD_SETTING(iPhone_Debug, "INFOPLIST_FILE", "Info.plist");
ValueList iPhone_LibPaths;
iPhone_LibPaths.push_back("$(inherited)");
@ -560,7 +752,7 @@ void XCodeProvider::setupBuildConfiguration() {
iPhone_Debug_Object->properties["buildSettings"] = iPhone_Debug;
// Release
Object *iPhone_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-iPhone_Release", _targets[0] /* ScummVM-iPhone */, "XCBuildConfiguration", "PBXNativeTarget", "Release");
Object *iPhone_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-iPhone_Release", _targets[IOS_TARGET] /* ScummVM-iPhone */, "XCBuildConfiguration", "PBXNativeTarget", "Release");
Property iPhone_Release(iPhone_Debug);
ADD_SETTING(iPhone_Release, "GCC_OPTIMIZATION_LEVEL", "3");
ADD_SETTING(iPhone_Release, "COPY_PHASE_STRIP", "YES");
@ -572,7 +764,7 @@ void XCodeProvider::setupBuildConfiguration() {
_buildConfiguration.add(iPhone_Debug_Object);
_buildConfiguration.add(iPhone_Release_Object);
#endif
/****************************************
* scummvm
****************************************/
@ -581,13 +773,14 @@ void XCodeProvider::setupBuildConfiguration() {
Object *scummvm_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_NAME "_Debug", PROJECT_NAME, "XCBuildConfiguration", "PBXProject", "Debug");
Property scummvm_Debug;
ADD_SETTING(scummvm_Debug, "ALWAYS_SEARCH_USER_PATHS", "NO");
ADD_SETTING_QUOTE(scummvm_Debug, "USER_HEADER_SEARCH_PATHS", "$(SRCROOT) $(SRCROOT)/engines");
ADD_SETTING_QUOTE(scummvm_Debug, "ARCHS", "$(ARCHS_STANDARD_32_BIT)");
ADD_SETTING_QUOTE(scummvm_Debug, "CODE_SIGN_IDENTITY", "Don't Code Sign");
ADD_SETTING_QUOTE_VAR(scummvm_Debug, "CODE_SIGN_IDENTITY[sdk=iphoneos*]", "Don't Code Sign");
ADD_SETTING_QUOTE(scummvm_Debug, "FRAMEWORK_SEARCH_PATHS", "");
ADD_SETTING(scummvm_Debug, "GCC_C_LANGUAGE_STANDARD", "c99");
ADD_SETTING(scummvm_Debug, "GCC_ENABLE_CPP_EXCEPTIONS", "NO");
ADD_SETTING(scummvm_Debug, "GCC_ENABLE_CPP_RTTI", "NO");
ADD_SETTING(scummvm_Debug, "GCC_ENABLE_CPP_RTTI", "YES");
ADD_SETTING(scummvm_Debug, "GCC_INPUT_FILETYPE", "automatic");
ADD_SETTING(scummvm_Debug, "GCC_OPTIMIZATION_LEVEL", "0");
ValueList scummvm_defines(_defines);
@ -601,15 +794,15 @@ void XCodeProvider::setupBuildConfiguration() {
ADD_SETTING(scummvm_Debug, "GCC_WARN_UNUSED_VARIABLE", "YES");
ValueList scummvm_HeaderPaths;
scummvm_HeaderPaths.push_back("include/");
scummvm_HeaderPaths.push_back("../../engines/");
scummvm_HeaderPaths.push_back("../../");
ADD_SETTING_LIST(scummvm_Debug, "HEADER_SEARCH_PATHS", scummvm_HeaderPaths, SettingsNoQuote|SettingsAsList, 5);
scummvm_HeaderPaths.push_back("$(SRCROOT)/engines/");
scummvm_HeaderPaths.push_back("$(SRCROOT)");
ADD_SETTING_LIST(scummvm_Debug, "HEADER_SEARCH_PATHS", scummvm_HeaderPaths, SettingsQuoteVariable|SettingsAsList, 5);
ADD_SETTING_QUOTE(scummvm_Debug, "LIBRARY_SEARCH_PATHS", "");
ADD_SETTING(scummvm_Debug, "ONLY_ACTIVE_ARCH", "YES");
ADD_SETTING_QUOTE(scummvm_Debug, "OTHER_CFLAGS", "");
ADD_SETTING_QUOTE(scummvm_Debug, "OTHER_LDFLAGS", "-lz");
ADD_SETTING(scummvm_Debug, "PREBINDING", "NO");
ADD_SETTING(scummvm_Debug, "SDKROOT", "macosx10.6");
ADD_SETTING(scummvm_Debug, "SDKROOT", "macosx");
scummvm_Debug_Object->addProperty("name", "Debug", "", SettingsNoValue);
scummvm_Debug_Object->properties["buildSettings"] = scummvm_Debug;
@ -633,7 +826,7 @@ void XCodeProvider::setupBuildConfiguration() {
****************************************/
// Debug
Object *scummvmOSX_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-OSX_Debug", _targets[1] /* ScummVM-OS X */, "XCBuildConfiguration", "PBXNativeTarget", "Debug");
Object *scummvmOSX_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-OSX_Debug", _targets[OSX_TARGET] /* ScummVM-OS X */, "XCBuildConfiguration", "PBXNativeTarget", "Debug");
Property scummvmOSX_Debug;
ADD_SETTING_QUOTE(scummvmOSX_Debug, "ARCHS", "$(NATIVE_ARCH)");
ADD_SETTING(scummvmOSX_Debug, "COMPRESS_PNG_FILES", "NO");
@ -642,7 +835,7 @@ void XCodeProvider::setupBuildConfiguration() {
ADD_SETTING_QUOTE(scummvmOSX_Debug, "FRAMEWORK_SEARCH_PATHS", "");
ADD_SETTING(scummvmOSX_Debug, "GCC_C_LANGUAGE_STANDARD", "c99");
ADD_SETTING(scummvmOSX_Debug, "GCC_ENABLE_CPP_EXCEPTIONS", "NO");
ADD_SETTING(scummvmOSX_Debug, "GCC_ENABLE_CPP_RTTI", "NO");
ADD_SETTING(scummvmOSX_Debug, "GCC_ENABLE_CPP_RTTI", "YES");
ADD_SETTING(scummvmOSX_Debug, "GCC_DYNAMIC_NO_PIC", "NO");
ADD_SETTING(scummvmOSX_Debug, "GCC_ENABLE_FIX_AND_CONTINUE", "NO");
ADD_SETTING(scummvmOSX_Debug, "GCC_OPTIMIZATION_LEVEL", "0");
@ -656,11 +849,12 @@ void XCodeProvider::setupBuildConfiguration() {
ValueList scummvmOSX_HeaderPaths;
scummvmOSX_HeaderPaths.push_back("/opt/local/include/SDL");
scummvmOSX_HeaderPaths.push_back("/opt/local/include");
scummvmOSX_HeaderPaths.push_back("/opt/local/include/freetype2");
scummvmOSX_HeaderPaths.push_back("include/");
scummvmOSX_HeaderPaths.push_back("../../engines/");
scummvmOSX_HeaderPaths.push_back("../../");
ADD_SETTING_LIST(scummvmOSX_Debug, "HEADER_SEARCH_PATHS", scummvmOSX_HeaderPaths, SettingsNoQuote|SettingsAsList, 5);
ADD_SETTING_QUOTE(scummvmOSX_Debug, "INFOPLIST_FILE", "$(SRCROOT)/../macosx/Info.plist");
scummvmOSX_HeaderPaths.push_back("$(SRCROOT)/engines/");
scummvmOSX_HeaderPaths.push_back("$(SRCROOT)");
ADD_SETTING_LIST(scummvmOSX_Debug, "HEADER_SEARCH_PATHS", scummvmOSX_HeaderPaths, SettingsQuoteVariable|SettingsAsList, 5);
ADD_SETTING_QUOTE(scummvmOSX_Debug, "INFOPLIST_FILE", "$(SRCROOT)/dists/macosx/Info.plist");
ValueList scummvmOSX_LibPaths;
scummvmOSX_LibPaths.push_back("/sw/lib");
scummvmOSX_LibPaths.push_back("/opt/local/lib");
@ -671,6 +865,10 @@ void XCodeProvider::setupBuildConfiguration() {
ValueList scummvmOSX_LdFlags;
scummvmOSX_LdFlags.push_back("-lSDLmain");
scummvmOSX_LdFlags.push_back("-logg");
scummvmOSX_LdFlags.push_back("-lpng");
scummvmOSX_LdFlags.push_back("-ljpeg");
scummvmOSX_LdFlags.push_back("-ltheora");
scummvmOSX_LdFlags.push_back("-lfreetype");
scummvmOSX_LdFlags.push_back("-lvorbisfile");
scummvmOSX_LdFlags.push_back("-lvorbis");
scummvmOSX_LdFlags.push_back("-lmad");
@ -685,7 +883,7 @@ void XCodeProvider::setupBuildConfiguration() {
scummvmOSX_Debug_Object->properties["buildSettings"] = scummvmOSX_Debug;
// Release
Object *scummvmOSX_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-OSX_Release", _targets[1] /* ScummVM-OS X */, "XCBuildConfiguration", "PBXNativeTarget", "Release");
Object *scummvmOSX_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-OSX_Release", _targets[OSX_TARGET] /* ScummVM-OS X */, "XCBuildConfiguration", "PBXNativeTarget", "Release");
Property scummvmOSX_Release(scummvmOSX_Debug);
ADD_SETTING(scummvmOSX_Release, "COPY_PHASE_STRIP", "YES");
REMOVE_SETTING(scummvmOSX_Release, "GCC_DYNAMIC_NO_PIC");
@ -697,13 +895,13 @@ void XCodeProvider::setupBuildConfiguration() {
_buildConfiguration.add(scummvmOSX_Debug_Object);
_buildConfiguration.add(scummvmOSX_Release_Object);
#ifdef ENABLE_IOS
/****************************************
* ScummVM-Simulator
****************************************/
// Debug
Object *scummvmSimulator_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-Simulator_Debug", _targets[2] /* ScummVM-Simulator */, "XCBuildConfiguration", "PBXNativeTarget", "Debug");
Object *scummvmSimulator_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-Simulator_Debug", _targets[SIM_TARGET] /* ScummVM-Simulator */, "XCBuildConfiguration", "PBXNativeTarget", "Debug");
Property scummvmSimulator_Debug(iPhone_Debug);
ADD_SETTING_QUOTE(scummvmSimulator_Debug, "FRAMEWORK_SEARCH_PATHS", "$(inherited)");
ADD_SETTING_LIST(scummvmSimulator_Debug, "GCC_PREPROCESSOR_DEFINITIONS", scummvm_defines, SettingsNoQuote|SettingsAsList, 5);
@ -715,7 +913,7 @@ void XCodeProvider::setupBuildConfiguration() {
scummvmSimulator_Debug_Object->properties["buildSettings"] = scummvmSimulator_Debug;
// Release
Object *scummvmSimulator_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-Simulator_Release", _targets[2] /* ScummVM-Simulator */, "XCBuildConfiguration", "PBXNativeTarget", "Release");
Object *scummvmSimulator_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-Simulator_Release", _targets[SIM_TARGET] /* ScummVM-Simulator */, "XCBuildConfiguration", "PBXNativeTarget", "Release");
Property scummvmSimulator_Release(scummvmSimulator_Debug);
ADD_SETTING(scummvmSimulator_Release, "COPY_PHASE_STRIP", "YES");
ADD_SETTING(scummvmSimulator_Release, "GCC_OPTIMIZATION_LEVEL", "3");
@ -732,7 +930,7 @@ void XCodeProvider::setupBuildConfiguration() {
// Configuration List
_configurationList.comment = "XCConfigurationList";
_configurationList.flags = SettingsAsList;
#endif
// Warning: This assumes we have all configurations with a Debug & Release pair
for (std::vector<Object *>::iterator config = _buildConfiguration.objects.begin(); config != _buildConfiguration.objects.end(); config++) {
@ -758,7 +956,7 @@ void XCodeProvider::setupBuildConfiguration() {
//////////////////////////////////////////////////////////////////////////
// Setup global defines
void XCodeProvider::setupDefines(const BuildSetup &setup) {
void XcodeProvider::setupDefines(const BuildSetup &setup) {
for (StringList::const_iterator i = setup.defines.begin(); i != setup.defines.end(); ++i) {
if (*i == "HAVE_NASM") // Not supported on Mac (TODO: change how it's handled in main class or add it only in MSVC/CodeBlocks providers?)
@ -772,7 +970,6 @@ void XCodeProvider::setupDefines(const BuildSetup &setup) {
ADD_DEFINE(_defines, "SCUMM_LITTLE_ENDIAN");
ADD_DEFINE(_defines, "UNIX");
ADD_DEFINE(_defines, "SCUMMVM");
ADD_DEFINE(_defines, "USE_TREMOR");
}
//////////////////////////////////////////////////////////////////////////
@ -780,7 +977,7 @@ void XCodeProvider::setupDefines(const BuildSetup &setup) {
//////////////////////////////////////////////////////////////////////////
// TODO use md5 to compute a file hash (and fall back to standard key generation if not passed a file)
std::string XCodeProvider::getHash(std::string key) {
std::string XcodeProvider::getHash(std::string key) {
#if DEBUG_XCODE_HASH
return key;
@ -800,7 +997,7 @@ std::string XCodeProvider::getHash(std::string key) {
bool isSeparator (char s) { return (s == '-'); }
std::string XCodeProvider::newHash() const {
std::string XcodeProvider::newHash() const {
std::string hash = createUUID();
// Remove { and - from UUID and resize to 96-bits uppercase hex string
@ -832,7 +1029,7 @@ std::string replace(std::string input, const std::string find, std::string repla
return input;
}
std::string XCodeProvider::writeProperty(const std::string &variable, Property &prop, int flags) const {
std::string XcodeProvider::writeProperty(const std::string &variable, Property &prop, int flags) const {
std::string output;
output += (flags & SettingsSingleItem ? "" : "\t\t\t") + variable + " = ";
@ -847,7 +1044,9 @@ std::string XCodeProvider::writeProperty(const std::string &variable, Property &
output += writeSetting((*setting).first, (*setting).second);
if ((prop.flags & SettingsAsList) && prop.settings.size() > 1) {
// The combination of SettingsAsList, and SettingsSingleItem should use "," and not ";" (i.e children
// in PBXGroup, so we special case that case here.
if ((prop.flags & SettingsAsList) && (prop.settings.size() > 1 || (prop.flags & SettingsSingleItem))) {
output += (prop.settings.size() > 0) ? ",\n" : "\n";
} else {
output += ";";
@ -861,13 +1060,13 @@ std::string XCodeProvider::writeProperty(const std::string &variable, Property &
return output;
}
std::string XCodeProvider::writeSetting(const std::string &variable, std::string value, std::string comment, int flags, int indent) const {
std::string XcodeProvider::writeSetting(const std::string &variable, std::string value, std::string comment, int flags, int indent) const {
return writeSetting(variable, Setting(value, comment, flags, indent));
}
// Heavily modified (not in a good way) function, imported from the QMake
// XCode project generator pbuilder_pbx.cpp, writeSettings() (under LGPL 2.1)
std::string XCodeProvider::writeSetting(const std::string &variable, const Setting &setting) const {
std::string XcodeProvider::writeSetting(const std::string &variable, const Setting &setting) const {
std::string output;
const std::string quote = (setting.flags & SettingsNoQuote) ? "" : "\"";
const std::string escape_quote = quote.empty() ? "" : "\\" + quote;

View file

@ -30,9 +30,9 @@
namespace CreateProjectTool {
class XCodeProvider : public ProjectProvider {
class XcodeProvider : public ProjectProvider {
public:
XCodeProvider(StringList &global_warnings, std::map<std::string, StringList> &project_warnings, const int version = 0);
XcodeProvider(StringList &global_warnings, std::map<std::string, StringList> &project_warnings, const int version = 0);
protected:
@ -45,7 +45,6 @@ protected:
void writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation,
const StringList &duplicate, const std::string &objPrefix, const std::string &filePrefix);
private:
enum {
SettingsAsList = 0x01,
@ -169,7 +168,7 @@ private:
PropertyList properties; // List of object properties, including output configuration
// Constructs an object and add a default type property
Object(XCodeProvider *objectParent, std::string objectId, std::string objectName, std::string objectType, std::string objectRefType = "", std::string objectComment = "")
Object(XcodeProvider *objectParent, std::string objectId, std::string objectName, std::string objectType, std::string objectRefType = "", std::string objectComment = "")
: id(objectId), name(objectName), refType(objectRefType), comment(objectComment), parent(objectParent) {
assert(objectParent);
assert(!objectId.empty());
@ -210,9 +209,10 @@ private:
return output;
}
// Slight hack, to allow Group access to parent.
protected:
XcodeProvider *parent;
private:
XCodeProvider *parent;
// Returns the type property (should always be the first in the properties map)
std::string getType() {
assert(!properties.empty());
@ -258,6 +258,36 @@ private:
}
};
// A class to maintain a folder-reference group-hierarchy, which together with the functionality below
// allows for breaking up sub-paths into a chain of groups. This helps with merging engines into the
// overall group-layout.
class Group : public Object {
int _childOrder;
std::map<std::string, Group *> _childGroups;
std::string _treeName;
void addChildInternal(const std::string &id, const std::string &comment);
public:
Group(XcodeProvider *objectParent, const std::string &groupName, const std::string &uniqueName, const std::string &path);
void addChildFile(const std::string &name);
void addChildByHash(const std::string &hash, const std::string &name);
// Should be passed the hash for the entry
void addChildGroup(const Group* group);
void ensureChildExists(const std::string &name);
Group *getChildGroup(const std::string &name);
std::string getHashRef() const { return parent->getHash(id); }
};
// The path used by the root-source group
std::string _projectRoot;
// The base source group, currently also re-purposed for containing the various support-groups.
Group *_rootSourceGroup;
// Helper function to create the chain of groups for the various subfolders. Necessary as
// create_project likes to start in engines/
Group *touchGroupsForPath(const std::string &path);
// Functionality for adding file-refs and build-files, as Group-objects need to be able to do this.
void addFileReference(const std::string &id, const std::string &name, FileProperty properties);
void addProductFileReference(const std::string &id, const std::string &name);
void addBuildFile(const std::string &id, const std::string &name, const std::string &fileRefId, const std::string &comment);
// All objects
std::map<std::string, std::string> _hashDictionnary;
ValueList _defines;

View file

@ -54,10 +54,11 @@ bool AIFFTrack::openSound(const Common::String &filename, const Common::String &
return false;
}
_soundName = soundName;
Audio::SeekableAudioStream *aiffStream = Audio::makeAIFFStream(file, DisposeAfterUse::YES);
Audio::RewindableAudioStream *aiffStream = Audio::makeAIFFStream(file, DisposeAfterUse::YES);
Audio::SeekableAudioStream *seekStream = dynamic_cast<Audio::SeekableAudioStream *>(aiffStream);
_stream = aiffStream;
if (start)
aiffStream->seek(*start);
seekStream->seek(*start);
if (!_stream)
return false;
_handle = new Audio::SoundHandle();

View file

@ -1160,9 +1160,9 @@ void LauncherDialog::updateButtons() {
_loadButton->setEnabled(en);
_loadButton->draw();
}
switchButtonsText(_addButton, "~A~dd Game...", "Mass Add...");
switchButtonsText(_addButton, "~A~dd Game...", _s("Mass Add..."));
#ifdef ENABLE_EVENTRECORDER
switchButtonsText(_loadButton, "~L~oad...", "Record...");
switchButtonsText(_loadButton, "~L~oad...", _s("Record..."));
#endif
}

View file

@ -461,11 +461,9 @@ void OptionsDialog::close() {
if (_oplPopUp) {
if (_enableAudioSettings) {
const OPL::Config::EmulatorDescription *ed = OPL::Config::getAvailable();
while (ed->name && ed->id != (int)_oplPopUp->getSelectedTag())
++ed;
const OPL::Config::EmulatorDescription *ed = OPL::Config::findDriver(_oplPopUp->getSelectedTag());
if (ed->name)
if (ed)
ConfMan.set("opl_driver", ed->name, _domain);
else
ConfMan.removeKey("opl_driver", _domain);

View file

@ -433,10 +433,12 @@ const Graphics::Surface *CinepakDecoder::decodeFrame(Common::SeekableReadStream
for (uint16 j = 0; j < 256; j++) {
_curFrame.strips[i].v1_codebook[j] = _curFrame.strips[i - 1].v1_codebook[j];
_curFrame.strips[i].v4_codebook[j] = _curFrame.strips[i - 1].v4_codebook[j];
}
// Copy the QuickTime dither tables
memcpy(_curFrame.strips[i].v1_dither, _curFrame.strips[i - 1].v1_dither, 256 * 4 * 4 * 4);
memcpy(_curFrame.strips[i].v4_dither, _curFrame.strips[i - 1].v4_dither, 256 * 4 * 4 * 4);
}
}
_curFrame.strips[i].id = stream.readUint16BE();
_curFrame.strips[i].length = stream.readUint16BE() - 12; // Subtract the 12 byte header

View file

@ -64,6 +64,9 @@ struct CinepakFrame {
* Cinepak decoder.
*
* Used by BMP/AVI and PICT/QuickTime.
*
* Used in engines:
* - sherlock
*/
class CinepakDecoder : public Codec {
public:
@ -88,7 +91,6 @@ private:
byte *_ditherPalette;
bool _dirtyPalette;
byte *_rgbLookup;
byte *_colorMap;
DitherType _ditherType;