Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dosbox-staging/dosbox-staging.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Novak <jnovak@skedulo.com>2022-06-07 06:54:57 +0300
committerJohn Novak <jnovak@skedulo.com>2022-06-09 16:12:07 +0300
commit64ab2af35b1cd7f901329046a7a040b3530a2fe0 (patch)
treefbcd78f1bb3b786e15aae9c36c1cfc817e6db015
parent26d5ade6b24b041aa5a7dd16d1563bde4430882f (diff)
Clean implementationjnovak/adlib-gold-surround
-rw-r--r--src/hardware/adlib.cpp343
-rw-r--r--src/hardware/adlib.h1
-rw-r--r--src/libs/TDA8425_emu/TDA8425_emu.c2
-rw-r--r--vs/dosbox.vcxproj4
-rw-r--r--vs/dosbox.vcxproj.filters18
5 files changed, 265 insertions, 103 deletions
diff --git a/src/hardware/adlib.cpp b/src/hardware/adlib.cpp
index 46caa7312..23a79a5c7 100644
--- a/src/hardware/adlib.cpp
+++ b/src/hardware/adlib.cpp
@@ -38,12 +38,207 @@
#include "mame/ymf262.h"
-YM7128B_ChipIdeal* surround_module;
-TDA8425_Chip* stereo_processor;
-
#define OPL2_INTERNAL_FREQ 3600000 // The OPL2 operates at 3.6MHz
#define OPL3_INTERNAL_FREQ 14400000 // The OPL3 operates at 14.4MHz
+
+static Adlib::Module* module = nullptr;
+
+struct StereoFrame {
+ int16_t left = 0;
+ int16_t right = 0;
+};
+
+
+class AdlibGoldSurroundProcessor {
+public:
+ AdlibGoldSurroundProcessor(const uint16_t sample_rate);
+ ~AdlibGoldSurroundProcessor();
+
+ void ControlWrite(uint8_t val) noexcept;
+ StereoFrame Process(const StereoFrame &frame) noexcept;
+
+private:
+ YM7128B_ChipIdeal *chip = nullptr;
+
+ struct {
+ uint8_t sci = 0;
+ uint8_t a0 = 0;
+ uint8_t addr = 0;
+ uint8_t data = 0;
+ } control_state = {};
+};
+
+AdlibGoldSurroundProcessor::AdlibGoldSurroundProcessor(const uint16_t sample_rate)
+{
+ chip = (YM7128B_ChipIdeal *)malloc(sizeof(YM7128B_ChipIdeal));
+
+ YM7128B_ChipIdeal_Ctor(chip);
+ YM7128B_ChipIdeal_Setup(chip, sample_rate);
+ YM7128B_ChipIdeal_Reset(chip);
+ YM7128B_ChipIdeal_Start(chip);
+}
+
+AdlibGoldSurroundProcessor::~AdlibGoldSurroundProcessor()
+{
+ if (chip) {
+ YM7128B_ChipIdeal_Stop(chip);
+ YM7128B_ChipIdeal_Dtor(chip);
+ free(chip);
+ chip = nullptr;
+ }
+}
+
+void AdlibGoldSurroundProcessor::ControlWrite(uint8_t val) noexcept
+{
+ // Serial data
+ const auto din = val & 1;
+ // Bit clock
+ const auto sci = val & 2;
+ // Word clock
+ const auto a0 = val & 4;
+
+ // Change register data at the falling edge of 'a0' word clock
+ if (control_state.a0 && !a0) {
+ LOG_MSG("ADLIBGOLD.SURROUND: Write control register %d, data: %d",
+ control_state.addr,
+ control_state.data);
+ YM7128B_ChipIdeal_Write(chip, control_state.addr, control_state.data);
+ } else {
+ // Data is sent in serially through 'din' in MSB->LSB order,
+ // synchronised by the 'sci' bit clock. Data should be read on
+ // the rising edge of 'sci'.
+ if (!control_state.sci && sci) {
+ // The 'a0' word clock determines the type of the data.
+ if (a0)
+ // Data cycle
+ control_state.data = (control_state.data << 1) | din;
+ else
+ // Address cycle
+ control_state.addr = (control_state.addr << 1) | din;
+ }
+ }
+
+ control_state.sci = sci;
+ control_state.a0 = a0;
+}
+
+StereoFrame AdlibGoldSurroundProcessor::Process(const StereoFrame &frame) noexcept
+{
+ YM7128B_ChipIdeal_Process_Data data = {};
+ data.inputs[0] = frame.left + frame.right;
+
+ YM7128B_ChipIdeal_Process(chip, &data);
+
+ return {static_cast<int16_t>(data.outputs[0]),
+ static_cast<int16_t>(data.outputs[1])};
+}
+
+
+class AdlibGoldStereoProcessor {
+public:
+ AdlibGoldStereoProcessor(const uint16_t sample_rate);
+ ~AdlibGoldStereoProcessor();
+
+ void Reset() noexcept;
+ void ControlWrite(const TDA8425_Reg reg, const TDA8425_Register data) noexcept;
+ StereoFrame Process(const StereoFrame &frame) noexcept;
+
+private:
+ TDA8425_Chip *chip = nullptr;
+};
+
+AdlibGoldStereoProcessor::AdlibGoldStereoProcessor(const uint16_t sample_rate)
+{
+ chip = (TDA8425_Chip *)malloc(sizeof(TDA8425_Chip));
+
+ TDA8425_Chip_Ctor(chip);
+ TDA8425_Chip_Setup(chip,
+ sample_rate,
+ TDA8425_Pseudo_C1_Table[TDA8425_Pseudo_Preset_1],
+ TDA8425_Pseudo_C2_Table[TDA8425_Pseudo_Preset_1],
+ TDA8425_Tfilter_Mode_Disabled);
+ TDA8425_Chip_Reset(chip);
+ TDA8425_Chip_Start(chip);
+
+ Reset();
+}
+
+AdlibGoldStereoProcessor::~AdlibGoldStereoProcessor()
+{
+ if (chip) {
+ TDA8425_Chip_Stop(chip);
+ TDA8425_Chip_Dtor(chip);
+ free(chip);
+ chip = nullptr;
+ }
+}
+
+void AdlibGoldStereoProcessor::Reset() noexcept
+{
+ constexpr auto final_volume_0db = 60;
+ constexpr auto bass_0db = 6;
+ constexpr auto treble_0db = 6;
+ constexpr auto stereo_output = TDA8425_Selector_Stereo_1;
+ constexpr auto linear_stereo = TDA8425_Mode_LinearStereo << TDA8425_Reg_SF_STL;
+
+ ControlWrite(TDA8425_Reg_VL, final_volume_0db);
+ ControlWrite(TDA8425_Reg_VR, final_volume_0db);
+ ControlWrite(TDA8425_Reg_BA, bass_0db);
+ ControlWrite(TDA8425_Reg_TR, treble_0db);
+ ControlWrite(TDA8425_Reg_SF, stereo_output & linear_stereo);
+};
+
+void AdlibGoldStereoProcessor::ControlWrite(const TDA8425_Reg addr,
+ const TDA8425_Register data) noexcept
+{
+ TDA8425_Chip_Write(chip, addr, data);
+}
+
+StereoFrame AdlibGoldStereoProcessor::Process(const StereoFrame &frame) noexcept
+{
+ TDA8425_Chip_Process_Data data = {};
+ data.inputs[0][0] = frame.left;
+ data.inputs[1][0] = frame.left;
+ data.inputs[0][1] = frame.right;
+ data.inputs[1][1] = frame.right;
+
+ TDA8425_Chip_Process(chip, &data);
+
+ return {static_cast<int16_t>(data.outputs[0]),
+ static_cast<int16_t>(data.outputs[1])};
+}
+
+
+static AdlibGoldSurroundProcessor *surround_processor = nullptr;
+static AdlibGoldStereoProcessor *stereo_processor = nullptr;
+
+void adlib_gold_postprocess_and_add_samples(mixer_channel_t &chan,
+ int16_t *data, const uint32_t frames,
+ const bool surround_enabled)
+{
+ auto frames_left = frames;
+ auto buf = data;
+
+ while (frames_left--) {
+ StereoFrame frame = {buf[0], buf[1]};
+ if (surround_enabled) {
+ const auto wet = surround_processor->Process(frame);
+ constexpr auto wet_boost = 1.6;
+ frame.left += wet.left * wet_boost;
+ frame.right += wet.right * wet_boost;
+ }
+ frame = stereo_processor->Process(frame);
+
+ buf[0] = frame.left;
+ buf[1] = frame.right;
+ buf += 2;
+ }
+ // TODO add floats without conversion
+ chan->AddSamples_s16(frames, data);
+}
+
+
namespace OPL2 {
#include "opl.cpp"
@@ -86,7 +281,8 @@ struct Handler : public Adlib::Handler {
while (remaining > 0) {
const auto todo = std::min(remaining, 1024);
adlib_getsample(buf, todo);
- chan->AddSamples_s16(todo, buf);
+ adlib_gold_postprocess_and_add_samples(chan, buf, todo, true);
+ // chan->AddSamples_s16(todo, buf);
remaining -= todo;
}
}
@@ -197,28 +393,19 @@ struct Handler : public Adlib::Handler {
void Generate(mixer_channel_t &chan, uint16_t samples) override
{
int16_t buf[1024 * 2];
- YM7128B_ChipIdeal_Process_Data surround_data;
- TDA8425_Chip_Process_Data stereo_data;
while (samples > 0) {
uint32_t todo = samples > 1024 ? 1024 : samples;
OPL3_GenerateStream(&chip, buf, todo);
- for (uint32_t i = 0; i < todo; ++i) {
- surround_data.inputs[0] = (buf[i * 2] + buf[i * 2 + 1]);
- YM7128B_ChipIdeal_Process(surround_module, &surround_data);
-
- stereo_data.inputs[0][0] = buf[i * 2] + surround_data.outputs[0];
- stereo_data.inputs[1][0] = buf[i * 2] + surround_data.outputs[0];
- stereo_data.inputs[0][1] = buf[i * 2 + 1] + surround_data.outputs[1];
- stereo_data.inputs[1][1] = buf[i * 2 + 1] + surround_data.outputs[1];
- TDA8425_Chip_Process(stereo_processor, &stereo_data);
-
- buf[i * 2] = stereo_data.outputs[0] * 2;
- buf[i * 2 + 1] = stereo_data.outputs[1] * 2;
+ if (module->oplmode == OPL_opl3gold) {
+ auto surround_enabled = true;
+ adlib_gold_postprocess_and_add_samples(
+ chan, buf, todo, surround_enabled);
+ } else {
+ chan->AddSamples_s16(todo, buf);
}
- chan->AddSamples_s16(todo, buf);
samples -= todo;
}
}
@@ -232,6 +419,7 @@ struct Handler : public Adlib::Handler {
} // namespace NukedOPL
+
/*
Main Adlib implementation
@@ -592,69 +780,29 @@ void Module::DualWrite(uint8_t index, uint8_t port, uint8_t val)
CacheWrite(full_port, val);
}
-void Module::SurroundCtrlWrite(uint8_t val)
-{
- static uint8_t prev_sci = 0;
- static uint8_t prev_a0 = 0;
- static uint8_t addr = 0;
- static uint8_t data = 0;
-
- // Serial data
- const uint8_t din = val & 1;
- // Bit clock
- const uint8_t sci = val & 2;
- // Word clock
- const uint8_t a0 = val & 4;
-
- // Change register data at the falling edge of 'a0' word clock
- if (prev_a0 && !a0) {
- LOG_MSG("OPL3GOLD: Change register %d, data: %d", addr, data);
- YM7128B_ChipIdeal_Write(surround_module, addr, data);
- } else {
- // Data is sent in serially through 'din' in MSB->LSB order,
- // synchronised by the 'sci' bit clock. Data should be read on the
- // rising edge of 'sci'.
- if (!prev_sci && sci) {
- // The 'a0' word clock determines the type of the data.
- if (a0)
- // Data cycle
- data = (data << 1) | din;
- else
- // Address cycle
- addr = (addr << 1) | din;
- }
- }
-
- prev_sci = sci;
- prev_a0 = a0;
-}
-
-
void Module::CtrlWrite( uint8_t val ) {
switch ( ctrl.index ) {
- case 0x00:
- LOG_MSG("OPL3GOLD: CtrlWrite: Control/ID, data: %x", val);
- break;
case 0x04:
- LOG_MSG("OPL3GOLD: CtrlWrite: Final output volume left, data: %x", val);
- TDA8425_Chip_Write(stereo_processor, (TDA8425_Address)TDA8425_Reg_VL, val);
+ LOG_MSG("ADLIBGOLD.STEREO: Control write, final output volume left: %d", val & 0x3f);
+ stereo_processor->ControlWrite(TDA8425_Reg_VL, val);
break;
case 0x05:
- LOG_MSG("OPL3GOLD: CtrlWrite: Final output volume right, data: %x", val);
- TDA8425_Chip_Write(stereo_processor, (TDA8425_Address)TDA8425_Reg_VR, val);
+ LOG_MSG("ADLIBGOLD.STEREO: Control write, final output volume right: %d", val & 0x3f);
+ stereo_processor->ControlWrite(TDA8425_Reg_VR, val);
break;
case 0x06:
- LOG_MSG("OPL3GOLD: CtrlWrite: Bass, data: %x", val);
- TDA8425_Chip_Write(stereo_processor, (TDA8425_Address)TDA8425_Reg_BA, val);
+ LOG_MSG("ADLIBGOLD.STEREO: Control write, bass: %d", val & 0xf);
+ stereo_processor->ControlWrite(TDA8425_Reg_BA, val);
break;
case 0x07:
- LOG_MSG("OPL3GOLD: CtrlWrite: Treble, data: %x", val);
- TDA8425_Chip_Write(stereo_processor, (TDA8425_Address)TDA8425_Reg_TR, val);
+ LOG_MSG("ADLIBGOLD.STEREO: Control write, treble: %d", val & 0xf);
+ stereo_processor->ControlWrite(TDA8425_Reg_TR, val + 1);
break;
case 0x08:
- LOG_MSG("OPL3GOLD: CtrlWrite: Stereo mode, data: %x, chan: %x, mode: %x", val, val & 6, val & 18);
- TDA8425_Chip_Write(stereo_processor, (TDA8425_Address)TDA8425_Reg_SF, val);
+ LOG_MSG("ADLIBGOLD.STEREO: Control write, input selector: 0x%02x, stereo mode: 0x%02x", val & 6, val & 18);
+ stereo_processor->ControlWrite(TDA8425_Reg_SF, val);
break;
+
case 0x09: /* Left FM Volume */
ctrl.lvol = val;
goto setvol;
@@ -663,11 +811,14 @@ void Module::CtrlWrite( uint8_t val ) {
setvol:
if ( ctrl.mixer ) {
//Dune cdrom uses 32 volume steps in an apparent mistake, should be 128
+ //
mixerChan->SetVolume( (float)(ctrl.lvol&0x1f)/31.0f, (float)(ctrl.rvol&0x1f)/31.0f );
}
break;
+
+ // TODO only for adlib gold
case 0x18: /* Surround */
- SurroundCtrlWrite(val);
+ surround_processor->ControlWrite(val);
}
}
@@ -676,7 +827,7 @@ uint8_t Module::CtrlRead(void)
switch (ctrl.index) {
// case 0x00: /* Board Options */ return 0x70; // 16-bit ISA, no telephone/surround/CD-ROM
case 0x00: /* Board Options */ return 0x50; // 16-bit ISA, surround module, no telephone/CDROM
- //
+
case 0x09: /* Left FM Volume */ return ctrl.lvol;
case 0x0a: /* Right FM Volume */
return ctrl.rvol;
@@ -811,25 +962,15 @@ uint8_t Module::PortRead(io_port_t port, io_width_t)
void Module::Init( Mode m ) {
mode = m;
memset(cache, 0, ARRAY_LEN(cache));
+
switch ( mode ) {
case MODE_OPL3:
case MODE_OPL3GOLD:
- surround_module = (YM7128B_ChipIdeal*)malloc(sizeof(YM7128B_ChipIdeal));
- YM7128B_ChipIdeal_Ctor(surround_module);
- YM7128B_ChipIdeal_Setup(surround_module, mixerChan->GetSampleRate());
- YM7128B_ChipIdeal_Reset(surround_module);
- YM7128B_ChipIdeal_Start(surround_module);
-
- stereo_processor = (TDA8425_Chip*)malloc(sizeof(TDA8425_Chip));
- TDA8425_Chip_Ctor(stereo_processor);
- TDA8425_Chip_Setup(
- stereo_processor,
- mixerChan->GetSampleRate(),
- TDA8425_Pseudo_C1_Table[TDA8425_Pseudo_Preset_1],
- TDA8425_Pseudo_C2_Table[TDA8425_Pseudo_Preset_1],
- TDA8425_Tfilter_Mode_Disabled
- );
- TDA8425_Chip_Reset(stereo_processor);
+ surround_processor = new AdlibGoldSurroundProcessor(
+ mixerChan->GetSampleRate());
+
+ stereo_processor = new AdlibGoldStereoProcessor(
+ mixerChan->GetSampleRate());
break;
case MODE_OPL2:
break;
@@ -844,8 +985,6 @@ void Module::Init( Mode m ) {
} // namespace Adlib
-static Adlib::Module* module = 0;
-
static void OPL_CallBack(uint16_t len)
{
module->handler->Generate(module->mixerChan, len);
@@ -994,39 +1133,39 @@ Module::Module(Section *configuration)
const auto read_from = std::bind(&Module::PortRead, this, _1, _2);
const auto write_to = std::bind(&Module::PortWrite, this, _1, _2, _3);
- // 0x388 range
+ // 0x388-0x39b ports (read/write)
constexpr io_port_t port_0x388 = 0x388;
WriteHandler[0].Install(port_0x388, write_to, io_width_t::byte, 4);
ReadHandler[0].Install(port_0x388, read_from, io_width_t::byte, 4);
- // 0x220 range
+
+ // 0x220-0x224 ports (read/write)
if (!single) {
WriteHandler[1].Install(base, write_to, io_width_t::byte, 4);
ReadHandler[1].Install(base, read_from, io_width_t::byte, 4);
}
- //0x228 range
+ // 0x228-0x229 ports (read/write)
WriteHandler[2].Install(base + 8u, write_to, io_width_t::byte, 2);
+
+ // 0x228 port (read
ReadHandler[2].Install(base + 8u, read_from, io_width_t::byte, 1);
MAPPER_AddHandler(OPL_SaveRawEvent, SDL_SCANCODE_UNKNOWN, 0,
"caprawopl", "Rec. OPL");
}
-Module::~Module() {
+Module::~Module()
+{
delete capture;
capture = nullptr;
delete handler;
handler = nullptr;
- if (surround_module) {
- YM7128B_ChipIdeal_Stop(surround_module);
- YM7128B_ChipIdeal_Dtor(surround_module);
- free(surround_module);
- surround_module = nullptr;
+ if (surround_processor) {
+ delete surround_processor;
+ surround_processor = nullptr;
}
if (stereo_processor) {
- TDA8425_Chip_Stop(stereo_processor);
- TDA8425_Chip_Dtor(stereo_processor);
- free(stereo_processor);
+ delete stereo_processor;
stereo_processor = nullptr;
}
}
diff --git a/src/hardware/adlib.h b/src/hardware/adlib.h
index 2dbd02a0d..ca8e41b2b 100644
--- a/src/hardware/adlib.h
+++ b/src/hardware/adlib.h
@@ -173,7 +173,6 @@ class Module: public Module_base {
void CacheWrite( uint32_t reg, uint8_t val );
void DualWrite( uint8_t index, uint8_t reg, uint8_t val );
void CtrlWrite( uint8_t val );
- void SurroundCtrlWrite(uint8_t val);
uint8_t CtrlRead(void);
public:
diff --git a/src/libs/TDA8425_emu/TDA8425_emu.c b/src/libs/TDA8425_emu/TDA8425_emu.c
index eb4f17cce..6544d21ec 100644
--- a/src/libs/TDA8425_emu/TDA8425_emu.c
+++ b/src/libs/TDA8425_emu/TDA8425_emu.c
@@ -718,6 +718,8 @@ static void TDA8425_Chip_ProcessSelector(
break;
default:
+ stereo[L] = data->inputs[S1][L];
+ stereo[R] = data->inputs[S1][R];
break;
}
}
diff --git a/vs/dosbox.vcxproj b/vs/dosbox.vcxproj
index eaf355f80..301ecb185 100644
--- a/vs/dosbox.vcxproj
+++ b/vs/dosbox.vcxproj
@@ -504,6 +504,8 @@
<ClCompile Include="..\src\libs\residfp\Spline.cpp" />
<ClCompile Include="..\src\libs\residfp\WaveformCalculator.cpp" />
<ClCompile Include="..\src\libs\residfp\WaveformGenerator.cpp" />
+ <ClCompile Include="..\src\libs\TDA8425_emu\TDA8425_emu.c" />
+ <ClCompile Include="..\src\libs\YM7128B_emu\YM7128B_emu.c" />
<ClCompile Include="..\src\midi\midi.cpp" />
<ClCompile Include="..\src\midi\midi_fluidsynth.cpp" />
<ClCompile Include="..\src\midi\midi_lasynth_model.cpp" />
@@ -693,6 +695,8 @@
<ClInclude Include="..\src\libs\residfp\Voice.h" />
<ClInclude Include="..\src\libs\residfp\WaveformCalculator.h" />
<ClInclude Include="..\src\libs\residfp\WaveformGenerator.h" />
+ <ClInclude Include="..\src\libs\TDA8425_emu\TDA8425_emu.h" />
+ <ClInclude Include="..\src\libs\YM7128B_emu\YM7128B_emu.h" />
<ClInclude Include="..\src\midi\midi_fluidsynth.h" />
<ClInclude Include="..\src\midi\midi_lasynth_model.h" />
<ClInclude Include="..\src\midi\midi_mt32.h" />
diff --git a/vs/dosbox.vcxproj.filters b/vs/dosbox.vcxproj.filters
index c0631ac22..7c15d7022 100644
--- a/vs/dosbox.vcxproj.filters
+++ b/vs/dosbox.vcxproj.filters
@@ -499,6 +499,12 @@
<ClCompile Include="..\src\libs\residfp\WaveformGenerator.cpp">
<Filter>src\libs\residfp</Filter>
</ClCompile>
+ <ClCompile Include="..\src\libs\TDA8425_emu\TDA8425_emu.c">
+ <Filter>src\libs\TDA8425_emu</Filter>
+ </ClCompile>
+ <ClCompile Include="..\src\libs\YM7128B_emu\YM7128B_emu.c">
+ <Filter>src\libs\YM7128B_emu</Filter>
+ </ClCompile>
<ClCompile Include="..\src\misc\pacer.cpp">
<Filter>src\misc</Filter>
</ClCompile>
@@ -1161,6 +1167,12 @@
<ClInclude Include="..\src\libs\residfp\WaveformGenerator.h">
<Filter>src\libs\residfp</Filter>
</ClInclude>
+ <ClInclude Include="..\src\libs\TDA8425_emu\TDA8425_emu.h">
+ <Filter>src\libs\TDA8425_emu</Filter>
+ </ClInclude>
+ <ClInclude Include="..\src\libs\YM7128B_emu\YM7128B_emu.h">
+ <Filter>src\libs\YM7128B_emu</Filter>
+ </ClInclude>
<ClInclude Include="..\src\cpu\core_dyn_x86\risc_x64.h">
<Filter>src\cpu\core_dyn_x86</Filter>
</ClInclude>
@@ -1317,6 +1329,12 @@
<Filter Include="src\libs\residfp">
<UniqueIdentifier>{e8ef008d-ceb9-4224-9b9e-b5b62933ca0d}</UniqueIdentifier>
</Filter>
+ <Filter Include="src\libs\TDA8425_emu">
+ <UniqueIdentifier>{497bd83b-b828-408d-9086-72213cf00934}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\libs\YM7128B_emu">
+ <UniqueIdentifier>{033fc9cc-5916-480d-a3f8-839ae0d7a1b0}</UniqueIdentifier>
+ </Filter>
<Filter Include="src\libs\loguru">
<UniqueIdentifier>{61f19037-c60b-46ea-8d28-53fbf18dc6db}</UniqueIdentifier>
</Filter>