scummvm/audio/mpu401.cpp
athrxx 99dd6cb248 SCUMM: (IMS) - minor restructuring and cleanup
Currently, Roland MT-32 sound is broken for
SAMNMAX. Just try the intro song with shut off
speech. It is very obvious that it plays with a quite
reduced number of channels compared to the
original interpreter.

Now, due to the not-so-helpful code design (much
of the iMuse code has been drawn into the common
code) it has become increasingly difficult to fix Midi
related thing in iMuse. I have added more and more
crude hacks over time.

SAMNMAX requires more elaborate channel
allocation. To make it happen I have added driver
wrappers for Midi to the iMuse code.

Other than that, I have done only minor cleanup here.

Actually, I would have liked to withdraw much more
of the iMuse code from the common code and move
it to SCUMM (basically all the MidiChannel
stuff which is exclusively used by iMuse. But it turns
out that it is so thoroughly intertwined (the major
blocker here being the AdLib driver) that it requires
more thought and effort and would just distract me
from fixing the SAMNMAX sound.
2022-10-06 18:47:40 +02:00

151 lines
3.7 KiB
C++

/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "audio/mpu401.h"
#include "common/system.h"
#include "common/timer.h"
#include "common/util.h" // for ARRAYSIZE
void MidiChannel_MPU401::init(MidiDriver *owner, byte channel) {
_owner = owner;
_channel = channel;
_allocated = false;
}
bool MidiChannel_MPU401::allocate() {
if (_allocated)
return false;
_allocated = true;
return true;
}
MidiDriver *MidiChannel_MPU401::device() {
return _owner;
}
void MidiChannel_MPU401::send(uint32 b) {
_owner->send((b & 0xFFFFFFF0) | (_channel & 0xF));
}
void MidiChannel_MPU401::noteOff(byte note) {
_owner->send(note << 8 | 0x80 | _channel);
}
void MidiChannel_MPU401::noteOn(byte note, byte velocity) {
_owner->send(velocity << 16 | note << 8 | 0x90 | _channel);
}
void MidiChannel_MPU401::programChange(byte program) {
_owner->send(program << 8 | 0xC0 | _channel);
}
void MidiChannel_MPU401::pitchBend(int16 bend) {
_owner->send((((bend + 0x2000) >> 7) & 0x7F) << 16 | ((bend + 0x2000) & 0x7F) << 8 | 0xE0 | _channel);
}
void MidiChannel_MPU401::controlChange(byte control, byte value) {
_owner->send(value << 16 | control << 8 | 0xB0 | _channel);
}
void MidiChannel_MPU401::pitchBendFactor(byte value) {
_owner->setPitchBendRange(_channel, value);
}
const char *MidiDriver::getErrorName(int error_code) {
static const char *const midi_errors[] = {
"No error",
"Cannot connect",
"Streaming not available",
"Device not available",
"Driver already open"
};
if ((uint)error_code >= ARRAYSIZE(midi_errors))
return "Unknown Error";
return midi_errors[error_code];
}
MidiDriver_MPU401::MidiDriver_MPU401() :
MidiDriver(),
_timer_proc(nullptr),
_channel_mask(0xFFFF) // Permit all 16 channels by default
{
uint i;
for (i = 0; i < ARRAYSIZE(_midi_channels); ++i) {
_midi_channels[i].init(this, i);
}
}
MidiDriver_MPU401::~MidiDriver_MPU401() {
}
void MidiDriver_MPU401::close() {
if (_timer_proc) {
g_system->getTimerManager()->removeTimerProc(_timer_proc);
_timer_proc = nullptr;
}
if (isOpen()) {
for (int i = 0; i < 16; ++i)
send(0x7B << 8 | 0xB0 | i);
}
}
uint32 MidiDriver_MPU401::property(int prop, uint32 param) {
switch (prop) {
case PROP_CHANNEL_MASK:
_channel_mask = param & 0xFFFF;
return 1;
default:
break;
}
return 0;
}
MidiChannel *MidiDriver_MPU401::allocateChannel() {
MidiChannel_MPU401 *chan;
uint i;
for (i = 0; i < ARRAYSIZE(_midi_channels); ++i) {
if (i == 9 || !(_channel_mask & (1 << i)))
continue;
chan = &_midi_channels[i];
if (chan->allocate()) {
return chan;
}
}
return nullptr;
}
void MidiDriver_MPU401::setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) {
if (!_timer_proc || !timer_proc) {
if (_timer_proc)
g_system->getTimerManager()->removeTimerProc(_timer_proc);
_timer_proc = timer_proc;
if (timer_proc)
g_system->getTimerManager()->installTimerProc(timer_proc, 10000, timer_param, "MPU401");
}
}