scummvm/backends/midi/mt32.cpp

267 lines
6.1 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2001-2004 The ScummVM project
*
* YM2612 tone generation code written by Tomoaki Hayasaka.
* Used under the terms of the GNU General Public License.
* Adpated to ScummVM by Jamieson Christian.
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
*/
#include "stdafx.h"
#include "common/scummsys.h"
#ifdef USE_MT32EMU
#include "backends/midi/mt32/mt32emu.h"
#include "backends/midi/emumidi.h"
#include "sound/mpu401.h"
#include "common/util.h"
#include "common/file.h"
#include "common/config-manager.h"
class MidiDriver_MT32 : public MidiDriver_Emulated {
private:
MidiChannel_MPU401 _midiChannels[16];
uint16 _channelMask;
MT32Emu::Synth *_synth;
int _outputRate;
protected:
void generateSamples(int16 *buf, int len);
public:
MidiDriver_MT32(SoundMixer *mixer);
virtual ~MidiDriver_MT32();
int open();
void close();
void send(uint32 b);
void sysEx(byte *msg, uint16 length);
uint32 property(int prop, uint32 param);
MidiChannel *allocateChannel();
MidiChannel *getPercussionChannel();
// AudioStream API
bool isStereo() const { return true; }
int getRate() const { return _outputRate; }
};
typedef File SFile;
class MT32File: public MT32Emu::File {
SFile file;
public:
bool open(const char *filename, OpenMode mode) {
SFile::AccessMode accessMode = mode == OpenMode_read ? SFile::kFileReadMode : SFile::kFileWriteMode;
return file.open(filename, accessMode);
}
void close() {
return file.close();
}
size_t read(void *ptr, size_t size) {
return file.read(ptr, size);
}
bool readLine(char *ptr, size_t size) {
return file.gets(ptr, size) != NULL;
}
size_t write(const void *ptr, size_t size) {
return file.write(ptr, size);
}
int readByte() {
byte b = file.readByte();
if (file.eof())
return -1;
return b;
}
bool writeByte(unsigned char out) {
file.writeByte(out);
if (file.ioFailed())
return false;
return true;
}
bool isEOF() {
return file.eof();
}
};
MT32Emu::File *MT32_OpenFile(void *userData, const char *filename, MT32Emu::File::OpenMode mode) {
MT32File *file = new MT32File();
if (!file->open(filename, mode)) {
delete file;
return NULL;
}
return file;
}
////////////////////////////////////////
//
// MidiDriver_MT32
//
////////////////////////////////////////
static void report(void *userData, MT32Emu::ReportType type, void *reportData) {
switch(type) {
case MT32Emu::ReportType_lcdMessage:
g_system->displayMessageOnOSD((char *)reportData);
break;
case MT32Emu::ReportType_errorPreset1:
error("Couldn't open Preset1.syx file");
break;
case MT32Emu::ReportType_errorPreset2:
error("Couldn't open Preset2.syx file");
break;
case MT32Emu::ReportType_errorDrumpat:
error("Couldn't open drumpat.rom file");
break;
case MT32Emu::ReportType_errorPatchlog:
error("Couldn't open patchlog.cfg file");
break;
case MT32Emu::ReportType_errorMT32ROM:
error("Couldn't open MT32_PCM.ROM file");
break;
default:
break;
}
}
MidiDriver_MT32::MidiDriver_MT32(SoundMixer *mixer) : MidiDriver_Emulated(mixer) {
_channelMask = 0xFFFF; // Permit all 16 channels by default
uint i;
for (i = 0; i < ARRAYSIZE(_midiChannels); ++i) {
_midiChannels[i].init(this, i);
}
_synth = NULL;
_baseFreq = 1000;
_outputRate = _mixer->getOutputRate();
}
MidiDriver_MT32::~MidiDriver_MT32() {
if (_synth != NULL)
delete _synth;
}
static void vdebug(void *data, const char *fmt, va_list list) {
// do nothing here now
}
int MidiDriver_MT32::open() {
MT32Emu::SynthProperties prop;
if (_isOpen)
return MERR_ALREADY_OPEN;
MidiDriver_Emulated::open();
memset(&prop, 0, sizeof(prop));
prop.SampleRate = getRate();
prop.UseReverb = true;
prop.UseDefault = false;
prop.RevType = 0;
prop.RevTime = 5;
prop.RevLevel = 3;
prop.userData = (void *)1;
prop.printDebug = &vdebug;
prop.report = &report;
prop.openFile = MT32_OpenFile;
_synth = new MT32Emu::Synth();
if (!_synth->open(prop))
return MERR_DEVICE_NOT_AVAILABLE;
_mixer->setupPremix(this);
return 0;
}
void MidiDriver_MT32::send(uint32 b) {
_synth->playMsg(b);
}
void MidiDriver_MT32::sysEx(byte *msg, uint16 length) {
if (msg[0] == 0xf0) {
_synth->playSysex(msg, length);
} else {
_synth->playSysexWithoutFraming(msg, length);
}
}
void MidiDriver_MT32::close() {
if (!_isOpen)
return;
_isOpen = false;
// Detach the premix callback handler
_mixer->setupPremix(0);
_synth->close();
delete _synth;
_synth = NULL;
}
void MidiDriver_MT32::generateSamples(int16 *data, int len) {
_synth->render(data, len);
}
uint32 MidiDriver_MT32::property(int prop, uint32 param) {
switch (prop) {
case PROP_CHANNEL_MASK:
_channelMask = param & 0xFFFF;
return 1;
}
return 0;
}
MidiChannel *MidiDriver_MT32::allocateChannel() {
MidiChannel_MPU401 *chan;
uint i;
for (i = 0; i < ARRAYSIZE(_midiChannels); ++i) {
if (i == 9 || !(_channelMask & (1 << i)))
continue;
chan = &_midiChannels[i];
if (chan->allocate()) {
return chan;
}
}
return NULL;
}
MidiChannel *MidiDriver_MT32::getPercussionChannel() {
return &_midiChannels[9];
}
////////////////////////////////////////
//
// MidiDriver_MT32 factory
//
////////////////////////////////////////
MidiDriver *MidiDriver_MT32_create(SoundMixer *mixer) {
// HACK: It will stay here until engine plugin loader overhaul
if (ConfMan.hasKey("extrapath"))
File::addDefaultDirectory(ConfMan.get("extrapath"));
return new MidiDriver_MT32(mixer);
}
#endif