aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorWohlstand <admin@wohlnet.ru>2025-03-29 00:44:52 +0300
committerWohlstand <admin@wohlnet.ru>2025-03-29 00:44:52 +0300
commite92a4d7285197ab2868aa36f975d73ceee915bea (patch)
treeb5e3af6e6c58145a62ec6394a40e9e65d26a5fcf /src
parent7afda0483ab0af9db624052321b42c3d2a245e75 (diff)
downloadlibADLMIDI-e92a4d7285197ab2868aa36f975d73ceee915bea.tar.gz
libADLMIDI-e92a4d7285197ab2868aa36f975d73ceee915bea.tar.bz2
libADLMIDI-e92a4d7285197ab2868aa36f975d73ceee915bea.zip
Refactored DOS support
Diffstat (limited to 'src')
-rw-r--r--src/adlmidi.cpp39
-rw-r--r--src/adlmidi_midiplay.cpp4
-rw-r--r--src/adlmidi_opl3.cpp127
-rw-r--r--src/adlmidi_opl3.hpp49
-rw-r--r--src/adlmidi_private.hpp11
-rw-r--r--src/chips/dos_hw_opl.cpp163
-rw-r--r--src/chips/dos_hw_opl.h48
7 files changed, 359 insertions, 82 deletions
diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp
index 7fbf134..b81a342 100644
--- a/src/adlmidi.cpp
+++ b/src/adlmidi.cpp
@@ -24,11 +24,12 @@
#include "adlmidi_midiplay.hpp"
#include "adlmidi_opl3.hpp"
#include "adlmidi_private.hpp"
-#ifndef ADLMIDI_HW_OPL
#include "chips/opl_chip_base.h"
-#endif
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
-#include "midi_sequencer.hpp"
+# include "midi_sequencer.hpp"
+#endif
+#ifdef ENABLE_HW_OPL_DOS
+# include "chips/dos_hw_opl.h"
#endif
#if defined(_MSC_VER) && _MSC_VER < 1900
@@ -138,8 +139,7 @@ ADLMIDI_EXPORT int adl_setNumChips(ADL_MIDIPlayer *device, int numChips)
#endif
#ifdef ADLMIDI_HW_OPL
- ADL_UNUSED(numChips);
- play->m_setup.numChips = 1;
+ play->m_setup.numChips = numChips > 2 ? 1 : static_cast<unsigned int>(numChips);
#else
play->m_setup.numChips = static_cast<unsigned int>(numChips);
#endif
@@ -835,15 +835,11 @@ ADLMIDI_EXPORT const char *adl_chipEmulatorName(struct ADL_MIDIPlayer *device)
{
if(device)
{
-#ifndef ADLMIDI_HW_OPL
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
Synth &synth = *play->m_synth;
if(!synth.m_chips.empty())
return synth.m_chips[0]->emulatorName();
-#else
- return "Hardware OPL3 chip on 0x330";
-#endif
}
return "Unknown";
}
@@ -910,6 +906,31 @@ ADLMIDI_EXPORT int adl_switchSerialHW(struct ADL_MIDIPlayer *device,
return -1;
}
+int adl_switchDOSHW(int chipType, ADL_UInt16 baseAddress)
+{
+#ifdef ENABLE_HW_OPL_DOS
+ if(baseAddress > 0)
+ DOS_HW_OPL::setOplAddress(baseAddress);
+
+ switch(chipType)
+ {
+ case ADLMIDI_DOS_ChipAuto:
+ break;
+
+ case ADLMIDI_DOS_ChipOPL2:
+ DOS_HW_OPL::setChipType(OPLChipBase::CHIPTYPE_OPL2);
+ break;
+ case ADLMIDI_DOS_ChipOPL3:
+ DOS_HW_OPL::setChipType(OPLChipBase::CHIPTYPE_OPL3);
+ break;
+ }
+#else
+ (void)chipType;
+ (void)baseAddress;
+#endif
+ return -1;
+}
+
ADLMIDI_EXPORT const char *adl_linkedLibraryVersion()
{
#if !defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp
index 0bc0191..9db4b67 100644
--- a/src/adlmidi_midiplay.cpp
+++ b/src/adlmidi_midiplay.cpp
@@ -89,7 +89,11 @@ MIDIplay::MIDIplay(unsigned long sampleRate):
m_setup.bankId = 0;
m_setup.numFourOps = -1;
+#ifdef ADLMIDI_ENABLE_HW_DOS
+ m_setup.numChips = 1;
+#else
m_setup.numChips = 2;
+#endif
m_setup.deepTremoloMode = -1;
m_setup.deepVibratoMode = -1;
m_setup.rhythmMode = -1;
diff --git a/src/adlmidi_opl3.cpp b/src/adlmidi_opl3.cpp
index c612637..97760cf 100644
--- a/src/adlmidi_opl3.cpp
+++ b/src/adlmidi_opl3.cpp
@@ -26,12 +26,9 @@
#include <stdlib.h>
#include <cassert>
-#ifndef DISABLE_EMBEDDED_BANKS
-#include "wopl/wopl_file.h"
-#endif
-#ifdef ADLMIDI_HW_OPL
-static const unsigned OPLBase = 0x388;
+#ifdef ENABLE_HW_OPL_DOS
+# include "chips/dos_hw_opl.h"
#else
# if defined(ADLMIDI_DISABLE_NUKED_EMULATOR) && \
defined(ADLMIDI_DISABLE_DOSBOX_EMULATOR) && \
@@ -84,7 +81,7 @@ static const unsigned OPLBase = 0x388;
#endif
static const unsigned adl_emulatorSupport = 0
-#ifndef ADLMIDI_HW_OPL
+#ifndef ENABLE_HW_OPL_DOS
# ifndef ADLMIDI_DISABLE_NUKED_EMULATOR
| (1u << ADLMIDI_EMU_NUKED) | (1u << ADLMIDI_EMU_NUKED_174)
# endif
@@ -936,7 +933,9 @@ OPL3::OPL3() :
OPL3::~OPL3()
{
-#ifdef ADLMIDI_HW_OPL
+ m_curState.clear();
+
+#ifdef ENABLE_HW_OPL_DOS
silenceAll();
writeRegI(0, 0x0BD, 0);
writeRegI(0, 0x104, 0);
@@ -1004,48 +1003,17 @@ void OPL3::setEmbeddedBank(uint32_t bank)
void OPL3::writeReg(size_t chip, uint16_t address, uint8_t value)
{
-#ifdef ADLMIDI_HW_OPL
- ADL_UNUSED(chip);
- unsigned o = address >> 8;
- unsigned port = OPLBase + o * 2;
-
-# ifdef __DJGPP__
- outportb(port, address);
- for(unsigned c = 0; c < 6; ++c) inportb(port);
- outportb(port + 1, value);
- for(unsigned c = 0; c < 35; ++c) inportb(port);
-# endif
-
-# ifdef __WATCOMC__
- outp(port, address);
- for(uint16_t c = 0; c < 6; ++c) inp(port);
- outp(port + 1, value);
- for(uint16_t c = 0; c < 35; ++c) inp(port);
-# endif//__WATCOMC__
-
-#else//ADLMIDI_HW_OPL
m_chips[chip]->writeReg(address, value);
-#endif
}
void OPL3::writeRegI(size_t chip, uint32_t address, uint32_t value)
{
-#ifdef ADLMIDI_HW_OPL
- writeReg(chip, static_cast<uint16_t>(address), static_cast<uint8_t>(value));
-#else//ADLMIDI_HW_OPL
m_chips[chip]->writeReg(static_cast<uint16_t>(address), static_cast<uint8_t>(value));
-#endif
}
void OPL3::writePan(size_t chip, uint32_t address, uint32_t value)
{
-#ifndef ADLMIDI_HW_OPL
m_chips[chip]->writePan(static_cast<uint16_t>(address), static_cast<uint8_t>(value));
-#else
- ADL_UNUSED(chip);
- ADL_UNUSED(address);
- ADL_UNUSED(value);
-#endif
}
@@ -1579,7 +1547,7 @@ void OPL3::setPan(size_t c, uint8_t value)
if(g_channelsMapPan[cc] != 0xFFF)
{
-#ifndef ADLMIDI_HW_OPL
+#ifndef ENABLE_HW_OPL_DOS
if (m_softPanningSup && m_softPanning)
{
writePan(chip, g_channelsMapPan[cc], value);
@@ -1595,7 +1563,7 @@ void OPL3::setPan(size_t c, uint8_t value)
m_regC0[c] = panning;
writePan(chip, g_channelsMapPan[cc], 64);
writeRegI(chip, 0xC0 + g_channelsMapPan[cc], m_insCache[c].feedconn | panning);
-#ifndef ADLMIDI_HW_OPL
+#ifndef ENABLE_HW_OPL_DOS
}
#endif
}
@@ -1795,48 +1763,72 @@ ADLMIDI_VolumeModels OPL3::getVolumeScaleModel()
}
}
-#ifndef ADLMIDI_HW_OPL
void OPL3::clearChips()
{
for(size_t i = 0; i < m_chips.size(); i++)
m_chips[i].reset(NULL);
m_chips.clear();
}
-#endif
void OPL3::reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler)
{
-#ifndef ADLMIDI_HW_OPL
- clearChips();
-#else
- (void)emulator;
- (void)PCM_RATE;
-#endif
+ bool rebuild_needed = m_curState.cmp(emulator, m_numChips);
+
+ if(rebuild_needed)
+ clearChips();
+
#if !defined(ADLMIDI_AUDIO_TICK_HANDLER)
(void)audioTickHandler;
#endif
- m_insCache.clear();
- m_keyBlockFNumCache.clear();
- m_regBD.clear();
-#ifndef ADLMIDI_HW_OPL
- m_chips.resize(m_numChips, AdlMIDI_SPtr<OPLChipBase>());
-#endif
+ const struct OplTimbre defaultInsCache = { 0x1557403,0x005B381, 0x49,0x80, 0x4, +0 };
+
+ if(rebuild_needed)
+ {
+ m_insCache.clear();
+ m_keyBlockFNumCache.clear();
+ m_regBD.clear();
+ m_regC0.clear();
+ m_channelCategory.clear();
+ m_chips.resize(m_numChips, AdlMIDI_SPtr<OPLChipBase>());
+ }
+ else
+ {
+ adl_fill_vector<OplTimbre>(m_insCache, defaultInsCache);
+ adl_fill_vector<uint32_t>(m_channelCategory, 0);
+ adl_fill_vector<uint32_t>(m_keyBlockFNumCache, 0);
+ adl_fill_vector<uint32_t>(m_regBD, 0);
+ adl_fill_vector<uint8_t>(m_regC0, OPL_PANNING_BOTH);
+ }
#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);
- m_keyBlockFNumCache.resize(m_numChannels, 0);
- m_regBD.resize(m_numChips, 0);
- m_regC0.resize(m_numChips * m_numChannels, OPL_PANNING_BOTH);
- m_channelCategory.resize(m_numChannels, 0);
+ if(rebuild_needed)
+ {
+ m_numChannels = m_numChips * NUM_OF_CHANNELS;
+ m_insCache.resize(m_numChannels, defaultInsCache);
+ m_channelCategory.resize(m_numChannels, 0);
+ m_keyBlockFNumCache.resize(m_numChannels, 0);
+ m_regBD.resize(m_numChips, 0);
+ m_regC0.resize(m_numChips * m_numChannels, OPL_PANNING_BOTH);
+ }
- for(size_t i = 0; i < m_numChips; ++i)
+ if(!rebuild_needed)
+ {
+ bool newRate = m_curState.cmp_rate(PCM_RATE);
+
+ for(size_t i = 0; i < m_numChips; ++i)
+ {
+ if(newRate)
+ m_chips[i]->setRate(PCM_RATE);
+
+ initChip(i);
+ }
+ }
+ else for(size_t i = 0; i < m_numChips; ++i)
{
#ifdef ADLMIDI_ENABLE_HW_SERIAL
if(emulator < 0)
@@ -1849,8 +1841,11 @@ void OPL3::reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler)
}
#endif
-#ifndef ADLMIDI_HW_OPL
OPLChipBase *chip;
+#ifdef ENABLE_HW_OPL_DOS
+ chip = new DOS_HW_OPL();
+
+#else // ENABLE_HW_OPL_DOS
switch(emulator)
{
default:
@@ -1898,18 +1893,20 @@ void OPL3::reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler)
break;
#endif
}
+#endif // ENABLE_HW_OPL_DOS
m_chips[i].reset(chip);
chip->setChipId((uint32_t)i);
chip->setRate((uint32_t)PCM_RATE);
+#ifndef ENABLE_HW_OPL_DOS
if(m_runAtPcmRate)
chip->setRunningAtPcmRate(true);
+#endif
-# if defined(ADLMIDI_AUDIO_TICK_HANDLER)
+# if defined(ADLMIDI_AUDIO_TICK_HANDLER) && !defined(ENABLE_HW_OPL_DOS)
chip->setAudioTickHandlerInstance(audioTickHandler);
# endif
-#endif // ADLMIDI_HW_OPL
initChip(i);
}
diff --git a/src/adlmidi_opl3.hpp b/src/adlmidi_opl3.hpp
index 362b301..aee049f 100644
--- a/src/adlmidi_opl3.hpp
+++ b/src/adlmidi_opl3.hpp
@@ -60,10 +60,8 @@ public:
uint32_t m_numChannels;
//! Just a padding. Reserved.
char _padding[4];
-#ifndef ADLMIDI_HW_OPL
//! Running chip emulators
std::vector<AdlMIDI_SPtr<OPLChipBase > > m_chips;
-#endif
private:
//! Cached patch data, needed by Touch()
@@ -90,6 +88,51 @@ private:
//! Number channels per chip
size_t m_perChipChannels;
+ /*!
+ * \brief Current state of the synth (if values matched to setup, chips and arrays won't be fully re-created)
+ */
+ struct State
+ {
+ int emulator;
+ uint32_t numChips;
+ unsigned long pcm_rate;
+
+ State()
+ {
+ clear();
+ }
+
+ void clear()
+ {
+ emulator = -2;
+ numChips = 0;
+ pcm_rate = 0;
+ }
+
+ bool cmp_rate(unsigned long rate)
+ {
+ bool ret = pcm_rate != rate;
+
+ if(ret)
+ pcm_rate = rate;
+
+ return ret;
+ }
+
+ bool cmp(int emu, uint32_t chips)
+ {
+ bool ret = emu != emulator || chips != numChips;
+
+ if(ret)
+ {
+ emulator = emu;
+ numChips = chips;
+ }
+
+ return ret;
+ }
+ } m_curState;
+
public:
/**
* @brief MIDI bank entry
@@ -339,12 +382,10 @@ public:
*/
ADLMIDI_VolumeModels getVolumeScaleModel();
-#ifndef ADLMIDI_HW_OPL
/**
* @brief Clean up all running emulated chip instances
*/
void clearChips();
-#endif
/**
* @brief Reset chip properties and initialize them
diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp
index 0b1e92f..dcd0d03 100644
--- a/src/adlmidi_private.hpp
+++ b/src/adlmidi_private.hpp
@@ -60,9 +60,6 @@ typedef int32_t ssize_t;
#endif
#if defined(__DJGPP__) || (defined(__WATCOMC__) && (defined(__DOS__) || defined(__DOS4G__) || defined(__DOS4GNZ__)))
-# ifndef ADLMIDI_HW_OPL
-# define ADLMIDI_HW_OPL
-# endif
# include <conio.h>
# ifdef __DJGPP__
# include <pc.h>
@@ -157,7 +154,7 @@ class MIDIplay;
#define ADL_UNUSED(x) (void)x
-#ifdef ADLMIDI_HW_OPL
+#ifdef ENABLE_HW_OPL_DOS
#define ADL_MAX_CHIPS 1
#define ADL_MAX_CHIPS_STR "1" //Why not just "#MaxCards" ? Watcom fails to pass this with "syntax error" :-P
#else
@@ -214,6 +211,12 @@ inline int32_t adl_cvtU32(int32_t x)
return (uint32_t)adl_cvtS32(x) - (uint32_t)INT32_MIN;
}
+template<typename T>
+void adl_fill_vector(std::vector<T > &v, const T &value)
+{
+ for(typename std::vector<T>::iterator it = v.begin(); it != v.end(); ++it)
+ *it = value;
+}
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
extern void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate);
diff --git a/src/chips/dos_hw_opl.cpp b/src/chips/dos_hw_opl.cpp
new file mode 100644
index 0000000..a9f4765
--- /dev/null
+++ b/src/chips/dos_hw_opl.cpp
@@ -0,0 +1,163 @@
+/*
+ * Interfaces over Yamaha OPL3 (YMF262) chip emulators
+ *
+ * Copyright (c) 2017-2025 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
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#ifdef __DJGPP__
+# include <pc.h>
+#endif
+
+#include "dos_hw_opl.h"
+
+static uint16_t s_OPLBase[2] = {0x388, 0x38A};
+static char s_devName[80] = {0};
+static OPLChipBase::ChipType s_type = OPLChipBase::CHIPTYPE_OPL3;
+static bool s_detected = false;
+
+static void s_updateDevName()
+{
+ const char *oplName = (s_type == OPLChipBase::CHIPTYPE_OPL3) ? "OPL3" : "OPL2";
+ memset(s_devName, 0, sizeof(s_devName));
+ snprintf(s_devName, 80, "%s at 0x%03X", oplName, s_OPLBase[0]);
+}
+
+static void s_detect()
+{
+ if(s_detected)
+ return;
+
+ const char *blaster = getenv("BLASTER");
+ if(blaster)
+ {
+ size_t len = strlen(blaster);
+ for(size_t i = 0; i < len - 1; ++i)
+ {
+ if(blaster[i] == 'T')
+ {
+ switch(blaster[i + 1])
+ {
+ case '1':
+ case '2':
+ s_type = OPLChipBase::CHIPTYPE_OPL2;
+ break;
+ case '3':
+ case '4':
+ case '6':
+ s_type = OPLChipBase::CHIPTYPE_OPL3;
+ break;
+ default:
+ s_type = OPLChipBase::CHIPTYPE_OPL2;
+ break;
+ }
+
+ // printf("-- Detected BLASTER T%c\n", blaster[i + 1]);
+
+ break;
+ }
+ }
+ }
+ else
+ s_type = OPLChipBase::CHIPTYPE_OPL2;
+
+ s_detected = true;
+}
+
+DOS_HW_OPL::DOS_HW_OPL()
+{
+ s_detect();
+ s_updateDevName();
+}
+
+void DOS_HW_OPL::setOplAddress(uint16_t address)
+{
+ s_OPLBase[0] = address;
+ s_OPLBase[1] = address + 2;
+ s_updateDevName();
+}
+
+void DOS_HW_OPL::setChipType(ChipType type)
+{
+ s_type = type;
+ s_detected = true; // Assignd manually, no need to detect
+}
+
+DOS_HW_OPL::~DOS_HW_OPL()
+{
+ DOS_HW_OPL::writeReg(0x0BD, 0);
+ if(s_type == CHIPTYPE_OPL3)
+ {
+ DOS_HW_OPL::writeReg(0x104, 0);
+ DOS_HW_OPL::writeReg(0x105, 0);
+ }
+}
+
+void DOS_HW_OPL::writeReg(uint16_t addr, uint8_t data)
+{
+ assert(m_id <= 1);
+ unsigned o = addr >> 8;
+ unsigned port = s_OPLBase[m_id] + o * 2;
+
+# ifdef __DJGPP__
+ outportb(port, addr);
+
+ for(unsigned c = 0; c < 6; ++c)
+ inportb(port);
+
+ outportb(port + 1, data);
+
+ for(unsigned c = 0; c < 35; ++c)
+ inportb(port);
+# endif
+
+# ifdef __WATCOMC__
+ outp(port, addr);
+
+ for(uint16_t c = 0; c < 6; ++c)
+ inp(port);
+
+ outp(port + 1, data);
+
+ for(uint16_t c = 0; c < 35; ++c)
+ inp(port);
+# endif//__WATCOMC__
+}
+
+void DOS_HW_OPL::nativeGenerate(int16_t *frame)
+{
+ frame[0] = 0;
+ frame[1] = 0;
+}
+
+const char *DOS_HW_OPL::emulatorName()
+{
+ return s_devName;
+}
+
+bool DOS_HW_OPL::hasFullPanning()
+{
+ return false;
+}
+
+OPLChipBase::ChipType DOS_HW_OPL::chipType()
+{
+ return s_type;
+}
diff --git a/src/chips/dos_hw_opl.h b/src/chips/dos_hw_opl.h
new file mode 100644
index 0000000..5e3750b
--- /dev/null
+++ b/src/chips/dos_hw_opl.h
@@ -0,0 +1,48 @@
+/*
+ * Interfaces over Yamaha OPL3 (YMF262) chip emulators
+ *
+ * Copyright (c) 2017-2025 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 DOS_HW_OPL_H
+#define DOS_HW_OPL_H
+
+#include "opl_chip_base.h"
+
+class DOS_HW_OPL : public OPLChipBaseT<DOS_HW_OPL>
+{
+public:
+ DOS_HW_OPL();
+ virtual ~DOS_HW_OPL() override;
+
+ static void setChipType(ChipType type);
+ static void setOplAddress(uint16_t address);
+
+ 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;
+ bool hasFullPanning() override;
+};
+
+#endif // DOS_HW_OPL_H