Skip to content

Commit 30a563b

Browse files
authored
MT32 device & basic MIDI driver (#258)
* mt32 midi driver init * MT32 MIDI basic driver playback and example * fix build * fix test suite * code rev * MT32 Report Handler * code rev * code rev * code rev * mt32 sysex lcd message * code rev * sonarcloud code rev * code rev * update version
1 parent 8448f76 commit 30a563b

File tree

21 files changed

+474
-135
lines changed

21 files changed

+474
-135
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ if(DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE)
55
endif()
66

77

8-
project ("sdl2-hyper-sonic-drivers" VERSION 0.11.0 DESCRIPTION "SDL2 based Hyper-Sonic Drivers for emulating old soundcards")
8+
project ("sdl2-hyper-sonic-drivers" VERSION 0.12.0 DESCRIPTION "SDL2 based Hyper-Sonic Drivers for emulating old soundcards")
99
include (TestBigEndian)
1010
TEST_BIG_ENDIAN(IS_BIG_ENDIAN)
1111
if(IS_BIG_ENDIAN)

sdl2-hyper-sonic-drivers/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ target_sources(${LIB_NAME} PRIVATE
116116
${CMAKE_CURRENT_SOURCE_DIR}/src/HyperSonicDrivers/drivers/midi/opl/OplChannel.cpp
117117
${CMAKE_CURRENT_SOURCE_DIR}/src/HyperSonicDrivers/drivers/midi/opl/OplVoice.cpp
118118

119+
${CMAKE_CURRENT_SOURCE_DIR}/src/HyperSonicDrivers/drivers/midi/mt32/MT32Driver.cpp
120+
119121
${CMAKE_CURRENT_SOURCE_DIR}/src/HyperSonicDrivers/drivers/opl/OplWriter.cpp
120122
# --- #
121123
${CMAKE_CURRENT_SOURCE_DIR}/src/HyperSonicDrivers/files/File.cpp
@@ -158,6 +160,7 @@ target_sources(${LIB_NAME} PRIVATE
158160
${CMAKE_CURRENT_SOURCE_DIR}/src/HyperSonicDrivers/hardware/opl/woody/WoodyEmuOPL.cpp
159161
# --- #
160162
${CMAKE_CURRENT_SOURCE_DIR}/src/HyperSonicDrivers/hardware/mt32/MT32.cpp
163+
${CMAKE_CURRENT_SOURCE_DIR}/src/HyperSonicDrivers/hardware/mt32/MT32ReportHandler.cpp
161164
# --- #
162165
${CMAKE_CURRENT_SOURCE_DIR}/src/HyperSonicDrivers/hardware/PCSpeaker.cpp
163166
# --- #

sdl2-hyper-sonic-drivers/examples/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,13 @@ macro_example(
119119
"../test/fixtures/GENMIDI.OP2"
120120
NONE
121121
)
122+
123+
macro_example(
124+
EXE MT32example
125+
FILE "mt32-example.cpp"
126+
DEPS hyper-sonic-drivers-static
127+
LINKS ${LIB_SDL2main} hyper-sonic-drivers-static spdlog::spdlog
128+
FIXTURES
129+
"../test/fixtures/MI_intro.mid"
130+
NONE
131+
)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#include <HyperSonicDrivers/audio/sdl2/Mixer.hpp>
2+
#include <HyperSonicDrivers/drivers/MIDDriver.hpp>
3+
#include <HyperSonicDrivers/files/MIDFile.hpp>
4+
#include <HyperSonicDrivers/devices/MT32.hpp>
5+
#include <HyperSonicDrivers/utils/algorithms.hpp>
6+
7+
#include <memory>
8+
#include <spdlog/spdlog.h>
9+
#include <fmt/chrono.h>
10+
#include <fmt/color.h>
11+
#include <map>
12+
13+
#include <HyperSonicDrivers/utils/ILogger.hpp>
14+
15+
using namespace HyperSonicDrivers;
16+
17+
int main(int argc, char* argv[])
18+
{
19+
using audio::mixer::eChannelGroup;
20+
21+
utils::ILogger::instance->setLevelAll(utils::ILogger::eLevel::Trace);
22+
spdlog::warn("MT32 requires 2 ROMs files not included in this example");
23+
spdlog::warn("the ROM filenames assumed to be fund in the current working directory are:");
24+
spdlog::warn("MT32_CONTROL.ROM --- MT32_PCM.ROM");
25+
26+
auto mixer = audio::make_mixer<audio::sdl2::Mixer>(8, 44100, 1024);
27+
if (!mixer->init())
28+
{
29+
spdlog::error("can't init mixer");
30+
return 1;
31+
}
32+
33+
auto mt32 = devices::make_device<devices::MT32>(mixer, "MT32_CONTROL.ROM", "MT32_PCM.ROM");
34+
35+
if (!mt32->init())
36+
{
37+
spdlog::error("can't init MT32 device");
38+
return 2;
39+
}
40+
41+
auto midFile = std::make_shared<files::MIDFile>("MI_intro.mid");
42+
auto midi = midFile->getMIDI();
43+
44+
drivers::MIDDriver middrv(mt32, eChannelGroup::Music);
45+
middrv.setMidi(midi);
46+
middrv.play(0);
47+
while (middrv.isPlaying())
48+
{
49+
utils::delayMillis(100);
50+
}
51+
}

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/devices/IDevice.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
namespace HyperSonicDrivers::devices
44
{
5-
IDevice::IDevice(const std::shared_ptr<audio::IMixer>& mixer, const bool isOpl) :
6-
m_mixer(mixer), m_isOpl(isOpl)
5+
IDevice::IDevice(const std::shared_ptr<audio::IMixer>& mixer, const eDeviceType type) :
6+
type(type), m_mixer(mixer)
77
{
88
}
99

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/devices/IDevice.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <HyperSonicDrivers/drivers/IMusicDriver.hpp>
88
#include <HyperSonicDrivers/drivers/midi/IMidiDriver.hpp>
99
#include <HyperSonicDrivers/hardware/IHardware.hpp>
10+
#include <HyperSonicDrivers/devices/types.hpp>
1011

1112
namespace HyperSonicDrivers::devices
1213
{
@@ -17,7 +18,7 @@ namespace HyperSonicDrivers::devices
1718
class IDevice
1819
{
1920
public:
20-
IDevice(const std::shared_ptr<audio::IMixer>& mixer, const bool isOpl = false);
21+
IDevice(const std::shared_ptr<audio::IMixer>& mixer, const eDeviceType type);
2122
virtual ~IDevice() = default;
2223

2324
virtual bool init() noexcept = 0;
@@ -38,14 +39,13 @@ namespace HyperSonicDrivers::devices
3839
void setVolumePan(const uint8_t volume, const uint8_t pan);
3940

4041
inline std::shared_ptr<audio::IMixer> getMixer() const noexcept { return m_mixer; };
41-
inline bool isOpl() const noexcept { return m_isOpl; };
4242
virtual hardware::IHardware* getHardware() const noexcept { return m_hardware; };
43+
const eDeviceType type;
4344
protected:
4445
bool m_init = false;
4546
std::shared_ptr<audio::IMixer> m_mixer;
46-
hardware::IHardware* m_hardware;
47+
hardware::IHardware* m_hardware = nullptr;
4748
private:
48-
bool m_isOpl = false;
4949
// TODO: remove the atomic when removing the thread in MIDDrv
5050
std::atomic<bool> m_acquired = false;
5151
std::atomic<drivers::IMusicDriver*> m_owner = nullptr;

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/devices/MT32.cpp

Lines changed: 21 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -4,103 +4,41 @@
44

55
namespace HyperSonicDrivers::devices
66
{
7-
// test
8-
static audio::midi::MIDIEvent MT32SysEx(uint32_t addr, const uint8_t* data, uint32_t dataSize)
9-
{
10-
//typedef uint8_t byte;
11-
//uint32_t dataSize = 0;
12-
//uint8_t* data = nullptr;
13-
//uint32_t addr = 0x1FC000;
14-
static const uint8_t header[] = { 0x41, 0x10, 0x16, 0x12 };
15-
16-
uint8_t* msg = new uint8_t[sizeof(header) + 4 + dataSize];
17-
memcpy(msg, header, sizeof(header));
18-
uint8_t* dst = msg + sizeof(header);
19-
const uint8_t* src = dst;
20-
21-
*dst++ = (addr >> 14) & 0x7F;
22-
*dst++ = (addr >> 7) & 0x7F;
23-
*dst++ = addr & 0x7F;
24-
25-
while (dataSize) {
26-
*dst++ = *data++;
27-
--dataSize;
28-
}
29-
30-
uint8_t checkSum = 0;
31-
while (src < dst)
32-
checkSum -= *src++;
33-
34-
*dst++ = checkSum & 0x7F;
35-
36-
dataSize = dst - msg;
37-
audio::midi::MIDIEvent e;
38-
for (int i = 0; i < sizeof(header) + 4 + dataSize; i++)
39-
e.data.push_back(msg[i]);
40-
41-
delete[] msg;
42-
43-
return e;
44-
}
45-
46-
477
MT32::MT32(
488
const std::shared_ptr<audio::IMixer>& mixer,
499
const std::filesystem::path& control_rom_file,
5010
const std::filesystem::path& pcm_rom_file) :
51-
IDevice(mixer, false)
11+
IDevice(mixer, eDeviceType::Mt32)
5212
{
5313
m_mt32 = std::make_shared<hardware::mt32::MT32>(control_rom_file, pcm_rom_file, mixer);
54-
if (!m_mt32->init())
55-
{
56-
utils::throwLogC<std::runtime_error>(std::format("Can't init device MT32"));
57-
}
58-
59-
m_mt32->start(nullptr);
60-
61-
// test
62-
63-
// reset MT32
64-
//m_mt32->m_service.playSysex(MT32SysEx(0x1FC000, 0, 0));
65-
utils::delayMillis(250);
14+
m_hardware = m_mt32.get();
15+
}
6616

67-
// Setup master tune, reverb mode, reverb time, reverb level, channel mapping, partial reserve and master volume
68-
static const uint8_t initSysex1[] = "\x40\x00\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x64";
69-
//sendSysEx(MT32SysEx(0x40000, initSysex1, sizeof(initSysex1) - 1));
70-
utils::delayMillis(40);
71-
// Map percussion to notes 24 - 34 without reverb. It still happens in the DOTT driver, but not in the SAMNMAX one.
72-
static const uint8_t initSysex2[] = "\x40\x64\x07\x00\x4a\x64\x06\x00\x41\x64\x07\x00\x4b\x64\x08\x00\x45\x64\x06\x00\x44\x64"
73-
"\x0b\x00\x51\x64\x05\x00\x43\x64\x08\x00\x50\x64\x07\x00\x42\x64\x03\x00\x4c\x64\x07\x00";
74-
//sendSysEx(MT32SysEx(0xC090, initSysex2, sizeof(initSysex2) - 1));
75-
utils::delayMillis(40);
17+
bool MT32::init() noexcept
18+
{
19+
// TODO: it can be bring up into the parent class using IHardware check if not nullptr
20+
if (isInit())
21+
return true;
7622

23+
m_init = m_mt32->init();
7724

78-
const uint8_t pbRange = 0x10;
79-
for (int i = 0; i < 128; ++i) {
80-
//sendSysEx(MT32SysEx(0x014004 + (i << 3), &pbRange, 1));
81-
utils::delayMillis(5);
82-
}
25+
return m_init;
8326
}
8427

85-
/* void MidiMT32::sendEvent(const audio::midi::MIDIEvent& e) const noexcept
28+
bool MT32::shutdown() noexcept
8629
{
87-
m_mt32->m_service.playMsg(e.toUint32());
30+
// TODO: it can be bring up into the parent class using iHardware checkin if not nullptr
31+
m_mt32->stop();
32+
return false;
8833
}
8934

90-
void MidiMT32::sendMessage(const uint8_t msg[], const uint8_t size) const noexcept
35+
void MT32::lcd_message(const std::string& msg) noexcept
9136
{
37+
m_mt32->sysEx(
38+
hardware::mt32::mt32_sysex_addr_LCD,
39+
std::bit_cast<const uint8_t*>(msg.c_str()),
40+
//reinterpret_cast<const uint8_t*>(msg.c_str()),
41+
static_cast<uint32_t>(msg.size())
42+
);
9243
}
93-
94-
void MidiMT32::sendSysEx(const audio::midi::MIDIEvent& e) const noexcept
95-
{
96-
m_mt32->m_service.playSysex(e.data.data(), e.data.size());
97-
}*/
98-
99-
//void MT32::pause() const noexcept
100-
//{
101-
//}
102-
103-
//void MT32::resume() const noexcept
104-
//{
105-
//}
10644
}

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/devices/MT32.hpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <cstdint>
44
#include <memory>
55
#include <filesystem>
6+
#include <string>
67
#include <HyperSonicDrivers/audio/IMixer.hpp>
78
#include <HyperSonicDrivers/audio/midi/MIDIEvent.hpp>
89
#include <HyperSonicDrivers/devices/IDevice.hpp>
@@ -20,9 +21,12 @@ namespace HyperSonicDrivers::devices
2021
);
2122
~MT32() override = default;
2223

23-
virtual bool init() noexcept = 0;
24-
virtual bool shutdown() noexcept = 0;
24+
bool init() noexcept override;
25+
bool shutdown() noexcept override;
2526

27+
inline std::shared_ptr<hardware::mt32::MT32> getMt32() const noexcept { return m_mt32; };
28+
29+
void lcd_message(const std::string& msg) noexcept;
2630
private:
2731
std::shared_ptr<hardware::mt32::MT32> m_mt32;
2832
};

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/devices/Opl.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace HyperSonicDrivers::devices
1515
const hardware::opl::OplEmulator emulator,
1616
const hardware::opl::OplType type,
1717
const uint8_t volume, const uint8_t pan) :
18-
IDevice(mixer, true)
18+
IDevice(mixer, eDeviceType::Opl)
1919
{
2020
using hardware::opl::OPLFactory;
2121
using utils::logC;
@@ -26,6 +26,7 @@ namespace HyperSonicDrivers::devices
2626

2727
bool Opl::init() noexcept
2828
{
29+
// TODO can be put in the parent class using IHardware*
2930
if (isInit())
3031
return true;
3132

@@ -41,6 +42,7 @@ namespace HyperSonicDrivers::devices
4142

4243
bool Opl::shutdown() noexcept
4344
{
45+
// TODO: can be put in the parent calss using iHardware*
4446
if (m_opl != nullptr)
4547
m_opl->stop();
4648

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#pragma once
2+
3+
namespace HyperSonicDrivers::devices
4+
{
5+
enum class eDeviceType
6+
{
7+
Opl = 0,
8+
Mt32,
9+
};
10+
}

0 commit comments

Comments
 (0)