diff --git a/backends/midi/riscos.cpp b/backends/midi/riscos.cpp new file mode 100644 index 000000000000..fa786961271f --- /dev/null +++ b/backends/midi/riscos.cpp @@ -0,0 +1,174 @@ +/* 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 . + * + */ + +// Disable symbol overrides so that we can use system headers. +#define FORBIDDEN_SYMBOL_ALLOW_ALL + +#include "common/scummsys.h" + +#ifdef RISCOS + +#include "common/error.h" +#include "common/textconsole.h" +#include "audio/musicplugin.h" +#include "audio/mpu401.h" + +#include +#include + +#ifndef MIDI_TxByte +#define MIDI_TxByte 0x404C9 +#endif +#ifndef MIDI_TxCommand +#define MIDI_TxCommand 0x404CA +#endif + + +class MidiDriver_RISCOS final : public MidiDriver_MPU401 { +public: + MidiDriver_RISCOS(int port = 0) : _isOpen(false), _port(port) {} + ~MidiDriver_RISCOS() {} + + int open() override; + bool isOpen() const override { return _isOpen; } + void close() override; + void send(uint32 b) override; + void sysEx(const byte *msg, uint16 length) override; + +private: + _kernel_oserror *txByte(int r0); + _kernel_oserror *txCommand(int r0, int r1); + + bool _isOpen; + int _port; +}; + +int MidiDriver_RISCOS::open() { + if (isOpen()) + return MERR_ALREADY_OPEN; + + _isOpen = true; + return 0; +} + +void MidiDriver_RISCOS::close() { + MidiDriver_MPU401::close(); + _isOpen = false; +} + +void MidiDriver_RISCOS::send(uint32 b) { + assert(isOpen()); + + midiDriverCommonSend(b); + + // Extract the MIDI data + byte status_byte = (b & 0x000000FF); + // byte first_byte = (b & 0x0000FF00) >> 8; + // byte second_byte = (b & 0x00FF0000) >> 16; + + // Compute the correct length of the MIDI command. This is important, + // else things may screw up badly... + byte length; + switch (status_byte & 0xF0) { + case 0x80: // Note Off + case 0x90: // Note On + case 0xA0: // Polyphonic Aftertouch + case 0xB0: // Controller Change + case 0xE0: // Pitch Bending + length = 3; + break; + case 0xC0: // Programm Change + case 0xD0: // Monophonic Aftertouch + length = 2; + break; + default: + warning("RISC OS driver encountered unsupported status byte: 0x%02x", status_byte); + length = 3; + break; + } + + // Finally send it out to the synthesizer. + txCommand(b | (length << 24) | (_port << 28), 0); +} + +void MidiDriver_RISCOS::sysEx(const byte *msg, uint16 length) { + assert(isOpen()); + + int port = (_port << 28); + + txByte(0xF0 | port); + for (; length; --length, ++msg) { + txByte(*msg | port); + } + txByte(0xF7 | port); +} + +_kernel_oserror *MidiDriver_RISCOS::txByte(int r0) { + _kernel_swi_regs regs; + regs.r[0] = r0; + return _kernel_swi(MIDI_TxByte, ®s, ®s); +} + +_kernel_oserror *MidiDriver_RISCOS::txCommand(int r0, int r1) { + _kernel_swi_regs regs; + regs.r[0] = r0; + regs.r[1] = r1; + return _kernel_swi(MIDI_TxCommand, ®s, ®s); +} + + +// Plugin interface + +class RISCOSMusicPlugin : public MusicPluginObject { +public: + const char *getName() const { + return "RISC OS MIDI"; + } + + const char *getId() const { + return "riscos"; + } + + MusicDevices getDevices() const; + Common::Error createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle = 0) const; +}; + +MusicDevices RISCOSMusicPlugin::getDevices() const { + MusicDevices devices; + // TODO: Return a different music type depending on the configuration + // TODO: List the available devices + devices.push_back(MusicDevice(this, "", MT_GM)); + return devices; +} + +Common::Error RISCOSMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle device) const { + *mididriver = new MidiDriver_RISCOS(); + + return Common::kNoError; +} + +//#if PLUGIN_ENABLED_DYNAMIC(RISCOS) + //REGISTER_PLUGIN_DYNAMIC(RISCOS, PLUGIN_TYPE_MUSIC, RISCOSMusicPlugin); +//#else + REGISTER_PLUGIN_STATIC(RISCOS, PLUGIN_TYPE_MUSIC, RISCOSMusicPlugin); +//#endif + +#endif // RISCOS diff --git a/backends/module.mk b/backends/module.mk index 3501fcca805a..2f27ad9e1640 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -275,6 +275,7 @@ MODULE_OBJS += \ events/riscossdl/riscossdl-events.o \ fs/riscos/riscos-fs.o \ fs/riscos/riscos-fs-factory.o \ + midi/riscos.o \ plugins/riscos/riscos-provider.o ifndef SDL_BACKEND # This is needed for null backend but already included in SDL backend diff --git a/base/plugins.cpp b/base/plugins.cpp index fc5043e35415..e6022be019d1 100644 --- a/base/plugins.cpp +++ b/base/plugins.cpp @@ -130,6 +130,9 @@ class StaticPluginProvider : public PluginProvider { #if defined(__amigaos4__) || defined(__MORPHOS__) LINK_PLUGIN(CAMD) #endif + #if defined(RISCOS) + LINK_PLUGIN(RISCOS) + #endif #if defined(MACOSX) LINK_PLUGIN(COREAUDIO) LINK_PLUGIN(COREMIDI)