ALL: synced with ScummVM
This commit is contained in:
parent
fc33643a38
commit
11b457122b
34 changed files with 7815 additions and 863 deletions
|
@ -927,18 +927,20 @@ static void createLookupTable() {
|
||||||
//
|
//
|
||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
|
|
||||||
class MidiDriver_ADLIB : public MidiDriver_Emulated {
|
class MidiDriver_ADLIB : public MidiDriver {
|
||||||
friend class AdLibPart;
|
friend class AdLibPart;
|
||||||
friend class AdLibPercussionChannel;
|
friend class AdLibPercussionChannel;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MidiDriver_ADLIB(Audio::Mixer *mixer);
|
MidiDriver_ADLIB();
|
||||||
|
|
||||||
int open();
|
int open();
|
||||||
void close();
|
void close();
|
||||||
void send(uint32 b);
|
void send(uint32 b);
|
||||||
void send(byte channel, uint32 b); // Supports higher than channel 15
|
void send(byte channel, uint32 b); // Supports higher than channel 15
|
||||||
uint32 property(int prop, uint32 param);
|
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 setPitchBendRange(byte channel, uint range);
|
||||||
void sysEx_customInstrument(byte channel, uint32 type, const byte *instr);
|
void sysEx_customInstrument(byte channel, uint32 type, const byte *instr);
|
||||||
|
@ -946,10 +948,7 @@ public:
|
||||||
MidiChannel *allocateChannel();
|
MidiChannel *allocateChannel();
|
||||||
MidiChannel *getPercussionChannel() { return &_percussion; } // Percussion partially supported
|
MidiChannel *getPercussionChannel() { return &_percussion; } // Percussion partially supported
|
||||||
|
|
||||||
|
virtual void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc);
|
||||||
// AudioStream API
|
|
||||||
bool isStereo() const { return _opl->isStereo(); }
|
|
||||||
int getRate() const { return _mixer->getOutputRate(); }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _scummSmallHeader; // FIXME: This flag controls a special mode for SCUMM V3 games
|
bool _scummSmallHeader; // FIXME: This flag controls a special mode for SCUMM V3 games
|
||||||
|
@ -963,6 +962,9 @@ private:
|
||||||
byte *_regCacheSecondary;
|
byte *_regCacheSecondary;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Common::TimerManager::TimerProc _adlibTimerProc;
|
||||||
|
void *_adlibTimerParam;
|
||||||
|
|
||||||
int _timerCounter;
|
int _timerCounter;
|
||||||
|
|
||||||
uint16 _channelTable2[9];
|
uint16 _channelTable2[9];
|
||||||
|
@ -974,7 +976,8 @@ private:
|
||||||
AdLibPart _parts[32];
|
AdLibPart _parts[32];
|
||||||
AdLibPercussionChannel _percussion;
|
AdLibPercussionChannel _percussion;
|
||||||
|
|
||||||
void generateSamples(int16 *buf, int len);
|
bool _isOpen;
|
||||||
|
|
||||||
void onTimer();
|
void onTimer();
|
||||||
void partKeyOn(AdLibPart *part, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan);
|
void partKeyOn(AdLibPart *part, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan);
|
||||||
void partKeyOff(AdLibPart *part, byte note);
|
void partKeyOff(AdLibPart *part, byte note);
|
||||||
|
@ -1376,8 +1379,7 @@ void AdLibPercussionChannel::sysEx_customInstrument(uint32 type, const byte *ins
|
||||||
|
|
||||||
// MidiDriver method implementations
|
// MidiDriver method implementations
|
||||||
|
|
||||||
MidiDriver_ADLIB::MidiDriver_ADLIB(Audio::Mixer *mixer)
|
MidiDriver_ADLIB::MidiDriver_ADLIB() {
|
||||||
: MidiDriver_Emulated(mixer) {
|
|
||||||
uint i;
|
uint i;
|
||||||
|
|
||||||
_scummSmallHeader = false;
|
_scummSmallHeader = false;
|
||||||
|
@ -1403,13 +1405,16 @@ MidiDriver_ADLIB::MidiDriver_ADLIB(Audio::Mixer *mixer)
|
||||||
_timerIncrease = 0xD69;
|
_timerIncrease = 0xD69;
|
||||||
_timerThreshold = 0x411B;
|
_timerThreshold = 0x411B;
|
||||||
_opl = 0;
|
_opl = 0;
|
||||||
|
_adlibTimerProc = 0;
|
||||||
|
_adlibTimerParam = 0;
|
||||||
|
_isOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int MidiDriver_ADLIB::open() {
|
int MidiDriver_ADLIB::open() {
|
||||||
if (_isOpen)
|
if (_isOpen)
|
||||||
return MERR_ALREADY_OPEN;
|
return MERR_ALREADY_OPEN;
|
||||||
|
|
||||||
MidiDriver_Emulated::open();
|
_isOpen = true;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
AdLibVoice *voice;
|
AdLibVoice *voice;
|
||||||
|
@ -1434,7 +1439,7 @@ int MidiDriver_ADLIB::open() {
|
||||||
_opl3Mode = false;
|
_opl3Mode = false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
_opl->init(getRate());
|
_opl->init();
|
||||||
|
|
||||||
_regCache = (byte *)calloc(256, 1);
|
_regCache = (byte *)calloc(256, 1);
|
||||||
|
|
||||||
|
@ -1452,8 +1457,7 @@ int MidiDriver_ADLIB::open() {
|
||||||
}
|
}
|
||||||
#endif
|
#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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1462,7 +1466,8 @@ void MidiDriver_ADLIB::close() {
|
||||||
return;
|
return;
|
||||||
_isOpen = false;
|
_isOpen = false;
|
||||||
|
|
||||||
_mixer->stopHandle(_mixerSoundHandle);
|
// Stop the OPL timer
|
||||||
|
_opl->stop();
|
||||||
|
|
||||||
uint i;
|
uint i;
|
||||||
for (i = 0; i < ARRAYSIZE(_voices); ++i) {
|
for (i = 0; i < ARRAYSIZE(_voices); ++i) {
|
||||||
|
@ -1616,14 +1621,10 @@ void MidiDriver_ADLIB::adlibWriteSecondary(byte reg, byte value) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void MidiDriver_ADLIB::generateSamples(int16 *data, int len) {
|
|
||||||
if (_opl->isStereo()) {
|
|
||||||
len *= 2;
|
|
||||||
}
|
|
||||||
_opl->readBuffer(data, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MidiDriver_ADLIB::onTimer() {
|
void MidiDriver_ADLIB::onTimer() {
|
||||||
|
if (_adlibTimerProc)
|
||||||
|
(*_adlibTimerProc)(_adlibTimerParam);
|
||||||
|
|
||||||
_timerCounter += _timerIncrease;
|
_timerCounter += _timerIncrease;
|
||||||
while (_timerCounter >= _timerThreshold) {
|
while (_timerCounter >= _timerThreshold) {
|
||||||
_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) {
|
void MidiDriver_ADLIB::mcOff(AdLibVoice *voice) {
|
||||||
AdLibVoice *tmp;
|
AdLibVoice *tmp;
|
||||||
|
|
||||||
|
@ -2300,7 +2306,7 @@ MusicDevices AdLibEmuMusicPlugin::getDevices() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::Error AdLibEmuMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const {
|
Common::Error AdLibEmuMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const {
|
||||||
*mididriver = new MidiDriver_ADLIB(g_system->getMixer());
|
*mididriver = new MidiDriver_ADLIB();
|
||||||
|
|
||||||
return Common::kNoError;
|
return Common::kNoError;
|
||||||
}
|
}
|
349
audio/alsa_opl.cpp
Normal file
349
audio/alsa_opl.cpp
Normal 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
343
audio/decoders/3do.cpp
Normal 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
158
audio/decoders/3do.h
Normal 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
|
|
@ -24,16 +24,19 @@
|
||||||
* The code in this file is based on information found at
|
* The code in this file is based on information found at
|
||||||
* http://www.borg.com/~jglatt/tech/aiff.htm
|
* http://www.borg.com/~jglatt/tech/aiff.htm
|
||||||
*
|
*
|
||||||
* We currently only implement uncompressed AIFF. If we ever need AIFF-C, SoX
|
* Also partially based on libav's aiffdec.c
|
||||||
* (http://sox.sourceforge.net) may be a good place to start from.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "common/debug.h"
|
||||||
#include "common/endian.h"
|
#include "common/endian.h"
|
||||||
#include "common/stream.h"
|
#include "common/stream.h"
|
||||||
|
#include "common/substream.h"
|
||||||
#include "common/textconsole.h"
|
#include "common/textconsole.h"
|
||||||
|
|
||||||
|
#include "audio/audiostream.h"
|
||||||
#include "audio/decoders/aiff.h"
|
#include "audio/decoders/aiff.h"
|
||||||
#include "audio/decoders/raw.h"
|
#include "audio/decoders/raw.h"
|
||||||
|
#include "audio/decoders/3do.h"
|
||||||
|
|
||||||
namespace Audio {
|
namespace Audio {
|
||||||
|
|
||||||
|
@ -62,23 +65,34 @@ uint32 readExtended(Common::SeekableReadStream &stream) {
|
||||||
return mantissa;
|
return mantissa;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool loadAIFFFromStream(Common::SeekableReadStream &stream, int &size, int &rate, byte &flags) {
|
// AIFF versions
|
||||||
byte buf[4];
|
static const uint32 kVersionAIFF = MKTAG('A', 'I', 'F', 'F');
|
||||||
|
static const uint32 kVersionAIFC = MKTAG('A', 'I', 'F', 'C');
|
||||||
|
|
||||||
stream.read(buf, 4);
|
// Codecs
|
||||||
if (memcmp(buf, "FORM", 4) != 0) {
|
static const uint32 kCodecPCM = MKTAG('N', 'O', 'N', 'E'); // very original
|
||||||
warning("loadAIFFFromStream: No 'FORM' header");
|
|
||||||
return false;
|
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 (version != kVersionAIFF && version != kVersionAIFC) {
|
||||||
if (memcmp(buf, "AIFF", 4) != 0) {
|
warning("makeAIFFStream: No 'AIFF' or 'AIFC' header");
|
||||||
warning("loadAIFFFromStream: No 'AIFF' header");
|
|
||||||
return false;
|
if (disposeAfterUse == DisposeAfterUse::YES)
|
||||||
|
delete stream;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// From here on, we only care about the COMM and SSND chunks, which are
|
// 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 foundCOMM = false;
|
||||||
bool foundSSND = false;
|
bool foundSSND = false;
|
||||||
|
|
||||||
uint16 numChannels = 0, bitsPerSample = 0;
|
uint16 channels = 0, bitsPerSample = 0;
|
||||||
uint32 numSampleFrames = 0, offset = 0, blockSize = 0, soundOffset = 0;
|
uint32 blockAlign = 0, rate = 0;
|
||||||
|
uint32 codec = kCodecPCM; // AIFF default
|
||||||
|
Common::SeekableReadStream *dataStream = 0;
|
||||||
|
|
||||||
while (!(foundCOMM && foundSSND) && !stream.err() && !stream.eos()) {
|
while (!(foundCOMM && foundSSND) && !stream->err() && !stream->eos()) {
|
||||||
uint32 length, pos;
|
uint32 tag = stream->readUint32BE();
|
||||||
|
uint32 length = stream->readUint32BE();
|
||||||
|
uint32 pos = stream->pos();
|
||||||
|
|
||||||
stream.read(buf, 4);
|
if (stream->eos() || stream->err())
|
||||||
length = stream.readUint32BE();
|
break;
|
||||||
pos = stream.pos();
|
|
||||||
|
|
||||||
if (memcmp(buf, "COMM", 4) == 0) {
|
switch (tag) {
|
||||||
|
case MKTAG('C', 'O', 'M', 'M'):
|
||||||
foundCOMM = true;
|
foundCOMM = true;
|
||||||
numChannels = stream.readUint16BE();
|
channels = stream->readUint16BE();
|
||||||
numSampleFrames = stream.readUint32BE();
|
/* frameCount = */ stream->readUint32BE();
|
||||||
bitsPerSample = stream.readUint16BE();
|
bitsPerSample = stream->readUint16BE();
|
||||||
rate = readExtended(stream);
|
rate = readExtended(*stream);
|
||||||
size = numSampleFrames * numChannels * (bitsPerSample / 8);
|
|
||||||
} else if (memcmp(buf, "SSND", 4) == 0) {
|
if (version == kVersionAIFC)
|
||||||
|
codec = stream->readUint32BE();
|
||||||
|
break;
|
||||||
|
case MKTAG('S', 'S', 'N', 'D'):
|
||||||
foundSSND = true;
|
foundSSND = true;
|
||||||
offset = stream.readUint32BE();
|
/* uint32 offset = */ stream->readUint32BE();
|
||||||
blockSize = stream.readUint32BE();
|
blockAlign = stream->readUint32BE();
|
||||||
soundOffset = stream.pos();
|
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) {
|
if (!foundCOMM) {
|
||||||
warning("loadAIFFFromStream: Cound not find 'COMM' chunk");
|
warning("makeAIFFStream: Cound not find 'COMM' chunk");
|
||||||
return false;
|
|
||||||
|
if (!dataStream && disposeAfterUse == DisposeAfterUse::YES)
|
||||||
|
delete stream;
|
||||||
|
|
||||||
|
delete dataStream;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!foundSSND) {
|
if (!foundSSND) {
|
||||||
warning("loadAIFFFromStream: Cound not find 'SSND' chunk");
|
warning("makeAIFFStream: Cound not find 'SSND' chunk");
|
||||||
return false;
|
|
||||||
|
if (disposeAfterUse == DisposeAfterUse::YES)
|
||||||
|
delete stream;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only implement a subset of the AIFF standard.
|
// We only implement a subset of the AIFF standard.
|
||||||
|
|
||||||
if (numChannels < 1 || numChannels > 2) {
|
if (channels < 1 || channels > 2) {
|
||||||
warning("loadAIFFFromStream: Only 1 or 2 channels are supported, not %d", numChannels);
|
warning("makeAIFFStream: Only 1 or 2 channels are supported, not %d", channels);
|
||||||
return false;
|
delete dataStream;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
data = (byte *)malloc(size);
|
// Seek to the start of dataStream, required for at least FileStream
|
||||||
assert(data);
|
dataStream->seek(0);
|
||||||
stream->read(data, size);
|
|
||||||
|
|
||||||
if (disposeAfterUse == DisposeAfterUse::YES)
|
switch (codec) {
|
||||||
delete stream;
|
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(dataStream, rate, rawFlags);
|
||||||
return makeRawStream(data, size, rate, flags);
|
}
|
||||||
|
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
|
} // End of namespace Audio
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
/**
|
/**
|
||||||
* @file
|
* @file
|
||||||
* Sound decoder used in engines:
|
* Sound decoder used in engines:
|
||||||
|
* - bbvs
|
||||||
* - pegasus
|
* - pegasus
|
||||||
* - saga
|
* - saga
|
||||||
* - sci
|
* - sci
|
||||||
|
@ -41,28 +42,17 @@ class SeekableReadStream;
|
||||||
|
|
||||||
namespace Audio {
|
namespace Audio {
|
||||||
|
|
||||||
class SeekableAudioStream;
|
class RewindableAudioStream;
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to load an AIFF from the given seekable stream and create an AudioStream
|
* Try to load an AIFF from the given seekable stream and create an AudioStream
|
||||||
* from that data.
|
* from that data.
|
||||||
*
|
*
|
||||||
* This function uses loadAIFFFromStream() internally.
|
|
||||||
*
|
|
||||||
* @param stream the SeekableReadStream from which to read the AIFF data
|
* @param stream the SeekableReadStream from which to read the AIFF data
|
||||||
* @param disposeAfterUse whether to delete the stream after use
|
* @param disposeAfterUse whether to delete the stream after use
|
||||||
* @return a new SeekableAudioStream, or NULL, if an error occurred
|
* @return a new SeekableAudioStream, or NULL, if an error occurred
|
||||||
*/
|
*/
|
||||||
SeekableAudioStream *makeAIFFStream(
|
RewindableAudioStream *makeAIFFStream(
|
||||||
Common::SeekableReadStream *stream,
|
Common::SeekableReadStream *stream,
|
||||||
DisposeAfterUse::Flag disposeAfterUse);
|
DisposeAfterUse::Flag disposeAfterUse);
|
||||||
|
|
||||||
|
|
|
@ -23,15 +23,25 @@
|
||||||
/**
|
/**
|
||||||
* @file
|
* @file
|
||||||
* Sound decoder used in engines:
|
* Sound decoder used in engines:
|
||||||
|
* - access
|
||||||
* - agos
|
* - agos
|
||||||
|
* - cge
|
||||||
|
* - cge2
|
||||||
|
* - fullpipe
|
||||||
* - gob
|
* - gob
|
||||||
|
* - hopkins
|
||||||
* - mohawk
|
* - mohawk
|
||||||
|
* - prince
|
||||||
* - saga
|
* - saga
|
||||||
* - sci
|
* - sci
|
||||||
* - scumm
|
* - scumm
|
||||||
|
* - sherlock
|
||||||
* - sword1
|
* - sword1
|
||||||
* - sword2
|
* - sword2
|
||||||
|
* - tony
|
||||||
* - tucker
|
* - tucker
|
||||||
|
* - wintermute
|
||||||
|
* - zvision
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef AUDIO_WAVE_H
|
#ifndef AUDIO_WAVE_H
|
||||||
|
|
216
audio/fmopl.cpp
216
audio/fmopl.cpp
|
@ -22,21 +22,33 @@
|
||||||
|
|
||||||
#include "audio/fmopl.h"
|
#include "audio/fmopl.h"
|
||||||
|
|
||||||
|
#include "audio/mixer.h"
|
||||||
#include "audio/softsynth/opl/dosbox.h"
|
#include "audio/softsynth/opl/dosbox.h"
|
||||||
#include "audio/softsynth/opl/mame.h"
|
#include "audio/softsynth/opl/mame.h"
|
||||||
|
|
||||||
#include "common/config-manager.h"
|
#include "common/config-manager.h"
|
||||||
|
#include "common/system.h"
|
||||||
#include "common/textconsole.h"
|
#include "common/textconsole.h"
|
||||||
|
#include "common/timer.h"
|
||||||
#include "common/translation.h"
|
#include "common/translation.h"
|
||||||
|
|
||||||
namespace OPL {
|
namespace OPL {
|
||||||
|
|
||||||
|
// Factory functions
|
||||||
|
|
||||||
|
#ifdef USE_ALSA
|
||||||
|
namespace ALSA {
|
||||||
|
OPL *create(Config::OplType type);
|
||||||
|
} // End of namespace ALSA
|
||||||
|
#endif // USE_ALSA
|
||||||
|
|
||||||
// Config implementation
|
// Config implementation
|
||||||
|
|
||||||
enum OplEmulator {
|
enum OplEmulator {
|
||||||
kAuto = 0,
|
kAuto = 0,
|
||||||
kMame = 1,
|
kMame = 1,
|
||||||
kDOSBox = 2
|
kDOSBox = 2,
|
||||||
|
kALSA = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
OPL::OPL() {
|
OPL::OPL() {
|
||||||
|
@ -50,6 +62,9 @@ const Config::EmulatorDescription Config::_drivers[] = {
|
||||||
{ "mame", _s("MAME OPL emulator"), kMame, kFlagOpl2 },
|
{ "mame", _s("MAME OPL emulator"), kMame, kFlagOpl2 },
|
||||||
#ifndef DISABLE_DOSBOX_OPL
|
#ifndef DISABLE_DOSBOX_OPL
|
||||||
{ "db", _s("DOSBox OPL emulator"), kDOSBox, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 },
|
{ "db", _s("DOSBox OPL emulator"), kDOSBox, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 },
|
||||||
|
#endif
|
||||||
|
#ifdef USE_ALSA
|
||||||
|
{ "alsa", _s("ALSA Direct FM"), kALSA, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 },
|
||||||
#endif
|
#endif
|
||||||
{ 0, 0, 0, 0 }
|
{ 0, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
|
@ -63,6 +78,15 @@ Config::DriverId Config::parse(const Common::String &name) {
|
||||||
return -1;
|
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) {
|
Config::DriverId Config::detect(OplType type) {
|
||||||
uint32 flags = 0;
|
uint32 flags = 0;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -80,12 +104,21 @@ Config::DriverId Config::detect(OplType type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
DriverId drv = parse(ConfMan.get("opl_driver"));
|
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
|
// When a valid driver is selected, check whether it supports
|
||||||
// the requested OPL chip.
|
// the requested OPL chip.
|
||||||
if (drv != -1 && drv != kAuto) {
|
if (drv != -1 && drv != kAuto) {
|
||||||
|
const EmulatorDescription *driverDesc = findDriver(drv);
|
||||||
// If the chip is supported, just use the driver.
|
// 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;
|
return drv;
|
||||||
} else {
|
} else {
|
||||||
// Else we will output a warning and just
|
// Else we will output a warning and just
|
||||||
|
@ -145,6 +178,11 @@ OPL *Config::create(DriverId driver, OplType type) {
|
||||||
return new DOSBox::OPL(type);
|
return new DOSBox::OPL(type);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_ALSA
|
||||||
|
case kALSA:
|
||||||
|
return ALSA::create(type);
|
||||||
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
warning("Unsupported OPL emulator %d", driver);
|
warning("Unsupported OPL emulator %d", driver);
|
||||||
// TODO: Maybe we should add some dummy emulator too, which just outputs
|
// 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;
|
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
|
} // 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;
|
|
||||||
}
|
|
||||||
|
|
156
audio/fmopl.h
156
audio/fmopl.h
|
@ -23,8 +23,16 @@
|
||||||
#ifndef AUDIO_FMOPL_H
|
#ifndef AUDIO_FMOPL_H
|
||||||
#define AUDIO_FMOPL_H
|
#define AUDIO_FMOPL_H
|
||||||
|
|
||||||
|
#include "audio/audiostream.h"
|
||||||
|
|
||||||
|
#include "common/func.h"
|
||||||
|
#include "common/ptr.h"
|
||||||
#include "common/scummsys.h"
|
#include "common/scummsys.h"
|
||||||
|
|
||||||
|
namespace Audio {
|
||||||
|
class SoundHandle;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
class String;
|
class String;
|
||||||
}
|
}
|
||||||
|
@ -70,6 +78,12 @@ public:
|
||||||
*/
|
*/
|
||||||
static DriverId parse(const Common::String &name);
|
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.
|
* Detects a driver for the specific type.
|
||||||
*
|
*
|
||||||
|
@ -92,6 +106,14 @@ private:
|
||||||
static const EmulatorDescription _drivers[];
|
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 {
|
class OPL {
|
||||||
private:
|
private:
|
||||||
static bool _hasInstance;
|
static bool _hasInstance;
|
||||||
|
@ -102,10 +124,9 @@ public:
|
||||||
/**
|
/**
|
||||||
* Initializes the OPL emulator.
|
* Initializes the OPL emulator.
|
||||||
*
|
*
|
||||||
* @param rate output sample rate
|
|
||||||
* @return true on success, false on failure
|
* @return true on success, false on failure
|
||||||
*/
|
*/
|
||||||
virtual bool init(int rate) = 0;
|
virtual bool init() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reinitializes the OPL emulator
|
* Reinitializes the OPL emulator
|
||||||
|
@ -139,6 +160,101 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual void writeReg(int r, int v) = 0;
|
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.
|
* Read up to 'length' samples.
|
||||||
*
|
*
|
||||||
|
@ -149,33 +265,21 @@ public:
|
||||||
* So if you request 4 samples from a stereo OPL, you will get
|
* So if you request 4 samples from a stereo OPL, you will get
|
||||||
* a total of two left channel and two right channel samples.
|
* 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;
|
||||||
|
|
||||||
/**
|
private:
|
||||||
* Returns whether the setup OPL mode is stereo or not
|
int _baseFreq;
|
||||||
*/
|
|
||||||
virtual bool isStereo() const = 0;
|
enum {
|
||||||
|
FIXP_SHIFT = 16
|
||||||
|
};
|
||||||
|
|
||||||
|
int _nextTick;
|
||||||
|
int _samplesPerTick;
|
||||||
|
|
||||||
|
Audio::SoundHandle *_handle;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // End of namespace OPL
|
} // 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
|
#endif
|
||||||
|
|
|
@ -370,6 +370,7 @@ public:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef void (*XMidiCallbackProc)(byte eventData, void *refCon);
|
typedef void (*XMidiCallbackProc)(byte eventData, void *refCon);
|
||||||
|
typedef void (*XMidiNewTimbreListProc)(MidiDriver_BASE *driver, const byte *timbreListPtr, uint32 timbreListSize);
|
||||||
|
|
||||||
MidiParser();
|
MidiParser();
|
||||||
virtual ~MidiParser() { allNotesOff(); }
|
virtual ~MidiParser() { allNotesOff(); }
|
||||||
|
@ -395,7 +396,7 @@ public:
|
||||||
static void defaultXMidiCallback(byte eventData, void *refCon);
|
static void defaultXMidiCallback(byte eventData, void *refCon);
|
||||||
|
|
||||||
static MidiParser *createParser_SMF();
|
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 MidiParser *createParser_QT();
|
||||||
static void timerCallback(void *data) { ((MidiParser *) data)->onTimer(); }
|
static void timerCallback(void *data) { ((MidiParser *) data)->onTimer(); }
|
||||||
};
|
};
|
||||||
|
|
83
audio/miles.h
Normal file
83
audio/miles.h
Normal 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
1274
audio/miles_adlib.cpp
Normal file
File diff suppressed because it is too large
Load diff
912
audio/miles_mt32.cpp
Normal file
912
audio/miles_mt32.cpp
Normal 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
|
|
@ -1,16 +1,20 @@
|
||||||
MODULE := audio
|
MODULE := audio
|
||||||
|
|
||||||
MODULE_OBJS := \
|
MODULE_OBJS := \
|
||||||
|
adlib.o \
|
||||||
audiostream.o \
|
audiostream.o \
|
||||||
fmopl.o \
|
fmopl.o \
|
||||||
mididrv.o \
|
mididrv.o \
|
||||||
midiparser.o \
|
|
||||||
midiparser_qt.o \
|
midiparser_qt.o \
|
||||||
|
midiparser.o \
|
||||||
|
miles_adlib.o \
|
||||||
|
miles_mt32.o \
|
||||||
mixer.o \
|
mixer.o \
|
||||||
mpu401.o \
|
mpu401.o \
|
||||||
musicplugin.o \
|
musicplugin.o \
|
||||||
null.o \
|
null.o \
|
||||||
timestamp.o \
|
timestamp.o \
|
||||||
|
decoders/3do.o \
|
||||||
decoders/aac.o \
|
decoders/aac.o \
|
||||||
decoders/adpcm.o \
|
decoders/adpcm.o \
|
||||||
decoders/aiff.o \
|
decoders/aiff.o \
|
||||||
|
@ -27,7 +31,6 @@ MODULE_OBJS := \
|
||||||
decoders/wave.o \
|
decoders/wave.o \
|
||||||
decoders/wma.o \
|
decoders/wma.o \
|
||||||
decoders/xa.o \
|
decoders/xa.o \
|
||||||
softsynth/adlib.o \
|
|
||||||
softsynth/cms.o \
|
softsynth/cms.o \
|
||||||
softsynth/opl/dbopl.o \
|
softsynth/opl/dbopl.o \
|
||||||
softsynth/opl/dosbox.o \
|
softsynth/opl/dosbox.o \
|
||||||
|
@ -35,6 +38,11 @@ MODULE_OBJS := \
|
||||||
softsynth/fluidsynth.o \
|
softsynth/fluidsynth.o \
|
||||||
softsynth/mt32.o
|
softsynth/mt32.o
|
||||||
|
|
||||||
|
ifdef USE_ALSA
|
||||||
|
MODULE_OBJS += \
|
||||||
|
alsa_opl.o
|
||||||
|
endif
|
||||||
|
|
||||||
ifndef USE_ARM_SOUND_ASM
|
ifndef USE_ARM_SOUND_ASM
|
||||||
MODULE_OBJS += \
|
MODULE_OBJS += \
|
||||||
rate.o
|
rate.o
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "dosbox.h"
|
#include "dosbox.h"
|
||||||
#include "dbopl.h"
|
#include "dbopl.h"
|
||||||
|
|
||||||
|
#include "audio/mixer.h"
|
||||||
#include "common/system.h"
|
#include "common/system.h"
|
||||||
#include "common/scummsys.h"
|
#include "common/scummsys.h"
|
||||||
#include "common/util.h"
|
#include "common/util.h"
|
||||||
|
@ -148,6 +149,7 @@ OPL::OPL(Config::OplType type) : _type(type), _rate(0), _emulator(0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
OPL::~OPL() {
|
OPL::~OPL() {
|
||||||
|
stop();
|
||||||
free();
|
free();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +158,7 @@ void OPL::free() {
|
||||||
_emulator = 0;
|
_emulator = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OPL::init(int rate) {
|
bool OPL::init() {
|
||||||
free();
|
free();
|
||||||
|
|
||||||
memset(&_reg, 0, sizeof(_reg));
|
memset(&_reg, 0, sizeof(_reg));
|
||||||
|
@ -167,19 +169,19 @@ bool OPL::init(int rate) {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
DBOPL::InitTables();
|
DBOPL::InitTables();
|
||||||
_emulator->Setup(rate);
|
_rate = g_system->getMixer()->getOutputRate();
|
||||||
|
_emulator->Setup(_rate);
|
||||||
|
|
||||||
if (_type == Config::kDualOpl2) {
|
if (_type == Config::kDualOpl2) {
|
||||||
// Setup opl3 mode in the hander
|
// Setup opl3 mode in the hander
|
||||||
_emulator->WriteReg(0x105, 1);
|
_emulator->WriteReg(0x105, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
_rate = rate;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OPL::reset() {
|
void OPL::reset() {
|
||||||
init(_rate);
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OPL::write(int port, int val) {
|
void OPL::write(int port, int val) {
|
||||||
|
@ -307,7 +309,7 @@ void OPL::dualWrite(uint8 index, uint8 reg, uint8 val) {
|
||||||
_emulator->WriteReg(fullReg, 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,
|
// For stereo OPL cards, we divide the sample count by 2,
|
||||||
// to match stereo AudioStream behavior.
|
// to match stereo AudioStream behavior.
|
||||||
if (_type != Config::kOpl2)
|
if (_type != Config::kOpl2)
|
||||||
|
|
|
@ -69,7 +69,7 @@ namespace DBOPL {
|
||||||
struct Chip;
|
struct Chip;
|
||||||
} // end of namespace DBOPL
|
} // end of namespace DBOPL
|
||||||
|
|
||||||
class OPL : public ::OPL::OPL {
|
class OPL : public ::OPL::EmulatedOPL {
|
||||||
private:
|
private:
|
||||||
Config::OplType _type;
|
Config::OplType _type;
|
||||||
uint _rate;
|
uint _rate;
|
||||||
|
@ -87,7 +87,7 @@ public:
|
||||||
OPL(Config::OplType type);
|
OPL(Config::OplType type);
|
||||||
~OPL();
|
~OPL();
|
||||||
|
|
||||||
bool init(int rate);
|
bool init();
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
void write(int a, int v);
|
void write(int a, int v);
|
||||||
|
@ -95,8 +95,10 @@ public:
|
||||||
|
|
||||||
void writeReg(int r, int v);
|
void writeReg(int r, int v);
|
||||||
|
|
||||||
void readBuffer(int16 *buffer, int length);
|
|
||||||
bool isStereo() const { return _type != Config::kOpl2; }
|
bool isStereo() const { return _type != Config::kOpl2; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void generateSamples(int16 *buffer, int length);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // End of namespace DOSBox
|
} // End of namespace DOSBox
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
|
|
||||||
#include "mame.h"
|
#include "mame.h"
|
||||||
|
|
||||||
|
#include "audio/mixer.h"
|
||||||
|
#include "common/system.h"
|
||||||
#include "common/textconsole.h"
|
#include "common/textconsole.h"
|
||||||
#include "common/util.h"
|
#include "common/util.h"
|
||||||
|
|
||||||
|
@ -46,15 +48,19 @@ namespace OPL {
|
||||||
namespace MAME {
|
namespace MAME {
|
||||||
|
|
||||||
OPL::~OPL() {
|
OPL::~OPL() {
|
||||||
|
stop();
|
||||||
MAME::OPLDestroy(_opl);
|
MAME::OPLDestroy(_opl);
|
||||||
_opl = 0;
|
_opl = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OPL::init(int rate) {
|
bool OPL::init() {
|
||||||
if (_opl)
|
if (_opl) {
|
||||||
|
stopCallbacks();
|
||||||
MAME::OPLDestroy(_opl);
|
MAME::OPLDestroy(_opl);
|
||||||
|
}
|
||||||
|
|
||||||
|
_opl = MAME::makeAdLibOPL(g_system->getMixer()->getOutputRate());
|
||||||
|
|
||||||
_opl = MAME::makeAdLibOPL(rate);
|
|
||||||
return (_opl != 0);
|
return (_opl != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +80,7 @@ void OPL::writeReg(int r, int v) {
|
||||||
MAME::OPLWriteReg(_opl, r, 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);
|
MAME::YM3812UpdateOne(_opl, buffer, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -174,14 +174,14 @@ void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length);
|
||||||
FM_OPL *makeAdLibOPL(int rate);
|
FM_OPL *makeAdLibOPL(int rate);
|
||||||
|
|
||||||
// OPL API implementation
|
// OPL API implementation
|
||||||
class OPL : public ::OPL::OPL {
|
class OPL : public ::OPL::EmulatedOPL {
|
||||||
private:
|
private:
|
||||||
FM_OPL *_opl;
|
FM_OPL *_opl;
|
||||||
public:
|
public:
|
||||||
OPL() : _opl(0) {}
|
OPL() : _opl(0) {}
|
||||||
~OPL();
|
~OPL();
|
||||||
|
|
||||||
bool init(int rate);
|
bool init();
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
void write(int a, int v);
|
void write(int a, int v);
|
||||||
|
@ -189,8 +189,10 @@ public:
|
||||||
|
|
||||||
void writeReg(int r, int v);
|
void writeReg(int r, int v);
|
||||||
|
|
||||||
void readBuffer(int16 *buffer, int length);
|
|
||||||
bool isStereo() const { return false; }
|
bool isStereo() const { return false; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void generateSamples(int16 *buffer, int length);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // End of namespace MAME
|
} // End of namespace MAME
|
||||||
|
|
2318
b/audio/adlib.cpp
Normal file
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
349
b/audio/alsa_opl.cpp
Normal 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
343
b/audio/decoders/3do.cpp
Normal 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
158
b/audio/decoders/3do.h
Normal 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
|
215
common/dcl.cpp
215
common/dcl.cpp
|
@ -30,17 +30,15 @@ namespace Common {
|
||||||
|
|
||||||
class DecompressorDCL {
|
class DecompressorDCL {
|
||||||
public:
|
public:
|
||||||
bool unpack(ReadStream *src, byte *dest, uint32 nPacked, uint32 nUnpacked);
|
bool unpack(SeekableReadStream *sourceStream, WriteStream *targetStream, uint32 targetSize, bool targetFixedSize);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* Initialize decompressor.
|
* Initialize decompressor.
|
||||||
* @param src source stream to read from
|
* @param sourceStream source stream to read from
|
||||||
* @param dest destination stream to write to
|
* @param targetStream target memory stream to write to
|
||||||
* @param nPacked size of packed data
|
|
||||||
* @param nUnpacked size of unpacked data
|
|
||||||
*/
|
*/
|
||||||
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
|
* Get a number of bits from _src stream, starting with the least
|
||||||
|
@ -68,34 +66,36 @@ protected:
|
||||||
|
|
||||||
uint32 _dwBits; ///< bits buffer
|
uint32 _dwBits; ///< bits buffer
|
||||||
byte _nBits; ///< number of unread bits in _dwBits
|
byte _nBits; ///< number of unread bits in _dwBits
|
||||||
uint32 _szPacked; ///< size of the compressed data
|
uint32 _sourceSize; ///< size of the source stream
|
||||||
uint32 _szUnpacked; ///< size of the decompressed data
|
uint32 _targetSize; ///< size of the target stream (if fixed)
|
||||||
uint32 _dwRead; ///< number of bytes read from _src
|
bool _targetFixedSize; ///< if target stream is fixed size or dynamic size
|
||||||
uint32 _dwWrote; ///< number of bytes written to _dest
|
uint32 _bytesRead; ///< number of bytes read from _sourceStream
|
||||||
ReadStream *_src;
|
uint32 _bytesWritten; ///< number of bytes written to _targetStream
|
||||||
byte *_dest;
|
SeekableReadStream *_sourceStream;
|
||||||
|
WriteStream *_targetStream;
|
||||||
};
|
};
|
||||||
|
|
||||||
void DecompressorDCL::init(ReadStream *src, byte *dest, uint32 nPacked, uint32 nUnpacked) {
|
void DecompressorDCL::init(SeekableReadStream *sourceStream, WriteStream *targetStream, uint32 targetSize, bool targetFixedSize) {
|
||||||
_src = src;
|
_sourceStream = sourceStream;
|
||||||
_dest = dest;
|
_targetStream = targetStream;
|
||||||
_szPacked = nPacked;
|
_sourceSize = sourceStream->size();
|
||||||
_szUnpacked = nUnpacked;
|
_targetSize = targetSize;
|
||||||
|
_targetFixedSize = targetFixedSize;
|
||||||
_nBits = 0;
|
_nBits = 0;
|
||||||
_dwRead = _dwWrote = 0;
|
_bytesRead = _bytesWritten = 0;
|
||||||
_dwBits = 0;
|
_dwBits = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecompressorDCL::fetchBitsLSB() {
|
void DecompressorDCL::fetchBitsLSB() {
|
||||||
while (_nBits <= 24) {
|
while (_nBits <= 24) {
|
||||||
_dwBits |= ((uint32)_src->readByte()) << _nBits;
|
_dwBits |= ((uint32)_sourceStream->readByte()) << _nBits;
|
||||||
_nBits += 8;
|
_nBits += 8;
|
||||||
_dwRead++;
|
_bytesRead++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 DecompressorDCL::getBitsLSB(int n) {
|
uint32 DecompressorDCL::getBitsLSB(int n) {
|
||||||
// fetching more data to buffer if needed
|
// Fetching more data to buffer if needed
|
||||||
if (_nBits < n)
|
if (_nBits < n)
|
||||||
fetchBitsLSB();
|
fetchBitsLSB();
|
||||||
uint32 ret = (_dwBits & ~((~0) << n));
|
uint32 ret = (_dwBits & ~((~0) << n));
|
||||||
|
@ -109,7 +109,8 @@ byte DecompressorDCL::getByteLSB() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecompressorDCL::putByte(byte b) {
|
void DecompressorDCL::putByte(byte b) {
|
||||||
_dest[_dwWrote++] = b;
|
_targetStream->writeByte(b);
|
||||||
|
_bytesWritten++;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define HUFFMAN_LEAF 0x40000000
|
#define HUFFMAN_LEAF 0x40000000
|
||||||
|
@ -331,97 +332,189 @@ int DecompressorDCL::huffman_lookup(const int *tree) {
|
||||||
#define DCL_BINARY_MODE 0
|
#define DCL_BINARY_MODE 0
|
||||||
#define DCL_ASCII_MODE 1
|
#define DCL_ASCII_MODE 1
|
||||||
|
|
||||||
bool DecompressorDCL::unpack(ReadStream *src, byte *dest, uint32 nPacked, uint32 nUnpacked) {
|
#define MIDI_SETUP_BUNDLE_FILE_MAXIMUM_DICTIONARY_SIZE 4096
|
||||||
init(src, dest, nPacked, nUnpacked);
|
|
||||||
|
|
||||||
|
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;
|
int value;
|
||||||
uint32 val_distance, val_length;
|
uint16 tokenOffset = 0;
|
||||||
|
uint16 tokenLength = 0;
|
||||||
|
|
||||||
int mode = getByteLSB();
|
init(sourceStream, targetStream, targetSize, targetFixedSize);
|
||||||
int length_param = getByteLSB();
|
|
||||||
|
byte mode = getByteLSB();
|
||||||
|
byte dictionaryType = getByteLSB();
|
||||||
|
|
||||||
if (mode != DCL_BINARY_MODE && mode != DCL_ASCII_MODE) {
|
if (mode != DCL_BINARY_MODE && mode != DCL_ASCII_MODE) {
|
||||||
warning("DCL-INFLATE: Error: Encountered mode %02x, expected 00 or 01", mode);
|
warning("DCL-INFLATE: Error: Encountered mode %02x, expected 00 or 01", mode);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length_param < 3 || length_param > 6)
|
// TODO: original code supported 3 as well???
|
||||||
warning("Unexpected length_param value %d (expected in [3,6])", length_param);
|
// 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
|
if (getBitsLSB(1)) { // (length,distance) pair
|
||||||
value = huffman_lookup(length_tree);
|
value = huffman_lookup(length_tree);
|
||||||
|
|
||||||
if (value < 8)
|
if (value < 8)
|
||||||
val_length = value + 2;
|
tokenLength = value + 2;
|
||||||
else
|
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, " | ");
|
debug(8, " | ");
|
||||||
|
|
||||||
value = huffman_lookup(distance_tree);
|
value = huffman_lookup(distance_tree);
|
||||||
|
|
||||||
if (val_length == 2)
|
if (tokenLength == 2)
|
||||||
val_distance = (value << 2) | getBitsLSB(2);
|
tokenOffset = (value << 2) | getBitsLSB(2);
|
||||||
else
|
else
|
||||||
val_distance = (value << length_param) | getBitsLSB(length_param);
|
tokenOffset = (value << dictionaryType) | getBitsLSB(dictionaryType);
|
||||||
val_distance ++;
|
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)",
|
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;
|
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)",
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (val_length) {
|
uint16 dictionaryBaseIndex = (dictionaryPos - tokenOffset) & dictionaryMask;
|
||||||
uint32 copy_length = (val_length > val_distance) ? val_distance : val_length;
|
uint16 dictionaryIndex = dictionaryBaseIndex;
|
||||||
assert(val_distance >= copy_length);
|
uint16 dictionaryNextIndex = dictionaryPos;
|
||||||
uint32 pos = _dwWrote - val_distance;
|
|
||||||
for (uint32 i = 0; i < copy_length; i++)
|
|
||||||
putByte(dest[pos + i]);
|
|
||||||
|
|
||||||
for (uint32 i = 0; i < copy_length; i++)
|
while (tokenLength) {
|
||||||
debug(9, "\33[32;31m%02x\33[37;37m ", dest[pos + i]);
|
// Write byte from dictionary
|
||||||
debug(9, "\n");
|
putByte(dictionary[dictionaryIndex]);
|
||||||
|
debug(9, "\33[32;31m%02x\33[37;37m ", dictionary[dictionaryIndex]);
|
||||||
|
|
||||||
val_length -= copy_length;
|
dictionary[dictionaryNextIndex] = dictionary[dictionaryIndex];
|
||||||
val_distance += copy_length;
|
|
||||||
|
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
|
} else { // Copy byte verbatim
|
||||||
value = (mode == DCL_ASCII_MODE) ? huffman_lookup(ascii_tree) : getByteLSB();
|
value = (mode == DCL_ASCII_MODE) ? huffman_lookup(ascii_tree) : getByteLSB();
|
||||||
putByte(value);
|
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);
|
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 decompressDCL(ReadStream *src, byte *dest, uint32 packedSize, uint32 unpackedSize) {
|
||||||
|
bool success = false;
|
||||||
|
DecompressorDCL dcl;
|
||||||
|
|
||||||
if (!src || !dest)
|
if (!src || !dest)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
DecompressorDCL dcl;
|
byte *sourceBufferPtr = (byte *)malloc(packedSize);
|
||||||
return dcl.unpack(src, dest, packedSize, unpackedSize);
|
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) {
|
SeekableReadStream *decompressDCL(SeekableReadStream *sourceStream, uint32 packedSize, uint32 unpackedSize) {
|
||||||
byte *data = (byte *)malloc(unpackedSize);
|
bool success = false;
|
||||||
|
byte *targetPtr = nullptr;
|
||||||
|
Common::MemoryWriteStream *targetStream;
|
||||||
|
DecompressorDCL dcl;
|
||||||
|
|
||||||
if (decompressDCL(src, data, packedSize, unpackedSize))
|
targetPtr = (byte *)malloc(unpackedSize);
|
||||||
return new MemoryReadStream(data, unpackedSize, DisposeAfterUse::YES);
|
if (!targetPtr)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
free(data);
|
targetStream = new MemoryWriteStream(targetPtr, unpackedSize);
|
||||||
return 0;
|
|
||||||
|
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
|
} // End of namespace Common
|
||||||
|
|
17
common/dcl.h
17
common/dcl.h
|
@ -22,7 +22,8 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file
|
* @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
|
* - mohawk
|
||||||
* - sci
|
* - sci
|
||||||
*/
|
*/
|
||||||
|
@ -38,16 +39,22 @@ class ReadStream;
|
||||||
class SeekableReadStream;
|
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.
|
* 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.
|
* 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
|
} // End of namespace Common
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,10 @@ FFT::FFT(int bits, int inverse) : _bits(bits), _inverse(inverse) {
|
||||||
}
|
}
|
||||||
|
|
||||||
FFT::~FFT() {
|
FFT::~FFT() {
|
||||||
|
for (int i = 0; i < ARRAYSIZE(_cosTables); i++) {
|
||||||
|
delete _cosTables[i];
|
||||||
|
}
|
||||||
|
|
||||||
delete[] _revTab;
|
delete[] _revTab;
|
||||||
delete[] _expTab;
|
delete[] _expTab;
|
||||||
delete[] _tmpBuf;
|
delete[] _tmpBuf;
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//#define ENABLE_XCODE
|
#define ENABLE_XCODE
|
||||||
|
|
||||||
// HACK to allow building with the SDL backend on MinGW
|
// HACK to allow building with the SDL backend on MinGW
|
||||||
// see bug #1800764 "TOOLS: MinGW tools building broken"
|
// 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);
|
setup.defines.splice(setup.defines.begin(), featureDefines);
|
||||||
|
|
||||||
// Windows only has support for the SDL backend, so we hardcode it here (along with winmm)
|
// Windows only has support for the SDL backend, so we hardcode it here (along with winmm)
|
||||||
|
if (projectType != kProjectXcode) {
|
||||||
setup.defines.push_back("WIN32");
|
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");
|
setup.defines.push_back("SDL_BACKEND");
|
||||||
if (!useSDL2) {
|
if (!useSDL2) {
|
||||||
cout << "\nLinking to SDL 1.2\n\n";
|
cout << "\nLinking to SDL 1.2\n\n";
|
||||||
|
@ -410,7 +415,6 @@ int main(int argc, char *argv[]) {
|
||||||
globalWarnings.push_back("-Wwrite-strings");
|
globalWarnings.push_back("-Wwrite-strings");
|
||||||
// The following are not warnings at all... We should consider adding them to
|
// The following are not warnings at all... We should consider adding them to
|
||||||
// a different list of parameters.
|
// a different list of parameters.
|
||||||
globalWarnings.push_back("-fno-rtti");
|
|
||||||
globalWarnings.push_back("-fno-exceptions");
|
globalWarnings.push_back("-fno-exceptions");
|
||||||
globalWarnings.push_back("-fcheck-new");
|
globalWarnings.push_back("-fcheck-new");
|
||||||
|
|
||||||
|
@ -572,7 +576,7 @@ int main(int argc, char *argv[]) {
|
||||||
globalWarnings.push_back("-fno-exceptions");
|
globalWarnings.push_back("-fno-exceptions");
|
||||||
globalWarnings.push_back("-fcheck-new");
|
globalWarnings.push_back("-fcheck-new");
|
||||||
|
|
||||||
provider = new CreateProjectTool::XCodeProvider(globalWarnings, projectWarnings);
|
provider = new CreateProjectTool::XcodeProvider(globalWarnings, projectWarnings);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1039,7 +1043,7 @@ bool producesObjectFile(const std::string &fileName) {
|
||||||
std::string n, ext;
|
std::string n, ext;
|
||||||
splitFilename(fileName, 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;
|
return true;
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
@ -1279,8 +1283,9 @@ void ProjectProvider::createProject(BuildSetup &setup) {
|
||||||
for (UUIDMap::const_iterator i = _uuidMap.begin(); i != _uuidMap.end(); ++i) {
|
for (UUIDMap::const_iterator i = _uuidMap.begin(); i != _uuidMap.end(); ++i) {
|
||||||
if (i->first == setup.projectName)
|
if (i->first == setup.projectName)
|
||||||
continue;
|
continue;
|
||||||
|
// Retain the files between engines if we're creating a single project
|
||||||
in.clear(); ex.clear();
|
in.clear(); ex.clear();
|
||||||
|
|
||||||
const std::string moduleDir = setup.srcDir + targetFolder + i->first;
|
const std::string moduleDir = setup.srcDir + targetFolder + i->first;
|
||||||
|
|
||||||
createModuleList(moduleDir, setup.defines, setup.testDirs, in, ex);
|
createModuleList(moduleDir, setup.defines, setup.testDirs, in, ex);
|
||||||
|
@ -1290,7 +1295,6 @@ void ProjectProvider::createProject(BuildSetup &setup) {
|
||||||
if (setup.tests) {
|
if (setup.tests) {
|
||||||
// Create the main project file.
|
// Create the main project file.
|
||||||
in.clear(); ex.clear();
|
in.clear(); ex.clear();
|
||||||
|
|
||||||
createModuleList(setup.srcDir + "/backends", setup.defines, setup.testDirs, in, ex);
|
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 + "/backends/platform/sdl", setup.defines, setup.testDirs, in, ex);
|
||||||
createModuleList(setup.srcDir + "/base", 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) {
|
} else if (!setup.devTools) {
|
||||||
// Last but not least create the main project file.
|
// Last but not least create the main project file.
|
||||||
in.clear(); ex.clear();
|
in.clear(); ex.clear();
|
||||||
|
|
||||||
// File list for the Project file
|
// File list for the Project file
|
||||||
createModuleList(setup.srcDir + "/backends", setup.defines, setup.testDirs, in, ex);
|
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 + "/backends/platform/sdl", setup.defines, setup.testDirs, in, ex);
|
||||||
|
|
|
@ -30,6 +30,14 @@ namespace CreateProjectTool {
|
||||||
|
|
||||||
#define DEBUG_XCODE_HASH 0
|
#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) \
|
#define ADD_DEFINE(defines, name) \
|
||||||
defines.push_back(name);
|
defines.push_back(name);
|
||||||
|
|
||||||
|
@ -54,39 +62,172 @@ namespace CreateProjectTool {
|
||||||
#define REMOVE_SETTING(config, key) \
|
#define REMOVE_SETTING(config, key) \
|
||||||
config.settings.erase(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); \
|
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.add(buildFile); \
|
||||||
_buildFile.flags = SettingsSingleItem; \
|
_buildFile.flags = SettingsSingleItem; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ADD_FILE_REFERENCE(name, properties) { \
|
#define ADD_FILE_REFERENCE(id, name, properties) { \
|
||||||
Object *fileRef = new Object(this, name, name, "PBXFileReference", "PBXFileReference", name); \
|
Object *fileRef = new Object(this, id, name, "PBXFileReference", "PBXFileReference", name); \
|
||||||
if (!properties.fileEncoding.empty()) fileRef->addProperty("fileEncoding", properties.fileEncoding, "", SettingsNoValue); \
|
if (!properties.fileEncoding.empty()) fileRef->addProperty("fileEncoding", properties.fileEncoding, "", SettingsNoValue); \
|
||||||
if (!properties.lastKnownFileType.empty()) fileRef->addProperty("lastKnownFileType", properties.lastKnownFileType, "", SettingsNoValue); \
|
if (!properties.lastKnownFileType.empty()) fileRef->addProperty("lastKnownFileType", properties.lastKnownFileType, "", SettingsNoValue|SettingsQuoteVariable); \
|
||||||
if (!properties.fileName.empty()) fileRef->addProperty("name", properties.fileName, "", SettingsNoValue); \
|
if (!properties.fileName.empty()) fileRef->addProperty("name", properties.fileName, "", SettingsNoValue|SettingsQuoteVariable); \
|
||||||
if (!properties.filePath.empty()) fileRef->addProperty("path", properties.filePath, "", SettingsNoValue); \
|
if (!properties.filePath.empty()) fileRef->addProperty("path", properties.filePath, "", SettingsNoValue|SettingsQuoteVariable); \
|
||||||
if (!properties.sourceTree.empty()) fileRef->addProperty("sourceTree", properties.sourceTree, "", SettingsNoValue); \
|
if (!properties.sourceTree.empty()) fileRef->addProperty("sourceTree", properties.sourceTree, "", SettingsNoValue); \
|
||||||
_fileReference.add(fileRef); \
|
_fileReference.add(fileRef); \
|
||||||
_fileReference.flags = SettingsSingleItem; \
|
_fileReference.flags = SettingsSingleItem; \
|
||||||
}
|
}
|
||||||
|
|
||||||
XCodeProvider::XCodeProvider(StringList &global_warnings, std::map<std::string, StringList> &project_warnings, const int version)
|
bool producesObjectFileOnOSX(const std::string &fileName) {
|
||||||
: ProjectProvider(global_warnings, project_warnings, version) {
|
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
|
// Create project folder
|
||||||
std::string workspace = setup.outputDir + '/' + PROJECT_NAME ".xcodeproj";
|
std::string workspace = setup.outputDir + '/' + PROJECT_NAME ".xcodeproj";
|
||||||
createDirectory(workspace);
|
createDirectory(workspace);
|
||||||
|
_projectRoot = setup.srcDir;
|
||||||
|
touchGroupsForPath(_projectRoot);
|
||||||
|
|
||||||
// Setup global objects
|
// Setup global objects
|
||||||
setupDefines(setup);
|
setupDefines(setup);
|
||||||
|
#ifdef ENABLE_IOS
|
||||||
_targets.push_back(PROJECT_DESCRIPTION "-iPhone");
|
_targets.push_back(PROJECT_DESCRIPTION "-iPhone");
|
||||||
|
#endif
|
||||||
_targets.push_back(PROJECT_DESCRIPTION "-OS X");
|
_targets.push_back(PROJECT_DESCRIPTION "-OS X");
|
||||||
|
#ifdef ENABLE_IOS
|
||||||
_targets.push_back(PROJECT_DESCRIPTION "-Simulator");
|
_targets.push_back(PROJECT_DESCRIPTION "-Simulator");
|
||||||
|
#endif
|
||||||
setupCopyFilesBuildPhase();
|
setupCopyFilesBuildPhase();
|
||||||
setupFrameworksBuildPhase();
|
setupFrameworksBuildPhase();
|
||||||
setupNativeTarget();
|
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
|
// 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)
|
// (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
|
// This needs to be done at the end when all build files have been accounted for
|
||||||
setupSourcesBuildPhase();
|
setupSourcesBuildPhase();
|
||||||
|
|
||||||
|
@ -105,7 +246,7 @@ void XCodeProvider::createOtherBuildFiles(const BuildSetup &setup) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store information about a project here, for use at the end
|
// 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) {
|
const StringList &includeList, const StringList &excludeList) {
|
||||||
std::string modulePath;
|
std::string modulePath;
|
||||||
if (!moduleDir.compare(0, setup.srcDir.size(), setup.srcDir)) {
|
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
|
// 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());
|
std::ofstream project((setup.outputDir + '/' + PROJECT_NAME ".xcodeproj" + '/' + "project.pbxproj").c_str());
|
||||||
if (!project)
|
if (!project)
|
||||||
error("Could not open \"" + setup.outputDir + '/' + PROJECT_NAME ".xcodeproj" + '/' + "project.pbxproj\" for writing");
|
error("Could not open \"" + setup.outputDir + '/' + PROJECT_NAME ".xcodeproj" + '/' + "project.pbxproj\" for writing");
|
||||||
|
@ -164,92 +305,93 @@ void XCodeProvider::ouputMainProjectFile(const BuildSetup &setup) {
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// Files
|
// 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) {
|
const StringList &duplicate, const std::string &objPrefix, const std::string &filePrefix) {
|
||||||
|
|
||||||
// Add comments for shared lists
|
// Ensure that top-level groups are generated for i.e. engines/
|
||||||
_buildFile.comment = "PBXBuildFile";
|
Group *group = touchGroupsForPath(filePrefix);
|
||||||
_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;
|
|
||||||
for (FileNode::NodeList::const_iterator i = dir.children.begin(); i != dir.children.end(); ++i) {
|
for (FileNode::NodeList::const_iterator i = dir.children.begin(); i != dir.children.end(); ++i) {
|
||||||
const FileNode *node = *i;
|
const FileNode *node = *i;
|
||||||
|
|
||||||
std::string id = "FileReference_" + node->name;
|
// Iff it is a file, then add (build) file references. Since we're using Groups and not File References
|
||||||
FileProperty property = FileProperty(node->name, node->name, node->name, "<group>");
|
// for folders, we shouldn't add folders as file references, obviously.
|
||||||
|
if (node->children.empty()) {
|
||||||
ADD_SETTING_ORDER_NOVALUE(children, getHash(id), node->name, order++);
|
group->addChildFile(node->name);
|
||||||
ADD_BUILD_FILE(id, node->name, node->name + " in Sources");
|
}
|
||||||
ADD_FILE_REFERENCE(node->name, property);
|
|
||||||
|
|
||||||
// Process child nodes
|
// Process child nodes
|
||||||
if (!node->children.empty())
|
if (!node->children.empty())
|
||||||
writeFileListToProject(*node, projectFile, indentation + 1, duplicate, objPrefix + node->name + '_', filePrefix + node->name + '/');
|
writeFileListToProject(*node, projectFile, indentation + 1, duplicate, objPrefix + node->name + '_', filePrefix + node->name + '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
group->properties["children"] = children;
|
|
||||||
|
|
||||||
_groups.add(group);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// Setup functions
|
// Setup functions
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
void XCodeProvider::setupCopyFilesBuildPhase() {
|
void XcodeProvider::setupCopyFilesBuildPhase() {
|
||||||
// Nothing to do here
|
// 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.
|
* Sets up the frameworks build phase.
|
||||||
*
|
*
|
||||||
* (each native target has different build rules)
|
* (each native target has different build rules)
|
||||||
*/
|
*/
|
||||||
void XCodeProvider::setupFrameworksBuildPhase() {
|
void XcodeProvider::setupFrameworksBuildPhase() {
|
||||||
_frameworksBuildPhase.comment = "PBXFrameworksBuildPhase";
|
_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
|
// Setup framework file properties
|
||||||
std::map<std::string, FileProperty> properties;
|
std::map<std::string, FileProperty> properties;
|
||||||
|
int fwOrder = 0;
|
||||||
// Frameworks
|
// Frameworks
|
||||||
properties["ApplicationServices.framework"] = FileProperty("wrapper.framework", "ApplicationServices.framework", "System/Library/Frameworks/ApplicationServices.framework", "SDKROOT");
|
DEF_SYSFRAMEWORK("ApplicationServices");
|
||||||
properties["AudioToolbox.framework"] = FileProperty("wrapper.framework", "AudioToolbox.framework", "System/Library/Frameworks/AudioToolbox.framework", "SDKROOT");
|
DEF_SYSFRAMEWORK("AudioToolbox");
|
||||||
properties["AudioUnit.framework"] = FileProperty("wrapper.framework", "AudioUnit.framework", "System/Library/Frameworks/AudioUnit.framework", "SDKROOT");
|
DEF_SYSFRAMEWORK("AudioUnit");
|
||||||
properties["Carbon.framework"] = FileProperty("wrapper.framework", "Carbon.framework", "System/Library/Frameworks/Carbon.framework", "SDKROOT");
|
DEF_SYSFRAMEWORK("Carbon");
|
||||||
properties["Cocoa.framework"] = FileProperty("wrapper.framework", "Cocoa.framework", "System/Library/Frameworks/Cocoa.framework", "SDKROOT");
|
DEF_SYSFRAMEWORK("Cocoa");
|
||||||
properties["CoreAudio.framework"] = FileProperty("wrapper.framework", "CoreAudio.framework", "System/Library/Frameworks/CoreAudio.framework", "SDKROOT");
|
DEF_SYSFRAMEWORK("CoreAudio");
|
||||||
properties["CoreFoundation.framework"] = FileProperty("wrapper.framework", "CoreFoundation.framework", "System/Library/Frameworks/CoreFoundation.framework", "SDKROOT");
|
DEF_SYSFRAMEWORK("CoreFoundation");
|
||||||
properties["CoreMIDI.framework"] = FileProperty("wrapper.framework", "CoreMIDI.framework", "System/Library/Frameworks/CoreMIDI.framework", "SDKROOT");
|
DEF_SYSFRAMEWORK("CoreMIDI");
|
||||||
properties["Foundation.framework"] = FileProperty("wrapper.framework", "Foundation.framework", "System/Library/Frameworks/Foundation.framework", "SDKROOT");
|
DEF_SYSFRAMEWORK("Foundation");
|
||||||
properties["IOKit.framework"] = FileProperty("wrapper.framework", "IOKit.framework", "System/Library/Frameworks/IOKit.framework", "SDKROOT");
|
DEF_SYSFRAMEWORK("IOKit");
|
||||||
properties["OpenGLES.framework"] = FileProperty("wrapper.framework", "OpenGLES.framework", "System/Library/Frameworks/OpenGLES.framework", "SDKROOT");
|
DEF_SYSFRAMEWORK("OpenGLES");
|
||||||
properties["QuartzCore.framework"] = FileProperty("wrapper.framework", "QuartzCore.framework", "System/Library/Frameworks/QuartzCore.framework", "SDKROOT");
|
DEF_SYSFRAMEWORK("QuartzCore");
|
||||||
properties["QuickTime.framework"] = FileProperty("wrapper.framework", "QuickTime.framework", "System/Library/Frameworks/QuickTime.framework", "SDKROOT");
|
DEF_SYSFRAMEWORK("QuickTime");
|
||||||
properties["UIKit.framework"] = FileProperty("wrapper.framework", "UIKit.framework", "System/Library/Frameworks/UIKit.framework", "SDKROOT");
|
DEF_SYSFRAMEWORK("UIKit");
|
||||||
|
// Optionals:
|
||||||
|
DEF_SYSFRAMEWORK("OpenGL");
|
||||||
|
|
||||||
// Local libraries
|
// Local libraries
|
||||||
properties["libFLAC.a"] = FileProperty("archive.ar", "libFLAC.a", "lib/libFLAC.a", "\"<group>\"");
|
DEF_LOCALLIB_STATIC("libFLAC");
|
||||||
properties["libmad.a"] = FileProperty("archive.ar", "libmad.a", "lib/libmad.a", "\"<group>\"");
|
DEF_LOCALLIB_STATIC("libmad");
|
||||||
//properties["libmpeg2.a"] = FileProperty("archive.ar", "libmpeg2.a", "lib/libmpeg2.a", "\"<group>\"");
|
DEF_LOCALLIB_STATIC("libvorbisidec");
|
||||||
properties["libvorbisidec.a"] = FileProperty("archive.ar", "libvorbisidec.a", "lib/libvorbisidec.a", "\"<group>\"");
|
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
|
// 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("buildActionMask", "2147483647", "", SettingsNoValue);
|
||||||
framework_iPhone->addProperty("runOnlyForDeploymentPostprocessing", "0", "", SettingsNoValue);
|
framework_iPhone->addProperty("runOnlyForDeploymentPostprocessing", "0", "", SettingsNoValue);
|
||||||
|
@ -272,23 +414,22 @@ void XCodeProvider::setupFrameworksBuildPhase() {
|
||||||
frameworks_iPhone.push_back("libvorbisidec.a");
|
frameworks_iPhone.push_back("libvorbisidec.a");
|
||||||
frameworks_iPhone.push_back("OpenGLES.framework");
|
frameworks_iPhone.push_back("OpenGLES.framework");
|
||||||
|
|
||||||
int order = 0;
|
|
||||||
for (ValueList::iterator framework = frameworks_iPhone.begin(); framework != frameworks_iPhone.end(); framework++) {
|
for (ValueList::iterator framework = frameworks_iPhone.begin(); framework != frameworks_iPhone.end(); framework++) {
|
||||||
std::string id = "Frameworks_" + *framework + "_iphone";
|
std::string id = "Frameworks_" + *framework + "_iphone";
|
||||||
std::string comment = *framework + " in Frameworks";
|
std::string comment = *framework + " in Frameworks";
|
||||||
|
|
||||||
ADD_SETTING_ORDER_NOVALUE(iPhone_files, getHash(id), comment, order++);
|
ADD_SETTING_ORDER_NOVALUE(iPhone_files, getHash(id), comment, order++);
|
||||||
ADD_BUILD_FILE(id, *framework, comment);
|
ADD_BUILD_FILE(id, *framework, getHash(*framework), comment);
|
||||||
ADD_FILE_REFERENCE(*framework, properties[*framework]);
|
ADD_FILE_REFERENCE(*framework, *framework, properties[*framework]);
|
||||||
}
|
}
|
||||||
|
|
||||||
framework_iPhone->properties["files"] = iPhone_files;
|
framework_iPhone->properties["files"] = iPhone_files;
|
||||||
|
|
||||||
_frameworksBuildPhase.add(framework_iPhone);
|
_frameworksBuildPhase.add(framework_iPhone);
|
||||||
|
#endif
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// ScummVM-OS X
|
// 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("buildActionMask", "2147483647", "", SettingsNoValue);
|
||||||
framework_OSX->addProperty("runOnlyForDeploymentPostprocessing", "0", "", SettingsNoValue);
|
framework_OSX->addProperty("runOnlyForDeploymentPostprocessing", "0", "", SettingsNoValue);
|
||||||
|
@ -311,6 +452,8 @@ void XCodeProvider::setupFrameworksBuildPhase() {
|
||||||
frameworks_osx.push_back("IOKit.framework");
|
frameworks_osx.push_back("IOKit.framework");
|
||||||
frameworks_osx.push_back("Cocoa.framework");
|
frameworks_osx.push_back("Cocoa.framework");
|
||||||
frameworks_osx.push_back("AudioUnit.framework");
|
frameworks_osx.push_back("AudioUnit.framework");
|
||||||
|
// Optionals:
|
||||||
|
frameworks_osx.push_back("OpenGL.framework");
|
||||||
|
|
||||||
order = 0;
|
order = 0;
|
||||||
for (ValueList::iterator framework = frameworks_osx.begin(); framework != frameworks_osx.end(); framework++) {
|
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";
|
std::string comment = *framework + " in Frameworks";
|
||||||
|
|
||||||
ADD_SETTING_ORDER_NOVALUE(osx_files, getHash(id), comment, order++);
|
ADD_SETTING_ORDER_NOVALUE(osx_files, getHash(id), comment, order++);
|
||||||
ADD_BUILD_FILE(id, *framework, comment);
|
ADD_BUILD_FILE(id, *framework, getHash(*framework), comment);
|
||||||
ADD_FILE_REFERENCE(*framework, properties[*framework]);
|
ADD_FILE_REFERENCE(*framework, *framework, properties[*framework]);
|
||||||
}
|
}
|
||||||
|
|
||||||
framework_OSX->properties["files"] = osx_files;
|
framework_OSX->properties["files"] = osx_files;
|
||||||
|
|
||||||
_frameworksBuildPhase.add(framework_OSX);
|
_frameworksBuildPhase.add(framework_OSX);
|
||||||
|
#ifdef ENABLE_IOS
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// Simulator
|
// 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("buildActionMask", "2147483647", "", SettingsNoValue);
|
||||||
framework_simulator->addProperty("runOnlyForDeploymentPostprocessing", "0", "", SettingsNoValue);
|
framework_simulator->addProperty("runOnlyForDeploymentPostprocessing", "0", "", SettingsNoValue);
|
||||||
|
@ -353,20 +496,28 @@ void XCodeProvider::setupFrameworksBuildPhase() {
|
||||||
std::string comment = *framework + " in Frameworks";
|
std::string comment = *framework + " in Frameworks";
|
||||||
|
|
||||||
ADD_SETTING_ORDER_NOVALUE(simulator_files, getHash(id), comment, order++);
|
ADD_SETTING_ORDER_NOVALUE(simulator_files, getHash(id), comment, order++);
|
||||||
ADD_BUILD_FILE(id, *framework, comment);
|
ADD_BUILD_FILE(id, *framework, getHash(*framework), comment);
|
||||||
ADD_FILE_REFERENCE(*framework, properties[*framework]);
|
ADD_FILE_REFERENCE(*framework, *framework, properties[*framework]);
|
||||||
}
|
}
|
||||||
|
|
||||||
framework_simulator->properties["files"] = simulator_files;
|
framework_simulator->properties["files"] = simulator_files;
|
||||||
|
|
||||||
_frameworksBuildPhase.add(framework_simulator);
|
_frameworksBuildPhase.add(framework_simulator);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void XCodeProvider::setupNativeTarget() {
|
void XcodeProvider::setupNativeTarget() {
|
||||||
_nativeTarget.comment = "PBXNativeTarget";
|
_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
|
// Output native target section
|
||||||
for (unsigned int i = 0; i < _targets.size(); i++) {
|
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]);
|
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);
|
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("name", _targets[i], "", SettingsNoValue|SettingsQuoteVariable);
|
||||||
target->addProperty("productName", PROJECT_NAME, "", SettingsNoValue);
|
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("productReference", getHash("PBXFileReference_" PROJECT_DESCRIPTION ".app_" + _targets[i]), PROJECT_DESCRIPTION ".app", SettingsNoValue);
|
||||||
target->addProperty("productType", "com.apple.product-type.application", "", SettingsNoValue|SettingsQuoteVariable);
|
target->addProperty("productType", "com.apple.product-type.application", "", SettingsNoValue|SettingsQuoteVariable);
|
||||||
|
|
||||||
_nativeTarget.add(target);
|
_nativeTarget.add(target);
|
||||||
}
|
}
|
||||||
|
_rootSourceGroup->addChildGroup(productsGroup);
|
||||||
|
_groups.add(productsGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
void XCodeProvider::setupProject() {
|
void XcodeProvider::setupProject() {
|
||||||
_project.comment = "PBXProject";
|
_project.comment = "PBXProject";
|
||||||
|
|
||||||
Object *project = new Object(this, "PBXProject", "PBXProject", "PBXProject", "", "Project object");
|
Object *project = new Object(this, "PBXProject", "PBXProject", "PBXProject", "", "Project object");
|
||||||
|
@ -411,22 +566,30 @@ void XCodeProvider::setupProject() {
|
||||||
ADD_SETTING_ORDER_NOVALUE(regions, "German", "", 3);
|
ADD_SETTING_ORDER_NOVALUE(regions, "German", "", 3);
|
||||||
project->properties["knownRegions"] = regions;
|
project->properties["knownRegions"] = regions;
|
||||||
|
|
||||||
project->addProperty("mainGroup", getHash("PBXGroup_CustomTemplate"), "CustomTemplate", SettingsNoValue);
|
project->addProperty("mainGroup", _rootSourceGroup->getHashRef(), "CustomTemplate", SettingsNoValue);
|
||||||
project->addProperty("projectDirPath", "", "", SettingsNoValue|SettingsQuoteVariable);
|
project->addProperty("projectDirPath", _projectRoot, "", SettingsNoValue|SettingsQuoteVariable);
|
||||||
project->addProperty("projectRoot", "", "", SettingsNoValue|SettingsQuoteVariable);
|
project->addProperty("projectRoot", "", "", SettingsNoValue|SettingsQuoteVariable);
|
||||||
|
|
||||||
// List of targets
|
// List of targets
|
||||||
Property targets;
|
Property targets;
|
||||||
targets.flags = SettingsAsList;
|
targets.flags = SettingsAsList;
|
||||||
targets.settings[getHash("PBXNativeTarget_" + _targets[0])] = Setting("", _targets[0], SettingsNoValue, 0, 0);
|
#ifdef ENABLE_IOS
|
||||||
targets.settings[getHash("PBXNativeTarget_" + _targets[1])] = Setting("", _targets[1], SettingsNoValue, 0, 1);
|
targets.settings[getHash("PBXNativeTarget_" + _targets[IOS_TARGET])] = Setting("", _targets[IOS_TARGET], SettingsNoValue, 0, 0);
|
||||||
targets.settings[getHash("PBXNativeTarget_" + _targets[2])] = Setting("", _targets[2], SettingsNoValue, 0, 2);
|
#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;
|
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);
|
_project.add(project);
|
||||||
}
|
}
|
||||||
|
|
||||||
void XCodeProvider::setupResourcesBuildPhase() {
|
void XcodeProvider::setupResourcesBuildPhase() {
|
||||||
_resourcesBuildPhase.comment = "PBXResourcesBuildPhase";
|
_resourcesBuildPhase.comment = "PBXResourcesBuildPhase";
|
||||||
|
|
||||||
// Setup resource file properties
|
// Setup resource file properties
|
||||||
|
@ -483,7 +646,7 @@ void XCodeProvider::setupResourcesBuildPhase() {
|
||||||
ADD_SETTING_ORDER_NOVALUE(files, getHash(id), comment, order++);
|
ADD_SETTING_ORDER_NOVALUE(files, getHash(id), comment, order++);
|
||||||
// TODO Fix crash when adding build file for data
|
// TODO Fix crash when adding build file for data
|
||||||
//ADD_BUILD_FILE(id, *file, comment);
|
//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
|
// Add custom files depending on the target
|
||||||
|
@ -503,12 +666,41 @@ void XCodeProvider::setupResourcesBuildPhase() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void XCodeProvider::setupSourcesBuildPhase() {
|
void XcodeProvider::setupSourcesBuildPhase() {
|
||||||
// TODO
|
_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
|
// Setup all build configurations
|
||||||
void XCodeProvider::setupBuildConfiguration() {
|
void XcodeProvider::setupBuildConfiguration() {
|
||||||
|
|
||||||
_buildConfiguration.comment = "XCBuildConfiguration";
|
_buildConfiguration.comment = "XCBuildConfiguration";
|
||||||
_buildConfiguration.flags = SettingsAsList;
|
_buildConfiguration.flags = SettingsAsList;
|
||||||
|
@ -516,9 +708,9 @@ void XCodeProvider::setupBuildConfiguration() {
|
||||||
///****************************************
|
///****************************************
|
||||||
// * iPhone
|
// * iPhone
|
||||||
// ****************************************/
|
// ****************************************/
|
||||||
|
#ifdef ENABLE_IOS
|
||||||
// Debug
|
// 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;
|
Property iPhone_Debug;
|
||||||
ADD_SETTING_QUOTE(iPhone_Debug, "ARCHS", "$(ARCHS_UNIVERSAL_IPHONE_OS)");
|
ADD_SETTING_QUOTE(iPhone_Debug, "ARCHS", "$(ARCHS_UNIVERSAL_IPHONE_OS)");
|
||||||
ADD_SETTING_QUOTE(iPhone_Debug, "CODE_SIGN_IDENTITY", "iPhone Developer");
|
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_THUMB_SUPPORT", "NO");
|
||||||
ADD_SETTING(iPhone_Debug, "GCC_UNROLL_LOOPS", "YES");
|
ADD_SETTING(iPhone_Debug, "GCC_UNROLL_LOOPS", "YES");
|
||||||
ValueList iPhone_HeaderSearchPaths;
|
ValueList iPhone_HeaderSearchPaths;
|
||||||
iPhone_HeaderSearchPaths.push_back("../../engines/");
|
iPhone_HeaderSearchPaths.push_back("$(SRCROOT)/engines/");
|
||||||
iPhone_HeaderSearchPaths.push_back("../../");
|
iPhone_HeaderSearchPaths.push_back("$(SRCROOT)");
|
||||||
iPhone_HeaderSearchPaths.push_back("include/");
|
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");
|
ADD_SETTING(iPhone_Debug, "INFOPLIST_FILE", "Info.plist");
|
||||||
ValueList iPhone_LibPaths;
|
ValueList iPhone_LibPaths;
|
||||||
iPhone_LibPaths.push_back("$(inherited)");
|
iPhone_LibPaths.push_back("$(inherited)");
|
||||||
|
@ -560,7 +752,7 @@ void XCodeProvider::setupBuildConfiguration() {
|
||||||
iPhone_Debug_Object->properties["buildSettings"] = iPhone_Debug;
|
iPhone_Debug_Object->properties["buildSettings"] = iPhone_Debug;
|
||||||
|
|
||||||
// Release
|
// 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);
|
Property iPhone_Release(iPhone_Debug);
|
||||||
ADD_SETTING(iPhone_Release, "GCC_OPTIMIZATION_LEVEL", "3");
|
ADD_SETTING(iPhone_Release, "GCC_OPTIMIZATION_LEVEL", "3");
|
||||||
ADD_SETTING(iPhone_Release, "COPY_PHASE_STRIP", "YES");
|
ADD_SETTING(iPhone_Release, "COPY_PHASE_STRIP", "YES");
|
||||||
|
@ -572,7 +764,7 @@ void XCodeProvider::setupBuildConfiguration() {
|
||||||
|
|
||||||
_buildConfiguration.add(iPhone_Debug_Object);
|
_buildConfiguration.add(iPhone_Debug_Object);
|
||||||
_buildConfiguration.add(iPhone_Release_Object);
|
_buildConfiguration.add(iPhone_Release_Object);
|
||||||
|
#endif
|
||||||
/****************************************
|
/****************************************
|
||||||
* scummvm
|
* scummvm
|
||||||
****************************************/
|
****************************************/
|
||||||
|
@ -581,13 +773,14 @@ void XCodeProvider::setupBuildConfiguration() {
|
||||||
Object *scummvm_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_NAME "_Debug", PROJECT_NAME, "XCBuildConfiguration", "PBXProject", "Debug");
|
Object *scummvm_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_NAME "_Debug", PROJECT_NAME, "XCBuildConfiguration", "PBXProject", "Debug");
|
||||||
Property scummvm_Debug;
|
Property scummvm_Debug;
|
||||||
ADD_SETTING(scummvm_Debug, "ALWAYS_SEARCH_USER_PATHS", "NO");
|
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, "ARCHS", "$(ARCHS_STANDARD_32_BIT)");
|
||||||
ADD_SETTING_QUOTE(scummvm_Debug, "CODE_SIGN_IDENTITY", "Don't Code Sign");
|
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_VAR(scummvm_Debug, "CODE_SIGN_IDENTITY[sdk=iphoneos*]", "Don't Code Sign");
|
||||||
ADD_SETTING_QUOTE(scummvm_Debug, "FRAMEWORK_SEARCH_PATHS", "");
|
ADD_SETTING_QUOTE(scummvm_Debug, "FRAMEWORK_SEARCH_PATHS", "");
|
||||||
ADD_SETTING(scummvm_Debug, "GCC_C_LANGUAGE_STANDARD", "c99");
|
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_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_INPUT_FILETYPE", "automatic");
|
||||||
ADD_SETTING(scummvm_Debug, "GCC_OPTIMIZATION_LEVEL", "0");
|
ADD_SETTING(scummvm_Debug, "GCC_OPTIMIZATION_LEVEL", "0");
|
||||||
ValueList scummvm_defines(_defines);
|
ValueList scummvm_defines(_defines);
|
||||||
|
@ -601,15 +794,15 @@ void XCodeProvider::setupBuildConfiguration() {
|
||||||
ADD_SETTING(scummvm_Debug, "GCC_WARN_UNUSED_VARIABLE", "YES");
|
ADD_SETTING(scummvm_Debug, "GCC_WARN_UNUSED_VARIABLE", "YES");
|
||||||
ValueList scummvm_HeaderPaths;
|
ValueList scummvm_HeaderPaths;
|
||||||
scummvm_HeaderPaths.push_back("include/");
|
scummvm_HeaderPaths.push_back("include/");
|
||||||
scummvm_HeaderPaths.push_back("../../engines/");
|
scummvm_HeaderPaths.push_back("$(SRCROOT)/engines/");
|
||||||
scummvm_HeaderPaths.push_back("../../");
|
scummvm_HeaderPaths.push_back("$(SRCROOT)");
|
||||||
ADD_SETTING_LIST(scummvm_Debug, "HEADER_SEARCH_PATHS", scummvm_HeaderPaths, SettingsNoQuote|SettingsAsList, 5);
|
ADD_SETTING_LIST(scummvm_Debug, "HEADER_SEARCH_PATHS", scummvm_HeaderPaths, SettingsQuoteVariable|SettingsAsList, 5);
|
||||||
ADD_SETTING_QUOTE(scummvm_Debug, "LIBRARY_SEARCH_PATHS", "");
|
ADD_SETTING_QUOTE(scummvm_Debug, "LIBRARY_SEARCH_PATHS", "");
|
||||||
ADD_SETTING(scummvm_Debug, "ONLY_ACTIVE_ARCH", "YES");
|
ADD_SETTING(scummvm_Debug, "ONLY_ACTIVE_ARCH", "YES");
|
||||||
ADD_SETTING_QUOTE(scummvm_Debug, "OTHER_CFLAGS", "");
|
ADD_SETTING_QUOTE(scummvm_Debug, "OTHER_CFLAGS", "");
|
||||||
ADD_SETTING_QUOTE(scummvm_Debug, "OTHER_LDFLAGS", "-lz");
|
ADD_SETTING_QUOTE(scummvm_Debug, "OTHER_LDFLAGS", "-lz");
|
||||||
ADD_SETTING(scummvm_Debug, "PREBINDING", "NO");
|
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->addProperty("name", "Debug", "", SettingsNoValue);
|
||||||
scummvm_Debug_Object->properties["buildSettings"] = scummvm_Debug;
|
scummvm_Debug_Object->properties["buildSettings"] = scummvm_Debug;
|
||||||
|
@ -633,7 +826,7 @@ void XCodeProvider::setupBuildConfiguration() {
|
||||||
****************************************/
|
****************************************/
|
||||||
|
|
||||||
// Debug
|
// 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;
|
Property scummvmOSX_Debug;
|
||||||
ADD_SETTING_QUOTE(scummvmOSX_Debug, "ARCHS", "$(NATIVE_ARCH)");
|
ADD_SETTING_QUOTE(scummvmOSX_Debug, "ARCHS", "$(NATIVE_ARCH)");
|
||||||
ADD_SETTING(scummvmOSX_Debug, "COMPRESS_PNG_FILES", "NO");
|
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_QUOTE(scummvmOSX_Debug, "FRAMEWORK_SEARCH_PATHS", "");
|
||||||
ADD_SETTING(scummvmOSX_Debug, "GCC_C_LANGUAGE_STANDARD", "c99");
|
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_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_DYNAMIC_NO_PIC", "NO");
|
||||||
ADD_SETTING(scummvmOSX_Debug, "GCC_ENABLE_FIX_AND_CONTINUE", "NO");
|
ADD_SETTING(scummvmOSX_Debug, "GCC_ENABLE_FIX_AND_CONTINUE", "NO");
|
||||||
ADD_SETTING(scummvmOSX_Debug, "GCC_OPTIMIZATION_LEVEL", "0");
|
ADD_SETTING(scummvmOSX_Debug, "GCC_OPTIMIZATION_LEVEL", "0");
|
||||||
|
@ -656,11 +849,12 @@ void XCodeProvider::setupBuildConfiguration() {
|
||||||
ValueList scummvmOSX_HeaderPaths;
|
ValueList scummvmOSX_HeaderPaths;
|
||||||
scummvmOSX_HeaderPaths.push_back("/opt/local/include/SDL");
|
scummvmOSX_HeaderPaths.push_back("/opt/local/include/SDL");
|
||||||
scummvmOSX_HeaderPaths.push_back("/opt/local/include");
|
scummvmOSX_HeaderPaths.push_back("/opt/local/include");
|
||||||
|
scummvmOSX_HeaderPaths.push_back("/opt/local/include/freetype2");
|
||||||
scummvmOSX_HeaderPaths.push_back("include/");
|
scummvmOSX_HeaderPaths.push_back("include/");
|
||||||
scummvmOSX_HeaderPaths.push_back("../../engines/");
|
scummvmOSX_HeaderPaths.push_back("$(SRCROOT)/engines/");
|
||||||
scummvmOSX_HeaderPaths.push_back("../../");
|
scummvmOSX_HeaderPaths.push_back("$(SRCROOT)");
|
||||||
ADD_SETTING_LIST(scummvmOSX_Debug, "HEADER_SEARCH_PATHS", scummvmOSX_HeaderPaths, SettingsNoQuote|SettingsAsList, 5);
|
ADD_SETTING_LIST(scummvmOSX_Debug, "HEADER_SEARCH_PATHS", scummvmOSX_HeaderPaths, SettingsQuoteVariable|SettingsAsList, 5);
|
||||||
ADD_SETTING_QUOTE(scummvmOSX_Debug, "INFOPLIST_FILE", "$(SRCROOT)/../macosx/Info.plist");
|
ADD_SETTING_QUOTE(scummvmOSX_Debug, "INFOPLIST_FILE", "$(SRCROOT)/dists/macosx/Info.plist");
|
||||||
ValueList scummvmOSX_LibPaths;
|
ValueList scummvmOSX_LibPaths;
|
||||||
scummvmOSX_LibPaths.push_back("/sw/lib");
|
scummvmOSX_LibPaths.push_back("/sw/lib");
|
||||||
scummvmOSX_LibPaths.push_back("/opt/local/lib");
|
scummvmOSX_LibPaths.push_back("/opt/local/lib");
|
||||||
|
@ -671,6 +865,10 @@ void XCodeProvider::setupBuildConfiguration() {
|
||||||
ValueList scummvmOSX_LdFlags;
|
ValueList scummvmOSX_LdFlags;
|
||||||
scummvmOSX_LdFlags.push_back("-lSDLmain");
|
scummvmOSX_LdFlags.push_back("-lSDLmain");
|
||||||
scummvmOSX_LdFlags.push_back("-logg");
|
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("-lvorbisfile");
|
||||||
scummvmOSX_LdFlags.push_back("-lvorbis");
|
scummvmOSX_LdFlags.push_back("-lvorbis");
|
||||||
scummvmOSX_LdFlags.push_back("-lmad");
|
scummvmOSX_LdFlags.push_back("-lmad");
|
||||||
|
@ -685,7 +883,7 @@ void XCodeProvider::setupBuildConfiguration() {
|
||||||
scummvmOSX_Debug_Object->properties["buildSettings"] = scummvmOSX_Debug;
|
scummvmOSX_Debug_Object->properties["buildSettings"] = scummvmOSX_Debug;
|
||||||
|
|
||||||
// Release
|
// 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);
|
Property scummvmOSX_Release(scummvmOSX_Debug);
|
||||||
ADD_SETTING(scummvmOSX_Release, "COPY_PHASE_STRIP", "YES");
|
ADD_SETTING(scummvmOSX_Release, "COPY_PHASE_STRIP", "YES");
|
||||||
REMOVE_SETTING(scummvmOSX_Release, "GCC_DYNAMIC_NO_PIC");
|
REMOVE_SETTING(scummvmOSX_Release, "GCC_DYNAMIC_NO_PIC");
|
||||||
|
@ -697,13 +895,13 @@ void XCodeProvider::setupBuildConfiguration() {
|
||||||
|
|
||||||
_buildConfiguration.add(scummvmOSX_Debug_Object);
|
_buildConfiguration.add(scummvmOSX_Debug_Object);
|
||||||
_buildConfiguration.add(scummvmOSX_Release_Object);
|
_buildConfiguration.add(scummvmOSX_Release_Object);
|
||||||
|
#ifdef ENABLE_IOS
|
||||||
/****************************************
|
/****************************************
|
||||||
* ScummVM-Simulator
|
* ScummVM-Simulator
|
||||||
****************************************/
|
****************************************/
|
||||||
|
|
||||||
// Debug
|
// 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);
|
Property scummvmSimulator_Debug(iPhone_Debug);
|
||||||
ADD_SETTING_QUOTE(scummvmSimulator_Debug, "FRAMEWORK_SEARCH_PATHS", "$(inherited)");
|
ADD_SETTING_QUOTE(scummvmSimulator_Debug, "FRAMEWORK_SEARCH_PATHS", "$(inherited)");
|
||||||
ADD_SETTING_LIST(scummvmSimulator_Debug, "GCC_PREPROCESSOR_DEFINITIONS", scummvm_defines, SettingsNoQuote|SettingsAsList, 5);
|
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;
|
scummvmSimulator_Debug_Object->properties["buildSettings"] = scummvmSimulator_Debug;
|
||||||
|
|
||||||
// Release
|
// 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);
|
Property scummvmSimulator_Release(scummvmSimulator_Debug);
|
||||||
ADD_SETTING(scummvmSimulator_Release, "COPY_PHASE_STRIP", "YES");
|
ADD_SETTING(scummvmSimulator_Release, "COPY_PHASE_STRIP", "YES");
|
||||||
ADD_SETTING(scummvmSimulator_Release, "GCC_OPTIMIZATION_LEVEL", "3");
|
ADD_SETTING(scummvmSimulator_Release, "GCC_OPTIMIZATION_LEVEL", "3");
|
||||||
|
@ -732,7 +930,7 @@ void XCodeProvider::setupBuildConfiguration() {
|
||||||
// Configuration List
|
// Configuration List
|
||||||
_configurationList.comment = "XCConfigurationList";
|
_configurationList.comment = "XCConfigurationList";
|
||||||
_configurationList.flags = SettingsAsList;
|
_configurationList.flags = SettingsAsList;
|
||||||
|
#endif
|
||||||
// Warning: This assumes we have all configurations with a Debug & Release pair
|
// 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++) {
|
for (std::vector<Object *>::iterator config = _buildConfiguration.objects.begin(); config != _buildConfiguration.objects.end(); config++) {
|
||||||
|
|
||||||
|
@ -758,7 +956,7 @@ void XCodeProvider::setupBuildConfiguration() {
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Setup global defines
|
// 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) {
|
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?)
|
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, "SCUMM_LITTLE_ENDIAN");
|
||||||
ADD_DEFINE(_defines, "UNIX");
|
ADD_DEFINE(_defines, "UNIX");
|
||||||
ADD_DEFINE(_defines, "SCUMMVM");
|
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)
|
// 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
|
#if DEBUG_XCODE_HASH
|
||||||
return key;
|
return key;
|
||||||
|
@ -800,7 +997,7 @@ std::string XCodeProvider::getHash(std::string key) {
|
||||||
|
|
||||||
bool isSeparator (char s) { return (s == '-'); }
|
bool isSeparator (char s) { return (s == '-'); }
|
||||||
|
|
||||||
std::string XCodeProvider::newHash() const {
|
std::string XcodeProvider::newHash() const {
|
||||||
std::string hash = createUUID();
|
std::string hash = createUUID();
|
||||||
|
|
||||||
// Remove { and - from UUID and resize to 96-bits uppercase hex string
|
// 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;
|
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;
|
std::string output;
|
||||||
|
|
||||||
output += (flags & SettingsSingleItem ? "" : "\t\t\t") + variable + " = ";
|
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);
|
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";
|
output += (prop.settings.size() > 0) ? ",\n" : "\n";
|
||||||
} else {
|
} else {
|
||||||
output += ";";
|
output += ";";
|
||||||
|
@ -861,13 +1060,13 @@ std::string XCodeProvider::writeProperty(const std::string &variable, Property &
|
||||||
return output;
|
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));
|
return writeSetting(variable, Setting(value, comment, flags, indent));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Heavily modified (not in a good way) function, imported from the QMake
|
// Heavily modified (not in a good way) function, imported from the QMake
|
||||||
// XCode project generator pbuilder_pbx.cpp, writeSettings() (under LGPL 2.1)
|
// 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;
|
std::string output;
|
||||||
const std::string quote = (setting.flags & SettingsNoQuote) ? "" : "\"";
|
const std::string quote = (setting.flags & SettingsNoQuote) ? "" : "\"";
|
||||||
const std::string escape_quote = quote.empty() ? "" : "\\" + quote;
|
const std::string escape_quote = quote.empty() ? "" : "\\" + quote;
|
||||||
|
|
|
@ -30,9 +30,9 @@
|
||||||
|
|
||||||
namespace CreateProjectTool {
|
namespace CreateProjectTool {
|
||||||
|
|
||||||
class XCodeProvider : public ProjectProvider {
|
class XcodeProvider : public ProjectProvider {
|
||||||
public:
|
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:
|
protected:
|
||||||
|
|
||||||
|
@ -45,7 +45,6 @@ protected:
|
||||||
|
|
||||||
void writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation,
|
void writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation,
|
||||||
const StringList &duplicate, const std::string &objPrefix, const std::string &filePrefix);
|
const StringList &duplicate, const std::string &objPrefix, const std::string &filePrefix);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum {
|
enum {
|
||||||
SettingsAsList = 0x01,
|
SettingsAsList = 0x01,
|
||||||
|
@ -169,7 +168,7 @@ private:
|
||||||
PropertyList properties; // List of object properties, including output configuration
|
PropertyList properties; // List of object properties, including output configuration
|
||||||
|
|
||||||
// Constructs an object and add a default type property
|
// 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) {
|
: id(objectId), name(objectName), refType(objectRefType), comment(objectComment), parent(objectParent) {
|
||||||
assert(objectParent);
|
assert(objectParent);
|
||||||
assert(!objectId.empty());
|
assert(!objectId.empty());
|
||||||
|
@ -210,9 +209,10 @@ private:
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Slight hack, to allow Group access to parent.
|
||||||
|
protected:
|
||||||
|
XcodeProvider *parent;
|
||||||
private:
|
private:
|
||||||
XCodeProvider *parent;
|
|
||||||
|
|
||||||
// Returns the type property (should always be the first in the properties map)
|
// Returns the type property (should always be the first in the properties map)
|
||||||
std::string getType() {
|
std::string getType() {
|
||||||
assert(!properties.empty());
|
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
|
// All objects
|
||||||
std::map<std::string, std::string> _hashDictionnary;
|
std::map<std::string, std::string> _hashDictionnary;
|
||||||
ValueList _defines;
|
ValueList _defines;
|
||||||
|
|
|
@ -54,10 +54,11 @@ bool AIFFTrack::openSound(const Common::String &filename, const Common::String &
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_soundName = soundName;
|
_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;
|
_stream = aiffStream;
|
||||||
if (start)
|
if (start)
|
||||||
aiffStream->seek(*start);
|
seekStream->seek(*start);
|
||||||
if (!_stream)
|
if (!_stream)
|
||||||
return false;
|
return false;
|
||||||
_handle = new Audio::SoundHandle();
|
_handle = new Audio::SoundHandle();
|
||||||
|
|
|
@ -1160,9 +1160,9 @@ void LauncherDialog::updateButtons() {
|
||||||
_loadButton->setEnabled(en);
|
_loadButton->setEnabled(en);
|
||||||
_loadButton->draw();
|
_loadButton->draw();
|
||||||
}
|
}
|
||||||
switchButtonsText(_addButton, "~A~dd Game...", "Mass Add...");
|
switchButtonsText(_addButton, "~A~dd Game...", _s("Mass Add..."));
|
||||||
#ifdef ENABLE_EVENTRECORDER
|
#ifdef ENABLE_EVENTRECORDER
|
||||||
switchButtonsText(_loadButton, "~L~oad...", "Record...");
|
switchButtonsText(_loadButton, "~L~oad...", _s("Record..."));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -461,11 +461,9 @@ void OptionsDialog::close() {
|
||||||
|
|
||||||
if (_oplPopUp) {
|
if (_oplPopUp) {
|
||||||
if (_enableAudioSettings) {
|
if (_enableAudioSettings) {
|
||||||
const OPL::Config::EmulatorDescription *ed = OPL::Config::getAvailable();
|
const OPL::Config::EmulatorDescription *ed = OPL::Config::findDriver(_oplPopUp->getSelectedTag());
|
||||||
while (ed->name && ed->id != (int)_oplPopUp->getSelectedTag())
|
|
||||||
++ed;
|
|
||||||
|
|
||||||
if (ed->name)
|
if (ed)
|
||||||
ConfMan.set("opl_driver", ed->name, _domain);
|
ConfMan.set("opl_driver", ed->name, _domain);
|
||||||
else
|
else
|
||||||
ConfMan.removeKey("opl_driver", _domain);
|
ConfMan.removeKey("opl_driver", _domain);
|
||||||
|
|
|
@ -433,10 +433,12 @@ const Graphics::Surface *CinepakDecoder::decodeFrame(Common::SeekableReadStream
|
||||||
for (uint16 j = 0; j < 256; j++) {
|
for (uint16 j = 0; j < 256; j++) {
|
||||||
_curFrame.strips[i].v1_codebook[j] = _curFrame.strips[i - 1].v1_codebook[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];
|
_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].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);
|
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].id = stream.readUint16BE();
|
||||||
_curFrame.strips[i].length = stream.readUint16BE() - 12; // Subtract the 12 byte header
|
_curFrame.strips[i].length = stream.readUint16BE() - 12; // Subtract the 12 byte header
|
||||||
|
|
|
@ -64,6 +64,9 @@ struct CinepakFrame {
|
||||||
* Cinepak decoder.
|
* Cinepak decoder.
|
||||||
*
|
*
|
||||||
* Used by BMP/AVI and PICT/QuickTime.
|
* Used by BMP/AVI and PICT/QuickTime.
|
||||||
|
*
|
||||||
|
* Used in engines:
|
||||||
|
* - sherlock
|
||||||
*/
|
*/
|
||||||
class CinepakDecoder : public Codec {
|
class CinepakDecoder : public Codec {
|
||||||
public:
|
public:
|
||||||
|
@ -88,7 +91,6 @@ private:
|
||||||
|
|
||||||
byte *_ditherPalette;
|
byte *_ditherPalette;
|
||||||
bool _dirtyPalette;
|
bool _dirtyPalette;
|
||||||
byte *_rgbLookup;
|
|
||||||
byte *_colorMap;
|
byte *_colorMap;
|
||||||
DitherType _ditherType;
|
DitherType _ditherType;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue