aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/adlmidi.cpp70
-rw-r--r--src/adlmidi_load.cpp2
-rw-r--r--src/adlmidi_midiplay.cpp30
-rw-r--r--src/adlmidi_midiplay.hpp9
-rw-r--r--src/adlmidi_opl3.cpp71
-rw-r--r--src/adlmidi_opl3.hpp23
-rw-r--r--src/adlmidi_sequencer.cpp4
-rw-r--r--src/chips/opl_serial_misc.h317
-rw-r--r--src/chips/opl_serial_port.cpp166
-rw-r--r--src/chips/opl_serial_port.h65
10 files changed, 738 insertions, 19 deletions
diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp
index be4b6bf..a642259 100644
--- a/src/adlmidi.cpp
+++ b/src/adlmidi.cpp
@@ -131,13 +131,20 @@ ADLMIDI_EXPORT int adl_setNumChips(ADL_MIDIPlayer *device, int numChips)
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
+
+#ifdef ADLMIDI_ENABLE_HW_SERIAL
+ if(play->m_setup.serial)
+ numChips = 1;
+#endif
+
#ifdef ADLMIDI_HW_OPL
ADL_UNUSED(numChips);
play->m_setup.numChips = 1;
#else
play->m_setup.numChips = static_cast<unsigned int>(numChips);
#endif
- if(play->m_setup.numChips < 1 || play->m_setup.numChips > ADL_MAX_CHIPS)
+
+ if((play->m_setup.numChips < 1) || (play->m_setup.numChips > ADL_MAX_CHIPS))
{
play->setErrorString("number of chips may only be 1.." ADL_MAX_CHIPS_STR ".\n");
return -1;
@@ -618,6 +625,10 @@ ADLMIDI_EXPORT void adl_setSoftPanEnabled(ADL_MIDIPlayer *device, int softPanEn)
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_synth->m_softPanning = (softPanEn != 0);
+#ifdef ADLMIDI_ENABLE_HW_SERIAL
+ if(play->m_setup.serial) // Soft-panning won't work while serial is active
+ play->m_synth->m_softPanning = false;
+#endif
}
/* !!!DEPRECATED!!! */
@@ -846,6 +857,9 @@ ADLMIDI_EXPORT int adl_switchEmulator(struct ADL_MIDIPlayer *device, int emulato
if(adl_isEmulatorAvailable(emulator))
{
play->m_setup.emulator = emulator;
+#ifdef ADLMIDI_ENABLE_HW_SERIAL
+ play->m_setup.serial = false;
+#endif
play->partialReset();
return 0;
}
@@ -870,6 +884,31 @@ ADLMIDI_EXPORT int adl_setRunAtPcmRate(ADL_MIDIPlayer *device, int enabled)
return -1;
}
+ADLMIDI_EXPORT int adl_switchSerialHW(struct ADL_MIDIPlayer *device,
+ const char *name,
+ unsigned baud,
+ unsigned protocol)
+{
+ if(device)
+ {
+ MidiPlayer *play = GET_MIDI_PLAYER(device);
+ assert(play);
+#ifdef ADLMIDI_ENABLE_HW_SERIAL
+ play->m_setup.serial = true;
+ play->m_setup.serialName = std::string(name);
+ play->m_setup.serialBaud = baud;
+ play->m_setup.serialProtocol = protocol;
+ play->partialReset();
+ return 0;
+#else
+ (void)name; (void)baud; (void)protocol;
+ play->setErrorString("ADLMIDI: The hardware serial mode is not enabled in this build");
+ return -1;
+#endif
+ }
+
+ return -1;
+}
ADLMIDI_EXPORT const char *adl_linkedLibraryVersion()
{
@@ -1498,7 +1537,10 @@ ADLMIDI_EXPORT int adl_playFormat(ADL_MIDIPlayer *device, int sampleCount,
hasSkipped = setup.tick_skip_samples_delay > 0;
}
else
+ {
setup.delay = player->Tick(eat_delay, setup.mindelay);
+ player->TickIterators(eat_delay);
+ }
}
return static_cast<int>(gotten_len);
@@ -1593,6 +1635,24 @@ ADLMIDI_EXPORT double adl_tickEvents(struct ADL_MIDIPlayer *device, double secon
return -1.0;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
+ double ret = play->Tick(seconds, granulality);
+ play->TickIterators(seconds);
+ return ret;
+#else
+ ADL_UNUSED(device);
+ ADL_UNUSED(seconds);
+ ADL_UNUSED(granulality);
+ return -1.0;
+#endif
+}
+
+ADLMIDI_EXPORT double adl_tickEventsOnly(struct ADL_MIDIPlayer *device, double seconds, double granulality)
+{
+#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
+ if(!device)
+ return -1.0;
+ MidiPlayer *play = GET_MIDI_PLAYER(device);
+ assert(play);
return play->Tick(seconds, granulality);
#else
ADL_UNUSED(device);
@@ -1602,6 +1662,14 @@ ADLMIDI_EXPORT double adl_tickEvents(struct ADL_MIDIPlayer *device, double secon
#endif
}
+ADLMIDI_EXPORT void adl_tickIterators(struct ADL_MIDIPlayer *device, double seconds)
+{
+ if(!device)
+ return;
+ MidiPlayer *play = GET_MIDI_PLAYER(device);
+ play->TickIterators(seconds);
+}
+
ADLMIDI_EXPORT int adl_atEnd(struct ADL_MIDIPlayer *device)
{
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
diff --git a/src/adlmidi_load.cpp b/src/adlmidi_load.cpp
index e3f5819..ce02006 100644
--- a/src/adlmidi_load.cpp
+++ b/src/adlmidi_load.cpp
@@ -261,7 +261,7 @@ bool MIDIplay::LoadMIDI_post()
resetMIDIDefaults();
m_setup.tick_skip_samples_delay = 0;
- synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPL3 chip
+ chipReset(); // Reset OPL3 chip
//opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously)
m_chipChannels.clear();
m_chipChannels.resize(synth.m_numChannels);
diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp
index 2ce2ed0..8af0674 100644
--- a/src/adlmidi_midiplay.cpp
+++ b/src/adlmidi_midiplay.cpp
@@ -76,6 +76,13 @@ MIDIplay::MIDIplay(unsigned long sampleRate):
m_setup.emulator = adl_getLowestEmulator();
m_setup.runAtPcmRate = false;
+#ifdef ADLMIDI_ENABLE_HW_SERIAL
+ m_setup.serial = false;
+ m_setup.serialName = std::string();
+ m_setup.serialBaud = 0;
+ m_setup.serialProtocol = 0;
+#endif
+
m_setup.PCM_RATE = sampleRate;
m_setup.mindelay = 1.0 / (double)m_setup.PCM_RATE;
m_setup.maxdelay = 512.0 / (double)m_setup.PCM_RATE;
@@ -155,7 +162,7 @@ void MIDIplay::applySetup()
else
adlCalculateFourOpChannels(this, true);
- synth.reset(m_setup.emulator, m_setup.PCM_RATE, this);
+ chipReset();
m_chipChannels.clear();
m_chipChannels.resize(synth.m_numChannels);
@@ -169,7 +176,7 @@ void MIDIplay::partialReset()
realTime_panic();
m_setup.tick_skip_samples_delay = 0;
synth.m_runAtPcmRate = m_setup.runAtPcmRate;
- synth.reset(m_setup.emulator, m_setup.PCM_RATE, this);
+ chipReset();
m_chipChannels.clear();
m_chipChannels.resize((size_t)synth.m_numChannels);
resetMIDIDefaults();
@@ -193,6 +200,21 @@ void MIDIplay::resetMIDI()
caugh_missing_banks_percussion.clear();
}
+void MIDIplay::chipReset()
+{
+ Synth &synth = *m_synth;
+
+#ifdef ADLMIDI_ENABLE_HW_SERIAL
+ if(m_setup.serial)
+ {
+ synth.resetSerial(m_setup.serialName, m_setup.serialBaud, m_setup.serialProtocol);
+ return;
+ }
+#endif
+
+ synth.reset(m_setup.emulator, m_setup.PCM_RATE, this);
+}
+
void MIDIplay::resetMIDIDefaults(int offset)
{
Synth &synth = *m_synth;
@@ -252,7 +274,11 @@ void MIDIplay::TickIterators(double s)
updateVibrato(s);
updateArpeggio(s);
+
#if !defined(ADLMIDI_AUDIO_TICK_HANDLER)
+# ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
+ s *= m_sequencer->getTempoMultiplier(); // Glide will follow the tempo
+# endif
updateGlide(s);
#endif
}
diff --git a/src/adlmidi_midiplay.hpp b/src/adlmidi_midiplay.hpp
index 4ac5b3d..5b5bc45 100644
--- a/src/adlmidi_midiplay.hpp
+++ b/src/adlmidi_midiplay.hpp
@@ -76,6 +76,7 @@ public:
void resetMIDI();
private:
+ void chipReset();
void resetMIDIDefaults(int offset = 0);
public:
@@ -526,6 +527,14 @@ public:
{
int emulator;
bool runAtPcmRate;
+
+#ifdef ADLMIDI_ENABLE_HW_SERIAL
+ bool serial;
+ std::string serialName;
+ unsigned int serialBaud;
+ unsigned int serialProtocol;
+#endif
+
unsigned int bankId;
int numFourOps;
unsigned int numChips;
diff --git a/src/adlmidi_opl3.cpp b/src/adlmidi_opl3.cpp
index 5f9ecdc..4638c5a 100644
--- a/src/adlmidi_opl3.cpp
+++ b/src/adlmidi_opl3.cpp
@@ -60,6 +60,11 @@ static const unsigned OPLBase = 0x388;
# ifndef ADLMIDI_DISABLE_JAVA_EMULATOR
# include "chips/java_opl3.h"
# endif
+
+// HW OPL Serial
+# ifdef ADLMIDI_ENABLE_HW_SERIAL
+# include "chips/opl_serial_port.h"
+# endif
#endif
static const unsigned adl_emulatorSupport = 0
@@ -860,6 +865,11 @@ static OplInstMeta makeEmptyInstrument()
const OplInstMeta OPL3::m_emptyInstrument = makeEmptyInstrument();
OPL3::OPL3() :
+#ifdef ADLMIDI_ENABLE_HW_SERIAL
+ m_serial(false),
+ m_serialBaud(0),
+ m_serialProtocol(0),
+#endif
m_numChips(1),
m_numFourOps(0),
m_deepTremoloMode(false),
@@ -1739,6 +1749,11 @@ void OPL3::reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler)
m_chips.resize(m_numChips, AdlMIDI_SPtr<OPLChipBase>());
#endif
+#ifdef ADLMIDI_ENABLE_HW_SERIAL
+ if(emulator >= 0) // If less than zero - it's hardware synth!
+ m_serial = false;
+#endif
+
const struct OplTimbre defaultInsCache = { 0x1557403,0x005B381, 0x49,0x80, 0x4, +0 };
m_numChannels = m_numChips * NUM_OF_CHANNELS;
m_insCache.resize(m_numChannels, defaultInsCache);
@@ -1754,16 +1769,21 @@ void OPL3::reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler)
m_channelCategory[p++] = ChanCat_Rhythm_Secondary;
}
- static const uint16_t data[] =
- {
- 0x004, 96, 0x004, 128, // Pulse timer
- 0x105, 0, 0x105, 1, 0x105, 0, // Pulse OPL3 enable
- 0x001, 32, 0x105, 1 // Enable wave, OPL3 extensions
- };
// size_t fours = m_numFourOps;
for(size_t i = 0; i < m_numChips; ++i)
{
+#ifdef ADLMIDI_ENABLE_HW_SERIAL
+ if(emulator < 0)
+ {
+ OPL_SerialPort *serial = new OPL_SerialPort;
+ serial->connectPort(m_serialName, m_serialBaud, m_serialProtocol);
+ m_chips[i].reset(serial);
+ initChip(i);
+ break; // Only one REAL chip!
+ }
+#endif
+
#ifndef ADLMIDI_HW_OPL
OPLChipBase *chip;
switch(emulator)
@@ -1795,23 +1815,52 @@ void OPL3::reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler)
break;
#endif
}
+
m_chips[i].reset(chip);
chip->setChipId((uint32_t)i);
chip->setRate((uint32_t)PCM_RATE);
+
if(m_runAtPcmRate)
chip->setRunningAtPcmRate(true);
+
# if defined(ADLMIDI_AUDIO_TICK_HANDLER)
chip->setAudioTickHandlerInstance(audioTickHandler);
# endif
#endif // ADLMIDI_HW_OPL
- /* Clean-up channels from any playing junk sounds */
- for(size_t a = 0; a < OPL3_CHANNELS_RHYTHM_BASE; ++a)
- writeRegI(i, 0xB0 + g_channelsMap[a], 0x00);
- for(size_t a = 0; a < sizeof(data) / sizeof(*data); a += 2)
- writeRegI(i, data[a], (data[a + 1]));
+ initChip(i);
}
updateChannelCategories();
silenceAll();
}
+
+void OPL3::initChip(size_t chip)
+{
+ static const uint16_t data[] =
+ {
+ 0x004, 96, 0x004, 128, // Pulse timer
+ 0x105, 0, 0x105, 1, 0x105, 0, // Pulse OPL3 enable
+ 0x001, 32, 0x105, 1 // Enable wave, OPL3 extensions
+ };
+
+ /* Clean-up channels from any playing junk sounds */
+ for(size_t a = 0; a < OPL3_CHANNELS_RHYTHM_BASE; ++a)
+ writeRegI(chip, 0xB0 + g_channelsMap[a], 0x00);
+
+ for(size_t a = 0; a < sizeof(data) / sizeof(*data); a += 2)
+ writeRegI(chip, data[a], (data[a + 1]));
+}
+
+#ifdef ADLMIDI_ENABLE_HW_SERIAL
+void OPL3::resetSerial(const std::string &serialName, unsigned int baud, unsigned int protocol)
+{
+ m_serial = true;
+ m_serialName = serialName;
+ m_serialBaud = baud;
+ m_serialProtocol = protocol;
+ m_numChips = 1; // Only one chip!
+ m_softPanning = false; // Soft-panning doesn't work on hardware
+ reset(-1, 0, NULL);
+}
+#endif
diff --git a/src/adlmidi_opl3.hpp b/src/adlmidi_opl3.hpp
index ca35157..abdc20d 100644
--- a/src/adlmidi_opl3.hpp
+++ b/src/adlmidi_opl3.hpp
@@ -74,6 +74,13 @@ private:
//! Cached BD registry value (flags register: DeepTremolo, DeepVibrato, and RhythmMode)
std::vector<uint32_t> m_regBD;
+#ifdef ADLMIDI_ENABLE_HW_SERIAL
+ bool m_serial;
+ std::string m_serialName;
+ unsigned m_serialBaud;
+ unsigned m_serialProtocol;
+#endif
+
public:
/**
* @brief MIDI bank entry
@@ -321,12 +328,12 @@ public:
*/
ADLMIDI_VolumeModels getVolumeScaleModel();
- #ifndef ADLMIDI_HW_OPL
+#ifndef ADLMIDI_HW_OPL
/**
* @brief Clean up all running emulated chip instances
*/
void clearChips();
- #endif
+#endif
/**
* @brief Reset chip properties and initialize them
@@ -335,6 +342,18 @@ public:
* @param audioTickHandler PCM-accurate clock hook
*/
void reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler);
+
+ void initChip(size_t chip);
+
+#ifdef ADLMIDI_ENABLE_HW_SERIAL
+ /**
+ * @brief Reset chip properties for hardware use
+ * @param emulator
+ * @param PCM_RATE
+ * @param audioTickHandler
+ */
+ void resetSerial(const std::string &serialName, unsigned int baud, unsigned int protocol);
+#endif
};
/**
diff --git a/src/adlmidi_sequencer.cpp b/src/adlmidi_sequencer.cpp
index 39361cb..d0b3ce5 100644
--- a/src/adlmidi_sequencer.cpp
+++ b/src/adlmidi_sequencer.cpp
@@ -156,8 +156,8 @@ double MIDIplay::Tick(double s, double granularity)
MidiSequencer &seqr = *m_sequencer;
double ret = seqr.Tick(s, granularity);
- s *= seqr.getTempoMultiplier();
- TickIterators(s);
+ // s *= seqr.getTempoMultiplier();
+ // TickIterators(s);
return ret;
}
diff --git a/src/chips/opl_serial_misc.h b/src/chips/opl_serial_misc.h
new file mode 100644
index 0000000..84711c3
--- /dev/null
+++ b/src/chips/opl_serial_misc.h
@@ -0,0 +1,317 @@
+/*
+ * Interfaces over Yamaha OPL3 (YMF262) chip emulators
+ *
+ * Copyright (c) 2017-2023 Vitaly Novichkov (Wohlstand)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef OPL_SERIAL_MISC_H
+#define OPL_SERIAL_MISC_H
+
+#if defined( __unix__) || defined(__APPLE__)
+#include <fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <cstring>
+#include <cstdio>
+#include <errno.h>
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include <string>
+
+
+class ChipSerialPortBase
+{
+public:
+ ChipSerialPortBase() {}
+ virtual ~ChipSerialPortBase() {}
+
+ bool isOpen()
+ {
+ return false;
+ }
+
+ void close() {}
+
+ bool open(const std::string &/*portName*/, unsigned /*baudRate*/)
+ {
+ return false;
+ }
+
+ int write(uint8_t */*data*/, size_t /*size*/)
+ {
+ return 0;
+ }
+};
+
+
+
+#if defined( __unix__) || defined(__APPLE__)
+class ChipSerialPort : public ChipSerialPortBase
+{
+ int m_port;
+ struct termios m_portSetup;
+
+ static unsigned int baud2enum(unsigned int baud)
+ {
+ if(baud == 0)
+ return B0;
+ else if(baud <= 50)
+ return B50;
+ else if(baud <= 75)
+ return B75;
+ else if(baud <= 110)
+ return B110;
+ else if(baud <= 134)
+ return B134;
+ else if(baud <= 150)
+ return B150;
+ else if(baud <= 200)
+ return B200;
+ else if(baud <= 300)
+ return B300;
+ else if(baud <= 600)
+ return B600;
+ else if(baud <= 1200)
+ return B1200;
+ else if(baud <= 1800)
+ return B1800;
+ else if(baud <= 2400)
+ return B2400;
+ else if(baud <= 4800)
+ return B4800;
+ else if(baud <= 9600)
+ return B9600;
+ else if(baud <= 19200)
+ return B19200;
+ else if(baud <= 38400)
+ return B38400;
+ else if(baud <= 57600)
+ return B57600;
+ else if(baud <= 115200)
+ return B115200;
+ else
+ return B230400;
+ }
+
+public:
+ ChipSerialPort() : ChipSerialPortBase()
+ {
+ m_port = 0;
+ std::memset(&m_portSetup, 0, sizeof(struct termios));
+ }
+
+ virtual ~ChipSerialPort()
+ {
+ close();
+ }
+
+ bool isOpen()
+ {
+ return m_port != 0;
+ }
+
+ void close()
+ {
+ if(m_port)
+ ::close(m_port);
+
+ m_port = 0;
+ }
+
+ bool open(const std::string &portName, unsigned baudRate)
+ {
+ if(m_port)
+ this->close();
+
+ std::string portPath = "/dev/" + portName;
+ m_port = ::open(portPath.c_str(), O_WRONLY);
+
+ if(m_port < 0)
+ {
+ std::fprintf(stderr, "-- OPL Serial ERROR: failed to open tty device `%s': %s\n", portPath.c_str(), strerror(errno));
+ std::fflush(stderr);
+ m_port = 0;
+ return false;
+ }
+
+ if(tcgetattr(m_port, &m_portSetup) != 0)
+ {
+ std::fprintf(stderr, "-- OPL Serial ERROR: failed to retrieve setup `%s': %s\n", portPath.c_str(), strerror(errno));
+ std::fflush(stderr);
+ close();
+ return false;
+ }
+
+ m_portSetup.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
+ m_portSetup.c_oflag &= ~OPOST;
+ m_portSetup.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+ m_portSetup.c_cflag &= ~(CSIZE | PARENB);
+ m_portSetup.c_cflag |= CS8;
+
+#if defined (__linux__) || defined (__CYGWIN__)
+ m_portSetup.c_cflag &= ~CBAUD;
+#endif
+
+ cfsetospeed(&m_portSetup, baud2enum(baudRate));
+
+ if(tcsetattr(m_port, TCSANOW, &m_portSetup) != 0)
+ {
+ std::fprintf(stderr, "-- OPL Serial ERROR: failed to apply setup `%s': %s\n", portPath.c_str(), strerror(errno));
+ std::fflush(stderr);
+ close();
+ return false;
+ }
+
+ return true;
+ }
+
+ int write(uint8_t *data, size_t size)
+ {
+ if(!m_port)
+ return 0;
+
+ return ::write(m_port, data, size);
+ }
+};
+
+#endif // __unix__
+
+
+
+
+#ifdef _WIN32
+
+class ChipSerialPort : public ChipSerialPortBase
+{
+ HANDLE m_port;
+
+ static unsigned int baud2enum(unsigned int baud)
+ {
+ if(baud <= 110)
+ return CBR_110;
+ else if(baud <= 300)
+ return CBR_300;
+ else if(baud <= 600)
+ return CBR_600;
+ else if(baud <= 1200)
+ return CBR_1200;
+ else if(baud <= 2400)
+ return CBR_2400;
+ else if(baud <= 4800)
+ return CBR_4800;
+ else if(baud <= 9600)
+ return CBR_9600;
+ else if(baud <= 19200)
+ return CBR_19200;
+ else if(baud <= 38400)
+ return CBR_38400;
+ else if(baud <= 57600)
+ return CBR_57600;
+ else if(baud <= 115200)
+ return CBR_115200;
+ else
+ return CBR_115200;
+ }
+
+public:
+ ChipSerialPort() : ChipSerialPortBase()
+ {
+ m_port = NULL;
+ }
+
+ virtual ~ChipSerialPort()
+ {
+ close();
+ }
+
+ bool isOpen()
+ {
+ return m_port != NULL;
+ }
+
+ void close()
+ {
+ if(m_port)
+ CloseHandle(m_port);
+
+ m_port = NULL;
+ }
+
+ bool open(const std::string &portName, unsigned baudRate)
+ {
+ if(m_port)
+ this->close();
+
+ std::string portPath = "\\\\.\\" + portName;
+ m_port = CreateFileA(portPath.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+
+ if(m_port == INVALID_HANDLE_VALUE)
+ {
+ m_port = NULL;
+ return false;
+ }
+
+ DCB dcb;
+ BOOL succ;
+
+ SecureZeroMemory(&dcb, sizeof(DCB));
+ dcb.DCBlength = sizeof(DCB);
+
+ succ = GetCommState(m_port, &dcb);
+ if(!succ)
+ {
+ this->close();
+ return false;
+ }
+
+ dcb.BaudRate = baud2enum(baudRate);
+ dcb.ByteSize = 8;
+ dcb.Parity = NOPARITY;
+ dcb.StopBits = ONESTOPBIT;
+
+ succ = SetCommState(m_port, &dcb);
+
+ if(!succ)
+ {
+ this->close();
+ return false;
+ }
+
+ return true;
+ }
+
+ int write(uint8_t *data, size_t size)
+ {
+ if(!m_port)
+ return 0;
+
+ DWORD written = 0;
+
+ if(!WriteFile(m_port, data, size, &written, 0))
+ return 0;
+
+ return written;
+ }
+};
+
+#endif // _WIN32
+
+#endif // OPL_SERIAL_MISC_H
diff --git a/src/chips/opl_serial_port.cpp b/src/chips/opl_serial_port.cpp
new file mode 100644
index 0000000..0d2e58d
--- /dev/null
+++ b/src/chips/opl_serial_port.cpp
@@ -0,0 +1,166 @@
+/*
+ * Interfaces over Yamaha OPL3 (YMF262) chip emulators
+ *
+ * Copyright (c) 2017-2023 Vitaly Novichkov (Wohlstand)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#ifdef ENABLE_HW_OPL_SERIAL_PORT
+
+#include "opl_serial_port.h"
+#include "opl_serial_misc.h"
+
+
+static size_t retrowave_protocol_serial_pack(const uint8_t *buf_in, size_t len_in, uint8_t *buf_out)
+{
+ size_t in_cursor = 0;
+ size_t out_cursor = 0;
+
+ buf_out[out_cursor] = 0x00;
+ out_cursor += 1;
+
+ uint8_t shift_count = 0;
+
+ while(in_cursor < len_in)
+ {
+ uint8_t cur_byte_out = buf_in[in_cursor] >> shift_count;
+ if(in_cursor > 0)
+ cur_byte_out |= (buf_in[in_cursor - 1] << (8 - shift_count));
+
+ cur_byte_out |= 0x01;
+ buf_out[out_cursor] = cur_byte_out;
+
+ shift_count += 1;
+ in_cursor += 1;
+ out_cursor += 1;
+ if(shift_count > 7)
+ {
+ shift_count = 0;
+ in_cursor -= 1;
+ }
+ }
+
+ if(shift_count)
+ {
+ buf_out[out_cursor] = buf_in[in_cursor - 1] << (8 - shift_count);
+ buf_out[out_cursor] |= 0x01;
+ out_cursor += 1;
+ }
+
+ buf_out[out_cursor] = 0x02;
+ out_cursor += 1;
+
+ return out_cursor;
+}
+
+OPL_SerialPort::OPL_SerialPort()
+ : m_port(NULL), m_protocol(ProtocolUnknown)
+{}
+
+OPL_SerialPort::~OPL_SerialPort()
+{
+ delete m_port;
+ m_port = NULL;
+}
+
+bool OPL_SerialPort::connectPort(const std::string& name, unsigned baudRate, unsigned protocol)
+{
+ delete m_port;
+ m_port = NULL;
+
+ // ensure audio thread reads protocol atomically and in order,
+ // so chipType() will be correct after the port is live
+ m_protocol = protocol;
+
+ // QSerialPort *port = m_port = new QSerialPort(name);
+ // port->setBaudRate(baudRate);
+ // return port->open(QSerialPort::WriteOnly);
+
+ m_port = new ChipSerialPort;
+ return m_port->open(name, baudRate);
+}
+
+void OPL_SerialPort::writeReg(uint16_t addr, uint8_t data)
+{
+ uint8_t sendBuffer[16];
+ ChipSerialPort *port = m_port;
+
+ if(!port || !port->isOpen())
+ return;
+
+ switch(m_protocol)
+ {
+ default:
+ case ProtocolArduinoOPL2:
+ {
+ if(addr >= 0x100)
+ break;
+ sendBuffer[0] = (uint8_t)addr;
+ sendBuffer[1] = (uint8_t)data;
+ port->write(sendBuffer, 2);
+ break;
+ }
+ case ProtocolNukeYktOPL3:
+ {
+ sendBuffer[0] = (addr >> 6) | 0x80;
+ sendBuffer[1] = ((addr & 0x3f) << 1) | (data >> 7);
+ sendBuffer[2] = (data & 0x7f);
+ port->write(sendBuffer, 3);
+ break;
+ }
+ case ProtocolRetroWaveOPL3:
+ {
+ bool port1 = (addr & 0x100) != 0;
+ uint8_t buf[8] =
+ {
+ static_cast<uint8_t>(0x21 << 1), 0x12,
+ static_cast<uint8_t>(port1 ? 0xe5 : 0xe1), static_cast<uint8_t>(addr & 0xff),
+ static_cast<uint8_t>(port1 ? 0xe7 : 0xe3), static_cast<uint8_t>(data),
+ 0xfb, static_cast<uint8_t>(data)
+ };
+ size_t packed_len = retrowave_protocol_serial_pack(buf, sizeof(buf), sendBuffer);
+ port->write(sendBuffer, packed_len);
+ break;
+ }
+ }
+}
+
+void OPL_SerialPort::nativeGenerate(int16_t *frame)
+{
+ frame[0] = 0;
+ frame[1] = 0;
+}
+
+const char *OPL_SerialPort::emulatorName()
+{
+ return "OPL Serial Port Driver";
+}
+
+OPLChipBase::ChipType OPL_SerialPort::chipType()
+{
+ switch(m_protocol)
+ {
+ default:
+ case ProtocolArduinoOPL2:
+ return OPLChipBase::CHIPTYPE_OPL2;
+ case ProtocolNukeYktOPL3:
+ case ProtocolRetroWaveOPL3:
+ return OPLChipBase::CHIPTYPE_OPL3;
+ }
+}
+
+#endif // ENABLE_HW_OPL_SERIAL_PORT
diff --git a/src/chips/opl_serial_port.h b/src/chips/opl_serial_port.h
new file mode 100644
index 0000000..ec7bc39
--- /dev/null
+++ b/src/chips/opl_serial_port.h
@@ -0,0 +1,65 @@
+/*
+ * Interfaces over Yamaha OPL3 (YMF262) chip emulators
+ *
+ * Copyright (c) 2017-2023 Vitaly Novichkov (Wohlstand)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#ifndef OPL_SERIAL_PORT_H
+#define OPL_SERIAL_PORT_H
+
+#ifdef ENABLE_HW_OPL_SERIAL_PORT
+
+#include <string>
+#include "opl_chip_base.h"
+
+class ChipSerialPort;
+
+class OPL_SerialPort : public OPLChipBaseT<OPL_SerialPort>
+{
+public:
+ OPL_SerialPort();
+ virtual ~OPL_SerialPort() override;
+
+ enum Protocol
+ {
+ ProtocolUnknown,
+ ProtocolArduinoOPL2,
+ ProtocolNukeYktOPL3,
+ ProtocolRetroWaveOPL3
+ };
+
+ bool connectPort(const std::string &name, unsigned baudRate, unsigned protocol);
+
+ bool canRunAtPcmRate() const override { return false; }
+ void setRate(uint32_t /*rate*/) override {}
+ void reset() override {}
+ void writeReg(uint16_t addr, uint8_t data) override;
+ void nativePreGenerate() override {}
+ void nativePostGenerate() override {}
+ void nativeGenerate(int16_t *frame) override;
+ const char *emulatorName() override;
+ ChipType chipType() override;
+
+private:
+ ChipSerialPort *m_port;
+ int m_protocol;
+};
+
+#endif // ENABLE_HW_OPL_SERIAL_PORT
+
+#endif // OPL_SERIAL_PORT_H