aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt12
-rw-r--r--include/adlmidi.h57
-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.cpp77
-rw-r--r--src/adlmidi_opl3.hpp23
-rw-r--r--src/adlmidi_sequencer.cpp4
-rw-r--r--src/chips/opl_serial_misc.h338
-rw-r--r--src/chips/opl_serial_port.cpp166
-rw-r--r--src/chips/opl_serial_port.h65
-rw-r--r--utils/midiplay/CMakeLists.txt4
-rw-r--r--utils/midiplay/adlmidiplay.cpp1251
-rw-r--r--utils/midiplay/utf8main.cmake10
-rw-r--r--utils/midiplay/utf8main.h41
-rw-r--r--utils/midiplay/utf8main_win32.cpp95
-rw-r--r--utils/vlc_codec/libadlmidi.c102
18 files changed, 1962 insertions, 394 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2fd89e9..dcfddc9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -189,6 +189,7 @@ option(USE_DOSBOX_EMULATOR "Use DosBox 0.74 OPL3 emulator (semi-accurate, sugge
option(USE_NUKED_EMULATOR "Use Nuked OPL3 emulator (most accurate, powerful)" ${DEFAULT_HEAVY_EMULATORS})
option(USE_OPAL_EMULATOR "Use Opal emulator (inaccurate)" ${DEFAULT_HEAVY_EMULATORS})
option(USE_JAVA_EMULATOR "Use JavaOPL emulator" ${DEFAULT_HEAVY_EMULATORS})
+option(USE_HW_SERIAL "Use the hardware OPL3 chip via Serial on modern systems" OFF)
option(WITH_GENADLDATA "Build and run full rebuild of embedded banks cache" OFF)
option(WITH_GENADLDATA_COMMENTS "Enable comments in a generated embedded instruments cache file" OFF)
@@ -286,6 +287,15 @@ function(handle_options targetLib)
target_compile_definitions(${targetLib} PUBLIC ADLMIDI_DISABLE_JAVA_EMULATOR)
endif()
+ if(USE_HW_SERIAL)
+ set(HAS_EMULATOR TRUE)
+ target_sources(${targetLib} PRIVATE
+ ${libADLMIDI_SOURCE_DIR}/src/chips/opl_serial_port.cpp
+ )
+ target_compile_definitions(${targetLib} PRIVATE ENABLE_HW_OPL_SERIAL_PORT)
+ target_compile_definitions(${targetLib} PUBLIC ADLMIDI_ENABLE_HW_SERIAL)
+ endif()
+
if(NOT HAS_EMULATOR)
message(FATAL_ERROR "No emulators enabled! You must enable at least one emulator!")
endif()
@@ -414,6 +424,7 @@ function(libADLMIDI_find_SDL2)
add_library(ADLMIDI_SDL2 INTERFACE)
if(TARGET SDL2::SDL2)
target_link_libraries(ADLMIDI_SDL2 INTERFACE SDL2::SDL2)
+ target_include_directories(ADLMIDI_SDL2 INTERFACE ${SDL2_INCLUDE_DIRS})
else()
string(STRIP ${SDL2_LIBRARIES} SDL2_LIBRARIES)
target_include_directories(ADLMIDI_SDL2 INTERFACE ${SDL2_INCLUDE_DIRS})
@@ -547,6 +558,7 @@ message("USE_DOSBOX_EMULATOR = ${USE_DOSBOX_EMULATOR}")
message("USE_NUKED_EMULATOR = ${USE_NUKED_EMULATOR}")
message("USE_OPAL_EMULATOR = ${USE_OPAL_EMULATOR}")
message("USE_JAVA_EMULATOR = ${USE_JAVA_EMULATOR}")
+message("USE_HW_SERIAL = ${USE_HW_SERIAL}")
message("===== Utils and extras =====")
message("WITH_GENADLDATA = ${WITH_GENADLDATA}")
diff --git a/include/adlmidi.h b/include/adlmidi.h
index 3dd9d00..e78e920 100644
--- a/include/adlmidi.h
+++ b/include/adlmidi.h
@@ -738,6 +738,30 @@ typedef struct {
extern ADLMIDI_DECLSPEC int adl_setRunAtPcmRate(struct ADL_MIDIPlayer *device, int enabled);
/**
+ * @brief The list of serial port protocols
+ */
+enum ADL_SerialProtocol
+{
+ ADLMIDI_SerialProtocol_Unknown = 0,
+ ADLMIDI_SerialProtocol_ArduinoOPL2,
+ ADLMIDI_SerialProtocol_NukeYktOPL3,
+ ADLMIDI_SerialProtocol_RetroWaveOPL3,
+ ADLMIDI_SerialProtocol_END
+};
+
+/**
+ * @brief Switch the synthesizer into hardware mode using Serial port
+ * @param name The name of the serial port device (it may look different on various platforms. On UNIX-like systems don't type the /dev/ prefix: only name).
+ * @param baud The baud speed of the serial port
+ * @param protocol The binary protocol used to communicate the device (#ADL_SerialProtocol)
+ * @return 0 on success, <0 when any error has occurred
+ */
+extern ADLMIDI_DECLSPEC int adl_switchSerialHW(struct ADL_MIDIPlayer *device,
+ const char *name,
+ unsigned baud,
+ unsigned protocol);
+
+/**
* @brief Set 4-bit device identifier. Used by the SysEx processor.
* @param device Instance of the library
* @param id 4-bit device identifier
@@ -1152,6 +1176,39 @@ extern ADLMIDI_DECLSPEC int adl_generateFormat(struct ADL_MIDIPlayer *device, i
*/
extern ADLMIDI_DECLSPEC double adl_tickEvents(struct ADL_MIDIPlayer *device, double seconds, double granulality);
+/**
+ * @brief Periodic tick handler without iterators.
+ *
+ * Unlike adl_tickEvents(), it doesn't handles iterators, you need to perform
+ * them naually via adl_tickIterators().
+ *
+ * Notice: The function is provided to use it with Hardware OPL3 mode or for the purpose to iterate
+ * MIDI playback without of sound generation.
+ *
+ * DON'T USE IT TOGETHER WITH adl_play() and adl_playFormat() calls
+ * as there are all using this function internally!!!
+ *
+ * @param device Instance of the library
+ * @param seconds Previous delay. On a first moment, pass the `0.0`
+ * @param granulality Minimal size of one MIDI tick in seconds.
+ * @return desired number of seconds until next call. Pass this value into `seconds` field in next time
+ */
+extern ADLMIDI_DECLSPEC double adl_tickEventsOnly(struct ADL_MIDIPlayer *device, double seconds, double granulality);
+
+
+/**
+ * @brief Periodic tick hander for the real-time hardware output
+ *
+ * This function runs a single step of vibrato, auto-arpeggio, and the portamento of @seconds duration.
+ *
+ * When running the libADLMIDI as a real-time driver for the ral hardware, call
+ * this function from the timer and specify the @seconds value with a delay of the single cycle.
+ *
+ * @param device Instance of the library
+ * @param seconds Previous delay. On a first moment, pass the `0.0`
+ */
+extern ADLMIDI_DECLSPEC void adl_tickIterators(struct ADL_MIDIPlayer *device, double seconds);
+
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..b934475 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,58 @@ 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
+ 0x08, 0 // CSW/Note Sel
+ };
+
+ /* Clean-up channels from any playing junk sounds */
+ for(size_t a = 0; a < OPL3_CHANNELS_RHYTHM_BASE; ++a)
+ {
+ writeRegI(chip, 0x20 + g_operatorsMap[a * 2], 0x00);
+ writeRegI(chip, 0x20 + g_operatorsMap[(a * 2) + 1], 0x00);
+ writeRegI(chip, 0xA0 + g_channelsMap[a], 0x00);
+ 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..2583a4b
--- /dev/null
+++ b/src/chips/opl_serial_misc.h
@@ -0,0 +1,338 @@
+/*
+ * 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>
+#include <sys/ioctl.h>
+#endif
+
+#ifdef __APPLE__
+#include <IOKit/serial/ioss.h>
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include <string>
+
+
+class ChipSerialPortBase
+{
+public:
+ ChipSerialPortBase() {}
+ virtual ~ChipSerialPortBase() {}
+
+ virtual bool isOpen()
+ {
+ return false;
+ }
+
+ virtual void close() {}
+
+ virtual bool open(const std::string &/*portName*/, unsigned /*baudRate*/)
+ {
+ return false;
+ }
+
+ virtual 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
+
+#if defined (BSD) || defined(__FreeBSD__)
+ cfsetispeed(&m_portSetup, baudRate);
+ cfsetospeed(&m_portSetup, baudRate);
+#elif !defined(__APPLE__)
+ cfsetospeed(&m_portSetup, baud2enum(baudRate));
+#endif
+
+ 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;
+ }
+
+#ifdef __APPLE__
+ if(ioctl(m_port, IOSSIOSPEED, &baudRate) == -1)
+ {
+ std::fprintf(stderr, "-- OPL Serial ERROR: Failed to set MacOS specific tty attributes for `%s': %s", portPath.c_str(), strerror(errno));
+ std::fflush(stderr);
+ close();
+ return false;
+ }
+#endif
+
+ 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..1607d8c
--- /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];
+ ChipSerialPortBase *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..346c68e
--- /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 ChipSerialPortBase;
+
+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:
+ ChipSerialPortBase *m_port;
+ int m_protocol;
+};
+
+#endif // ENABLE_HW_OPL_SERIAL_PORT
+
+#endif // OPL_SERIAL_PORT_H
diff --git a/utils/midiplay/CMakeLists.txt b/utils/midiplay/CMakeLists.txt
index 52d0128..3d5eeda 100644
--- a/utils/midiplay/CMakeLists.txt
+++ b/utils/midiplay/CMakeLists.txt
@@ -7,9 +7,12 @@ if(NOT ADLMIDI_DOS AND NOT MIDIPLAY_WAVE_ONLY)
endif()
endif()
+include(utf8main.cmake)
+
set(ADLMIDI_PLAY_SRC
adlmidiplay.cpp
wave_writer.c
+ ${UTF8MAIN_SRCS}
)
if(USE_SDL2_AUDIO)
@@ -30,6 +33,7 @@ add_executable(adlmidiplay ${ADLMIDI_PLAY_SRC})
if(USE_SDL2_AUDIO)
target_link_libraries(adlmidiplay PRIVATE ADLMIDI_SDL2)
+ target_compile_definitions(adlmidiplay PRIVATE ADLMIDI_USE_SDL2)
elseif(WIN32)
target_link_libraries(adlmidiplay PRIVATE winmm)
endif()
diff --git a/utils/midiplay/adlmidiplay.cpp b/utils/midiplay/adlmidiplay.cpp
index fe0a758..cef025a 100644
--- a/utils/midiplay/adlmidiplay.cpp
+++ b/utils/midiplay/adlmidiplay.cpp
@@ -34,6 +34,60 @@
#include <algorithm>
#include <signal.h>
#include <stdint.h>
+#include "utf8main.h"
+
+#if defined(ADLMIDI_ENABLE_HW_SERIAL) && !defined(OUTPUT_WAVE_ONLY)
+# ifdef ADLMIDI_USE_SDL2
+# include <SDL2/SDL_timer.h>
+#ifdef __APPLE__
+# include <unistd.h>
+#endif
+
+static inline double s_getTime()
+{
+ return SDL_GetTicks64() / 1000.0;
+}
+
+static inline void s_sleepU(double s)
+{
+#ifdef __APPLE__
+ // For unknown reasons, any sleep functions to way WAY LONGER than requested
+ // So, implementing an own one.
+ static double debt = 0.0;
+ double target = s_getTime() + s - debt;
+
+ while(s_getTime() < target)
+ usleep(1000);
+
+ debt = s_getTime() - target;
+#else
+ SDL_Delay((Uint32)(s * 1000));
+#endif
+}
+# else
+
+# include <time.h>
+# include <unistd.h>
+# include <assert.h>
+static inline double s_getTime()
+{
+ struct timespec t;
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return (t.tv_nsec + (t.tv_sec * 1000000000)) / 1000000000.0;
+}
+
+static inline void s_sleepU(double s)
+{
+ static double debt = 0.0;
+ double target = s_getTime() + s - debt;
+
+ while(s_getTime() < target)
+ usleep(1000);
+
+ debt = s_getTime() - target;
+}
+# endif
+#endif
#ifdef DEBUG_SONG_SWITCHING
#include <unistd.h>
@@ -209,17 +263,89 @@ typedef std::deque<uint8_t> AudioBuff;
static AudioBuff g_audioBuffer;
static MutexType g_audioBuffer_lock;
static ADLMIDI_AudioFormat g_audioFormat;
+static float g_gaining = 2.0f;
+
+static void applyGain(uint8_t *buffer, size_t bufferSize)
+{
+ size_t i;
+
+ switch(g_audioFormat.type)
+ {
+ case ADLMIDI_SampleType_S8:
+ {
+ int8_t *buf = reinterpret_cast<int8_t *>(buffer);
+ size_t samples = bufferSize;
+ for(i = 0; i < samples; ++i)
+ *(buf++) *= g_gaining;
+ break;
+ }
+ case ADLMIDI_SampleType_U8:
+ {
+ uint8_t *buf = buffer;
+ size_t samples = bufferSize;
+ for(i = 0; i < samples; ++i)
+ {
+ int8_t s = static_cast<int8_t>(static_cast<int32_t>(*buf) + (-0x7f - 1)) * g_gaining;
+ *(buf++) = static_cast<uint8_t>(static_cast<int32_t>(s) - (-0x7f - 1));
+ }
+ break;
+ }
+ case ADLMIDI_SampleType_S16:
+ {
+ int16_t *buf = reinterpret_cast<int16_t *>(buffer);
+ size_t samples = bufferSize / g_audioFormat.containerSize;
+ for(i = 0; i < samples; ++i)
+ *(buf++) *= g_gaining;
+ break;
+ }
+ case ADLMIDI_SampleType_U16:
+ {
+ uint16_t *buf = reinterpret_cast<uint16_t *>(buffer);
+ size_t samples = bufferSize / g_audioFormat.containerSize;
+ for(i = 0; i < samples; ++i)
+ {
+ int16_t s = static_cast<int16_t>(static_cast<int32_t>(*buf) + (-0x7fff - 1)) * g_gaining;
+ *(buf++) = static_cast<uint16_t>(static_cast<int32_t>(s) - (-0x7fff - 1));
+ }
+ break;
+ }
+ case ADLMIDI_SampleType_S32:
+ {
+ int32_t *buf = reinterpret_cast<int32_t *>(buffer);
+ size_t samples = bufferSize / g_audioFormat.containerSize;
+ for(i = 0; i < samples; ++i)
+ *(buf++) *= g_gaining;
+ break;
+ }
+ case ADLMIDI_SampleType_F32:
+ {
+ float *buf = reinterpret_cast<float *>(buffer);
+ size_t samples = bufferSize / g_audioFormat.containerSize;
+ for(i = 0; i < samples; ++i)
+ *(buf++) *= g_gaining;
+ break;
+ }
+ default:
+ break;
+ }
+}
static void SDL_AudioCallbackX(void *, uint8_t *stream, int len)
{
+ unsigned ate = static_cast<unsigned>(len); // number of bytes
+
audio_lock();
//short *target = (short *) stream;
- g_audioBuffer_lock.Lock();
- unsigned ate = static_cast<unsigned>(len); // number of bytes
+ g_audioBuffer_lock.Lock();
+
if(ate > g_audioBuffer.size())
ate = (unsigned)g_audioBuffer.size();
+
for(unsigned a = 0; a < ate; ++a)
stream[a] = g_audioBuffer[a];
+
+ applyGain(stream, len);
+
g_audioBuffer.erase(g_audioBuffer.begin(), g_audioBuffer.begin() + ate);
g_audioBuffer_lock.Unlock();
audio_unlock();
@@ -305,9 +431,12 @@ static bool is_number(const std::string &s)
return !s.empty() && it == s.end();
}
-static void printError(const char *err)
+static void printError(const char *err, const char *what = NULL)
{
- std::fprintf(stderr, "\nERROR: %s\n\n", err);
+ if(what)
+ std::fprintf(stderr, "\nERROR (%s): %s\n\n", what, err);
+ else
+ std::fprintf(stderr, "\nERROR: %s\n\n", err);
flushout(stderr);
}
@@ -439,6 +568,651 @@ static inline void secondsToHMSM(double seconds_full, char *hmsm_buffer, size_t
snprintf(hmsm_buffer, hmsm_buffer_size, "%02u:%02u,%03u", minutes, seconds, milliseconds);
}
+
+static struct TimeCounter
+{
+ char posHMS[25];
+ char totalHMS[25];
+ char loopStartHMS[25];
+ char loopEndHMS[25];
+ char realHMS[25];
+
+ bool hasLoop;
+ uint64_t milliseconds_prev;
+ int printsCounter;
+ int printsCounterPeriod;
+ int complete_prev;
+ double totalTime;
+
+ double realTimeStart;
+
+#ifdef HARDWARE_OPL3
+ unsigned newTimerFreq;
+ unsigned timerPeriod;
+ int haveYield;
+ int haveDosIdle;
+ unsigned int ring;
+ unsigned long BIOStimer_begin;
+
+ unsigned long timerNext;
+
+ enum wmethod
+ {
+ WM_NONE,
+ WM_YIELD,
+ WM_IDLE,
+ WM_HLT
+ } idleMethod;
+
+#endif
+
+ TimeCounter()
+ {
+ hasLoop = false;
+ totalTime = 0.0;
+ milliseconds_prev = ~0u;
+ printsCounter = 0;
+ complete_prev = -1;
+
+#ifndef HARDWARE_OPL3
+ printsCounterPeriod = 1;
+#else
+ printsCounterPeriod = 20;
+ setDosTimerHZ(209);
+ haveYield = 0;
+ haveDosIdle = 0;
+ ring = 0;
+ idleMethod = WM_NONE;
+
+ timerNext = 0;
+#endif
+ }
+
+#ifdef HARDWARE_OPL3
+ void initDosTimer()
+ {
+# ifdef __DJGPP__
+ /* determine protection ring */
+ __asm__ ("mov %%cs, %0\n\t"
+ "and $3, %0" : "=r" (ring));
+
+ errno = 0;
+ __dpmi_yield();
+ haveYield = errno ? 0 : 1;
+
+ if(!haveYield)
+ {
+ __dpmi_regs regs;
+ regs.x.ax = 0x1680;
+ __dpmi_int(0x28, &regs);
+ haveDosIdle = regs.h.al ? 0 : 1;
+
+ if(haveDosIdle)
+ idleMethod = WM_IDLE;
+ else if(ring == 0)
+ idleMethod = WM_HLT;
+ else
+ idleMethod = WM_NONE;
+ }
+ else
+ {
+ idleMethod = WM_YIELD;
+ }
+
+ const char *method;
+ switch(idleMethod)
+ {
+ default:
+ case WM_NONE:
+ method = "none";
+ break;
+ case WM_YIELD:
+ method = "yield";
+ break;
+ case WM_IDLE:
+ method = "idle";
+ break;
+ case WM_HLT:
+ method = "hlt";
+ break;
+ }
+
+ std::fprintf(stdout, " - [DOS] Using idle method: %s\n", method);
+# endif
+ }
+
+ void setDosTimerHZ(unsigned timer)
+ {
+ newTimerFreq = timer;
+ timerPeriod = 0x1234DDul / newTimerFreq;
+ }
+
+ void flushDosTimer()
+ {
+# ifdef __DJGPP__
+ outportb(0x43, 0x34);
+ outportb(0x40, timerPeriod & 0xFF);
+ outportb(0x40, timerPeriod >> 8);
+# endif
+
+# ifdef __WATCOMC__
+ outp(0x43, 0x34);
+ outp(0x40, TimerPeriod & 0xFF);
+ outp(0x40, TimerPeriod >> 8);
+# endif
+
+ BIOStimer_begin = BIOStimer;
+
+ std::fprintf(stdout, " - [DOS] Running clock with %d hz\n", newTimerFreq);
+ }
+
+ void restoreDosTimer()
+ {
+# ifdef __DJGPP__
+ // Fix the skewed clock and reset BIOS tick rate
+ _farpokel(_dos_ds, 0x46C, BIOStimer_begin + (BIOStimer - BIOStimer_begin) * (0x1234DD / 65536.0) / newTimerFreq);
+
+ //disable();
+ outportb(0x43, 0x34);
+ outportb(0x40, 0);
+ outportb(0x40, 0);
+ //enable();
+# endif
+
+# ifdef __WATCOMC__
+ outp(0x43, 0x34);
+ outp(0x40, 0);
+ outp(0x40, 0);
+# endif
+ }
+
+ void waitDosTimer()
+ {
+//__asm__ volatile("sti\nhlt");
+//usleep(10000);
+# ifdef __DJGPP__
+ switch(idleMethod)
+ {
+ default:
+ case WM_NONE:
+ if(timerNext != 0)
+ while(BIOStimer < timerNext);
+ timerNext = BIOStimer + 1;
+ break;
+
+ case WM_YIELD:
+ __dpmi_yield();
+ break;
+
+ case WM_IDLE:
+ {
+ __dpmi_regs regs;
+
+ /* the DOS Idle call is documented to return immediately if no other
+ * program is ready to run, therefore do one HLT if we can */
+ if(ring == 0)
+ __asm__ volatile ("hlt");
+
+ regs.x.ax = 0x1680;
+ __dpmi_int(0x28, &regs);
+ if (regs.h.al)
+ errno = ENOSYS;
+ break;
+ }
+
+ case WM_HLT:
+ __asm__ volatile("hlt");
+ break;
+ }
+# endif
+# ifdef __WATCOMC__
+ //dpmi_dos_yield();
+ mch_delay((unsigned int)(tick_delay * 1000.0));
+# endif
+ }
+#endif
+
+ void setTotal(double total)
+ {
+ totalTime = total;
+ secondsToHMSM(total, totalHMS, 25);
+ realTimeStart = s_getTime();
+ secondsToHMSM(s_getTime() - realTimeStart, realHMS, 25);
+ }
+
+ void setLoop(double loopStart, double loopEnd)
+ {
+ hasLoop = false;
+
+ if(loopStart >= 0.0 && loopEnd >= 0.0)
+ {
+ secondsToHMSM(loopStart, loopStartHMS, 25);
+ secondsToHMSM(loopEnd, loopEndHMS, 25);
+ hasLoop = true;
+ }
+ }
+
+ void clearLineR()
+ {
+ std::fprintf(stdout, " \r");
+ flushout(stdout);
+ }
+
+ void printTime(double pos)
+ {
+ uint64_t milliseconds = static_cast<uint64_t>(pos * 1000.0);
+
+ if(milliseconds != milliseconds_prev)
+ {
+ if(printsCounter >= printsCounterPeriod)
+ {
+ printsCounter = -1;
+ secondsToHMSM(pos, posHMS, 25);
+ secondsToHMSM(s_getTime() - realTimeStart, realHMS, 25);
+ std::fprintf(stdout, " \r");
+ std::fprintf(stdout, "Time position: %s / %s [Real time: %s]\r", posHMS, totalHMS, realHMS);
+ flushout(stdout);
+ milliseconds_prev = milliseconds;
+ }
+ printsCounter++;
+ }
+ }
+
+ void printProgress(double pos)
+ {
+ int complete = static_cast<int>(std::floor(100.0 * pos / totalTime));
+
+ if(complete_prev != complete)
+ {
+ std::fprintf(stdout, " \r");
+ std::fprintf(stdout, "Recording WAV... [%d%% completed]\r", complete);
+ flushout(stdout);
+ complete_prev = complete;
+ }
+ }
+
+ void clearLine()
+ {
+ std::fprintf(stdout, " \n\n");
+ flushout(stdout);
+ }
+
+} s_timeCounter;
+
+
+
+#if defined(ADLMIDI_ENABLE_HW_SERIAL) && !defined(OUTPUT_WAVE_ONLY)
+static void runHWSerialLoop(ADL_MIDIPlayer *myDevice)
+{
+ double tick_delay = 0.00000001;
+ double tick_wait = 0.0;
+ double timeBegL, timeEndL;
+#if _WIN32
+ const double minDelay = 0.050; // On Windows, the Serial bandwith is WAY SLOWER, so, bigger granuality.
+#else
+ const double minDelay = 0.005;
+#endif
+ double eat_delay;
+ // bool tickSkip = true;
+
+ s_timeCounter.clearLineR();
+
+ while(!stop)
+ {
+ timeBegL = s_getTime();
+ tick_delay = adl_tickEvents(myDevice, tick_delay < minDelay ? tick_delay : minDelay, minDelay / 10.0);
+ // adl_tickIterators(myDevice, minDelay);
+# ifndef DEBUG_TRACE_ALL_EVENTS
+ s_timeCounter.printTime(adl_positionTell(myDevice));
+# endif
+ timeEndL = s_getTime();
+
+ eat_delay = timeEndL - timeBegL;
+
+ if(tick_delay < minDelay)
+ tick_wait = tick_delay - eat_delay;
+ else
+ tick_wait = minDelay - eat_delay;
+
+ if(adl_atEnd(myDevice) && tick_delay <= 0)
+ stop = true;
+
+ if(tick_wait > 0.0)
+ s_sleepU(tick_wait);
+
+#if 0
+ timeBegL = s_getTime();
+
+ tick_delay = adl_tickEventsOnly(myDevice, tick_delay, 0.000000001);
+ adl_tickIterators(myDevice, tick_delay < minDelay ? tick_delay : minDelay);
+
+ if(adl_atEnd(myDevice) && tick_delay <= 0)
+ stop = true;
+
+# ifndef DEBUG_TRACE_ALL_EVENTS
+ s_timeCounter.printTime(adl_positionTell(myDevice));
+# endif
+
+ timeEndL = s_getTime();
+
+ if(timeEndL < timeBegL)
+ timeEndL = timeBegL;
+
+ eat_delay = timeEndL - timeBegL;
+ tick_wait += tick_delay - eat_delay;
+
+ if(tick_wait > 0.0)
+ {
+ if(tick_wait < minDelay)
+ {
+ if(tick_wait > 0.0)
+ s_sleepU(tick_wait);
+ tick_wait -= minDelay;
+ }
+
+ while(tick_wait > 0.0)
+ {
+ timeBegL = s_getTime();
+ if(!tickSkip)
+ adl_tickIterators(myDevice, minDelay);
+ else
+ tickSkip = false;
+ timeEndL = s_getTime();
+
+ if(timeEndL < timeBegL)
+ timeEndL = timeBegL;
+
+ double microDelay = minDelay - (timeEndL - timeBegL);
+
+ if(microDelay > 0.0)
+ s_sleepU(microDelay);
+
+ tick_wait -= minDelay;
+ }
+ }
+#endif
+ }
+
+ s_timeCounter.clearLine();
+
+ adl_panic(myDevice); //Shut up all sustaining notes
+}
+#endif // ADLMIDI_ENABLE_HW_SERIAL
+
+
+#ifndef HARDWARE_OPL3
+# ifndef OUTPUT_WAVE_ONLY
+static int runAudioLoop(ADL_MIDIPlayer *myDevice, AudioOutputSpec &spec)
+{
+ //const unsigned MaxSamplesAtTime = 512; // 512=dbopl limitation
+ // How long is SDL buffer, in seconds?
+ // The smaller the value, the more often SDL_AudioCallBack()
+ // is called.
+ //const double AudioBufferLength = 0.08;
+
+ // How much do WE buffer, in seconds? The smaller the value,
+ // the more prone to sound chopping we are.
+ const double OurHeadRoomLength = 0.1;
+ // The lag between visual content and audio content equals
+ // the sum of these two buffers.
+
+ AudioOutputSpec obtained;
+
+ // Set up SDL
+ if(audio_init(&spec, &obtained, SDL_AudioCallbackX) < 0)
+ {
+ std::fprintf(stderr, "\nERROR: Couldn't open audio: %s\n\n", audio_get_error());
+ return 1;
+ }
+
+ if(spec.samples != obtained.samples)
+ {
+ std::fprintf(stderr, " - Audio wanted (format=%s,samples=%u,rate=%u,channels=%u);\n"
+ " - Audio obtained (format=%s,samples=%u,rate=%u,channels=%u)\n",
+ audio_format_to_str(spec.format, spec.is_msb), spec.samples, spec.freq, spec.channels,
+ audio_format_to_str(obtained.format, obtained.is_msb), obtained.samples, obtained.freq, obtained.channels);
+ }
+
+ switch(obtained.format)
+ {
+ case ADLMIDI_SampleType_S8:
+ g_audioFormat.type = ADLMIDI_SampleType_S8;
+ g_audioFormat.containerSize = sizeof(int8_t);
+ g_audioFormat.sampleOffset = sizeof(int8_t) * 2;
+ break;
+ case ADLMIDI_SampleType_U8:
+ g_audioFormat.type = ADLMIDI_SampleType_U8;
+ g_audioFormat.containerSize = sizeof(uint8_t);
+ g_audioFormat.sampleOffset = sizeof(uint8_t) * 2;
+ break;
+ case ADLMIDI_SampleType_S16:
+ g_audioFormat.type = ADLMIDI_SampleType_S16;
+ g_audioFormat.containerSize = sizeof(int16_t);
+ g_audioFormat.sampleOffset = sizeof(int16_t) * 2;
+ break;
+ case ADLMIDI_SampleType_U16:
+ g_audioFormat.type = ADLMIDI_SampleType_U16;
+ g_audioFormat.containerSize = sizeof(uint16_t);
+ g_audioFormat.sampleOffset = sizeof(uint16_t) * 2;
+ break;
+ case ADLMIDI_SampleType_S32:
+ g_audioFormat.type = ADLMIDI_SampleType_S32;
+ g_audioFormat.containerSize = sizeof(int32_t);
+ g_audioFormat.sampleOffset = sizeof(int32_t) * 2;
+ break;
+ case ADLMIDI_SampleType_F32:
+ g_audioFormat.type = ADLMIDI_SampleType_F32;
+ g_audioFormat.containerSize = sizeof(float);
+ g_audioFormat.sampleOffset = sizeof(float) * 2;
+ break;
+ }
+
+
+# ifdef DEBUG_SONG_CHANGE
+ int delayBeforeSongChange = 50;
+ std::fprintf(stdout, "DEBUG: === Random song set test is active! ===\n");
+ flushout(stdout);
+# endif
+
+# ifdef DEBUG_SEEKING_TEST
+ int delayBeforeSeek = 50;
+ std::fprintf(stdout, "DEBUG: === Random position set test is active! ===\n");
+ flushout(stdout);
+# endif
+
+ size_t got;
+ uint8_t buff[16384];
+
+ audio_start();
+
+ s_timeCounter.clearLineR();
+
+ while(!stop)
+ {
+ got = (size_t)adl_playFormat(myDevice, 4096,
+ buff,
+ buff + g_audioFormat.containerSize,
+ &g_audioFormat) * g_audioFormat.containerSize;
+ if(got <= 0)
+ break;
+
+# ifdef DEBUG_TRACE_ALL_CHANNELS
+ enum { TerminalColumns = 80 };
+ char channelText[TerminalColumns + 1];
+ char channelAttr[TerminalColumns + 1];
+ adl_describeChannels(myDevice, channelText, channelAttr, sizeof(channelText));
+ std::fprintf(stdout, "%*s\r", TerminalColumns, ""); // erase the line
+ std::fprintf(stdout, "%s\n", channelText);
+# endif
+
+# ifndef DEBUG_TRACE_ALL_EVENTS
+ s_timeCounter.printTime(adl_positionTell(myDevice));
+# endif
+
+ g_audioBuffer_lock.Lock();
+ size_t pos = g_audioBuffer.size();
+ g_audioBuffer.resize(pos + got);
+ for(size_t p = 0; p < got; ++p)
+ g_audioBuffer[pos + p] = buff[p];
+ g_audioBuffer_lock.Unlock();
+
+ const AudioOutputSpec &spec = obtained;
+ while(!stop && (g_audioBuffer.size() > static_cast<size_t>(spec.samples + (spec.freq * g_audioFormat.sampleOffset) * OurHeadRoomLength)))
+ {
+ audio_delay(1);
+ }
+
+# ifdef DEBUG_SONG_SWITCHING
+ if(kbhit())
+ {
+ int code = getch();
+ if(code == '\033' && kbhit())
+ {
+ getch();
+ switch(getch())
+ {
+ case 'C':
+ // code for arrow right
+ songNumLoad++;
+ if(songNumLoad >= songsCount)
+ songNumLoad = songsCount;
+ adl_selectSongNum(myDevice, songNumLoad);
+ std::fprintf(stdout, "\rSwitching song to %d/%d... \r\n", songNumLoad, songsCount);
+ flushout(stdout);
+ break;
+ case 'D':
+ // code for arrow left
+ songNumLoad--;
+ if(songNumLoad < 0)
+ songNumLoad = 0;
+ adl_selectSongNum(myDevice, songNumLoad);
+ std::fprintf(stdout, "\rSwitching song to %d/%d... \r\n", songNumLoad, songsCount);
+ flushout(stdout);
+ break;
+ }
+ }
+ else if(code == 27) // Quit by ESC key
+ stop = 1;
+ }
+# endif
+
+# ifdef DEBUG_SEEKING_TEST
+ if(delayBeforeSeek-- <= 0)
+ {
+ delayBeforeSeek = rand() % 50;
+ double seekTo = double((rand() % int(adl_totalTimeLength(myDevice)) - delayBeforeSeek - 1 ));
+ adl_positionSeek(myDevice, seekTo);
+ }
+# endif
+
+# ifdef DEBUG_SONG_CHANGE
+ if(delayBeforeSongChange-- <= 0)
+ {
+ delayBeforeSongChange = rand() % 100;
+ adl_selectSongNum(myDevice, rand() % 10);
+ }
+# endif
+
+# ifdef DEBUG_SONG_CHANGE_BY_HOOK
+ if(gotXmiTrigger)
+ {
+ gotXmiTrigger = false;
+ adl_selectSongNum(myDevice, (rand() % 10) + 1);
+ }
+# endif
+ }
+
+ s_timeCounter.clearLine();
+
+ audio_stop();
+ audio_close();
+
+ return 0;
+}
+# endif // OUTPUT_WAVE_ONLY
+
+static int runWaveOutLoopLoop(ADL_MIDIPlayer *myDevice, const std::string &musPath, unsigned sampleRate)
+{
+ std::string wave_out = musPath + ".wav";
+ std::fprintf(stdout, " - Recording WAV file %s...\n", wave_out.c_str());
+ std::fprintf(stdout, "\n==========================================\n");
+ flushout(stdout);
+
+ if(wave_open(static_cast<long>(sampleRate), wave_out.c_str()) == 0)
+ {
+ wave_enable_stereo();
+ short buff[4096];
+
+ while(!stop)
+ {
+ size_t got = static_cast<size_t>(adl_play(myDevice, 4096, buff));
+ if(got <= 0)
+ break;
+ wave_write(buff, static_cast<long>(got));
+
+ s_timeCounter.printProgress(adl_positionTell(myDevice));
+ }
+
+ wave_close();
+ s_timeCounter.clearLine();
+
+ if(stop)
+ std::fprintf(stdout, "Interrupted! Recorded WAV is incomplete, but playable!\n");
+ else
+ std::fprintf(stdout, "Completed!\n");
+ flushout(stdout);
+ }
+ else
+ {
+ adl_close(myDevice);
+ return 1;
+ }
+
+ return 0;
+}
+
+#else // HARDWARE_OPL3
+static void runDOSLoop(ADL_MIDIPlayer *myDevice)
+{
+ double tick_delay = 0.0;
+
+ s_timeCounter.clearLineR();
+
+ while(!stop)
+ {
+ const double mindelay = 1.0 / s_timeCounter.newTimerFreq;
+
+# ifndef DEBUG_TRACE_ALL_EVENTS
+ s_timeCounter.printTime(adl_positionTell(myDevice));
+# endif
+
+ s_timeCounter.waitDosTimer();
+
+ static unsigned long PrevTimer = BIOStimer;
+ const unsigned long CurTimer = BIOStimer;
+ const double eat_delay = (CurTimer - PrevTimer) / (double)s_timeCounter.newTimerFreq;
+ PrevTimer = CurTimer;
+
+ tick_delay = adl_tickEvents(myDevice, eat_delay, mindelay);
+
+ if(adl_atEnd(myDevice) && tick_delay <= 0)
+ stop = true;
+
+ if(kbhit())
+ { // Quit on ESC key!
+ int c = getch();
+ if(c == 27)
+ stop = true;
+ }
+ }
+
+ s_timeCounter.clearLine();
+
+ adl_panic(myDevice); //Shut up all sustaining notes
+}
+#endif // HARDWARE_OPL3
+
+
int main(int argc, char **argv)
{
std::fprintf(stdout, "==========================================\n"
@@ -493,6 +1267,7 @@ int main(int argc, char **argv)
" -ea Enable the auto-arpeggio\n"
#ifndef HARDWARE_OPL3
" -fp Enables full-panning stereo support\n"
+ " --gain <value> Set the gaining factor (default 2.0)\n"
" --emu-nuked Uses Nuked OPL3 v 1.8 emulator\n"
" --emu-nuked7 Uses Nuked OPL3 v 1.7.4 emulator\n"
" --emu-dosbox Uses DosBox 0.74 OPL3 emulator\n"
@@ -519,36 +1294,28 @@ int main(int argc, char **argv)
}
unsigned int sampleRate = 44100;
-#ifndef HARDWARE_OPL3
+#if !defined(HARDWARE_OPL3) && !defined(OUTPUT_WAVE_ONLY)
//const unsigned MaxSamplesAtTime = 512; // 512=dbopl limitation
// How long is SDL buffer, in seconds?
// The smaller the value, the more often SDL_AudioCallBack()
// is called.
const double AudioBufferLength = 0.08;
- // How much do WE buffer, in seconds? The smaller the value,
- // the more prone to sound chopping we are.
- const double OurHeadRoomLength = 0.1;
- // The lag between visual content and audio content equals
- // the sum of these two buffers.
-# ifndef OUTPUT_WAVE_ONLY
AudioOutputSpec spec;
- AudioOutputSpec obtained;
-
spec.freq = sampleRate;
spec.format = ADLMIDI_SampleType_S16;
spec.is_msb = 0;
spec.channels = 2;
spec.samples = uint16_t((double)spec.freq * AudioBufferLength);
-# endif //OUTPUT_WAVE_ONLY
-#endif //HARDWARE_OPL3
+#endif // !HARDWARE_OPL3 && !OUTPUT_WAVE_ONLY
-#ifdef HARDWARE_OPL3
- static unsigned newTimerFreq = 209;
- unsigned timerPeriod = 0x1234DDul / newTimerFreq;
+#if defined(ADLMIDI_ENABLE_HW_SERIAL) && !defined(OUTPUT_WAVE_ONLY)
+ bool hwSerial = false;
+ std::string serialName;
+ unsigned serialBaud = 115200;
+ unsigned serialProto = ADLMIDI_SerialProtocol_RetroWaveOPL3;
#endif
-
ADL_MIDIPlayer *myDevice;
//Initialize libADLMIDI and create the instance (you can initialize multiple of them!)
@@ -648,12 +1415,57 @@ int main(int argc, char **argv)
else if(!std::strcmp("--emu-java", argv[2]))
emulator = ADLMIDI_EMU_JAVA;
#endif
+#if defined(ADLMIDI_ENABLE_HW_SERIAL) && !defined(OUTPUT_WAVE_ONLY)
+ else if(!std::strcmp("--serial", argv[2]))
+ {
+ if(argc <= 3)
+ {
+ printError("The option --serial requires an argument!\n");
+ return 1;
+ }
+ had_option = true;
+ hwSerial = true;
+ serialName = argv[3];
+ }
+ else if(!std::strcmp("--serial-baud", argv[2]))
+ {
+ if(argc <= 3)
+ {
+ printError("The option --serial-baud requires an argument!\n");
+ return 1;
+ }
+ had_option = true;
+ serialBaud = std::strtol(argv[3], NULL, 10);
+ }
+ else if(!std::strcmp("--serial-proto", argv[2]))
+ {
+ if(argc <= 3)
+ {
+ printError("The option --serial-proto requires an argument!\n");
+ return 1;
+ }
+ had_option = true;
+ serialProto = std::strtol(argv[3], NULL, 10);
+ }
+#endif
else if(!std::strcmp("-fp", argv[2]))
adl_setSoftPanEnabled(myDevice, 1);
else if(!std::strcmp("-mb", argv[2]))
multibankFromEnbededTest = true;
else if(!std::strcmp("-s", argv[2]))
adl_setScaleModulators(myDevice, 1);//Turn on modulators scaling by volume
+#ifndef ADLMIDI_HW_OPL
+ else if(!std::strcmp("--gain", argv[2]))
+ {
+ if(argc <= 3)
+ {
+ printError("The option --gain requires an argument!\n");
+ return 1;
+ }
+ had_option = true;
+ g_gaining = std::atof(argv[3]);
+ }
+#endif // HARDWARE_OPL3
#ifdef HARDWARE_OPL3
else if(!std::strcmp("--time-freq", argv[2]))
@@ -663,14 +1475,15 @@ int main(int argc, char **argv)
printError("The option --time-freq requires an argument!\n");
return 1;
}
- newTimerFreq = std::strtoul(argv[3], NULL, 0);
- if(newTimerFreq == 0)
+
+ unsigned timerFreq = std::strtoul(argv[3], NULL, 0);
+ if(timerFreq == 0)
{
printError("The option --time-freq requires a non-zero integer argument!\n");
return 1;
}
- timerPeriod = 0x1234DDul / newTimerFreq;
+ s_timeCounter.setDosTimerHZ(timerFreq);
had_option = true;
}
@@ -763,6 +1576,11 @@ int main(int argc, char **argv)
adl_setRawEventHook(myDevice, debugPrintEvent, NULL);
#endif
+#if defined(ADLMIDI_ENABLE_HW_SERIAL) && !defined(OUTPUT_WAVE_ONLY)
+ if(hwSerial)
+ adl_switchSerialHW(myDevice, serialName.c_str(), serialBaud, serialProto);
+ else
+#endif
#ifndef HARDWARE_OPL3
adl_switchEmulator(myDevice, emulator);
#endif
@@ -770,65 +1588,15 @@ int main(int argc, char **argv)
std::fprintf(stdout, " - Library version %s\n", adl_linkedLibraryVersion());
#ifdef HARDWARE_OPL3
std::fprintf(stdout, " - Hardware OPL3 chip in use\n");
+#elif defined(ADLMIDI_ENABLE_HW_SERIAL) && !defined(OUTPUT_WAVE_ONLY)
+ if(hwSerial)
+ std::fprintf(stdout, " - %s [device %s] in use\n", adl_chipEmulatorName(myDevice), serialName.c_str());
+ else
+ std::fprintf(stdout, " - %s Emulator in use\n", adl_chipEmulatorName(myDevice));
#else
std::fprintf(stdout, " - %s Emulator in use\n", adl_chipEmulatorName(myDevice));
#endif
-#if !defined(HARDWARE_OPL3) && !defined(OUTPUT_WAVE_ONLY)
- if(!recordWave)
- {
- // Set up SDL
- if(audio_init(&spec, &obtained, SDL_AudioCallbackX) < 0)
- {
- std::fprintf(stderr, "\nERROR: Couldn't open audio: %s\n\n", audio_get_error());
- adl_close(myDevice);
- return 1;
- }
-
- if(spec.samples != obtained.samples)
- {
- std::fprintf(stderr, " - Audio wanted (format=%s,samples=%u,rate=%u,channels=%u);\n"
- " - Audio obtained (format=%s,samples=%u,rate=%u,channels=%u)\n",
- audio_format_to_str(spec.format, spec.is_msb), spec.samples, spec.freq, spec.channels,
- audio_format_to_str(obtained.format, obtained.is_msb), obtained.samples, obtained.freq, obtained.channels);
- }
-
- switch(obtained.format)
- {
- case ADLMIDI_SampleType_S8:
- g_audioFormat.type = ADLMIDI_SampleType_S8;
- g_audioFormat.containerSize = sizeof(int8_t);
- g_audioFormat.sampleOffset = sizeof(int8_t) * 2;
- break;
- case ADLMIDI_SampleType_U8:
- g_audioFormat.type = ADLMIDI_SampleType_U8;
- g_audioFormat.containerSize = sizeof(uint8_t);
- g_audioFormat.sampleOffset = sizeof(uint8_t) * 2;
- break;
- case ADLMIDI_SampleType_S16:
- g_audioFormat.type = ADLMIDI_SampleType_S16;
- g_audioFormat.containerSize = sizeof(int16_t);
- g_audioFormat.sampleOffset = sizeof(int16_t) * 2;
- break;
- case ADLMIDI_SampleType_U16:
- g_audioFormat.type = ADLMIDI_SampleType_U16;
- g_audioFormat.containerSize = sizeof(uint16_t);
- g_audioFormat.sampleOffset = sizeof(uint16_t) * 2;
- break;
- case ADLMIDI_SampleType_S32:
- g_audioFormat.type = ADLMIDI_SampleType_S32;
- g_audioFormat.containerSize = sizeof(int32_t);
- g_audioFormat.sampleOffset = sizeof(int32_t) * 2;
- break;
- case ADLMIDI_SampleType_F32:
- g_audioFormat.type = ADLMIDI_SampleType_F32;
- g_audioFormat.containerSize = sizeof(float);
- g_audioFormat.sampleOffset = sizeof(float) * 2;
- break;
- }
- }
-#endif
-
if(argc >= 3)
{
if(is_number(argv[2]))
@@ -837,7 +1605,7 @@ int main(int argc, char **argv)
//Choose one of embedded banks
if(adl_setBank(myDevice, bankno) != 0)
{
- printError(adl_errorInfo(myDevice));
+ printError(adl_errorInfo(myDevice), "Can't set an embedded bank");
adl_close(myDevice);
return 1;
}
@@ -854,7 +1622,7 @@ int main(int argc, char **argv)
{
std::fprintf(stdout, "FAILED!\n");
flushout(stdout);
- printError(adl_errorInfo(myDevice));
+ printError(adl_errorInfo(myDevice), "Can't open a custom bank file");
adl_close(myDevice);
return 1;
}
@@ -881,14 +1649,14 @@ int main(int argc, char **argv)
ADL_Bank bank;
if(adl_getBank(myDevice, &id[i], ADLMIDI_Bank_Create, &bank) < 0)
{
- printError(adl_errorInfo(myDevice));
+ printError(adl_errorInfo(myDevice), "Can't get an embedded bank");
adl_close(myDevice);
return 1;
}
if(adl_loadEmbeddedBank(myDevice, &bank, banks[i]) < 0)
{
- printError(adl_errorInfo(myDevice));
+ printError(adl_errorInfo(myDevice), "Can't load an embedded bank");
adl_close(myDevice);
return 1;
}
@@ -902,10 +1670,15 @@ int main(int argc, char **argv)
if(argc >= 4)
numOfChips = std::atoi(argv[3]);
+#if defined(ADLMIDI_ENABLE_HW_SERIAL) && !defined(OUTPUT_WAVE_ONLY)
+ if(hwSerial)
+ numOfChips = 1;
+#endif
+
//Set count of concurrent emulated chips count to excite channels limit of one chip
if(adl_setNumChips(myDevice, numOfChips) != 0)
{
- printError(adl_errorInfo(myDevice));
+ printError(adl_errorInfo(myDevice), "Can't set number of chips");
adl_close(myDevice);
return 1;
}
@@ -922,7 +1695,7 @@ int main(int argc, char **argv)
//Set total count of 4-operator channels between all emulated chips
if(adl_setNumFourOpsChn(myDevice, std::atoi(argv[4])) != 0)
{
- printError(adl_errorInfo(myDevice));
+ printError(adl_errorInfo(myDevice), "Can't set number of 4-op channels");
adl_close(myDevice);
return 1;
}
@@ -933,6 +1706,7 @@ int main(int argc, char **argv)
adl_setLoopEndHook(myDevice, loopEndCallback, NULL);
adl_setLoopHooksOnly(myDevice, 1);
#endif
+
if(songNumLoad >= 0)
adl_selectSongNum(myDevice, songNumLoad);
@@ -946,7 +1720,8 @@ int main(int argc, char **argv)
//Open MIDI file to play
if(adl_openFile(myDevice, musPath.c_str()) != 0)
{
- printError(adl_errorInfo(myDevice));
+ printError(adl_errorInfo(myDevice), "Can't open MIDI file");
+ adl_close(myDevice);
return 2;
}
@@ -956,6 +1731,15 @@ int main(int argc, char **argv)
std::fprintf(stdout, " - Volume model: %s\n", volume_model_to_str(adl_getVolumeRangeModel(myDevice)));
std::fprintf(stdout, " - Channel allocation mode: %s\n", chanalloc_to_str(adl_getChannelAllocMode(myDevice)));
+#ifndef HARDWARE_OPL3
+# ifdef ADLMIDI_ENABLE_HW_SERIAL
+ if(!hwSerial)
+# endif
+ {
+ std::fprintf(stdout, " - Gain level: %g\n", g_gaining);
+ }
+#endif
+
int songsCount = adl_getSongsCount(myDevice);
if(songNumLoad >= 0)
std::fprintf(stdout, " - Attempt to load song number: %d / %d\n", songNumLoad, songsCount);
@@ -996,242 +1780,46 @@ int main(int argc, char **argv)
# endif
#else // HARDWARE_OPL3
-
-# ifdef __DJGPP__
- //disable();
- errno = 0;
- __dpmi_yield();
- int haveYield = errno ? 0 : 1;
-
- if(!haveYield)
- std::fprintf(stdout, " - [DOS] dmpi_yield failed, using hlt\n");
-
- outportb(0x43, 0x34);
- outportb(0x40, timerPeriod & 0xFF);
- outportb(0x40, timerPeriod >> 8);
- std::fprintf(stdout, " - [DOS] Running clock with %d hz\n", newTimerFreq);
- //enable();
-# endif//__DJGPP__
-
-# ifdef __WATCOMC__
- std::fprintf(stdout, " - Initializing BIOS timer...\n");
- flushout(stdout);
//disable();
- outp(0x43, 0x34);
- outp(0x40, TimerPeriod & 0xFF);
- outp(0x40, TimerPeriod >> 8);
+ s_timeCounter.initDosTimer();
+ s_timeCounter.flushDosTimer();
//enable();
- std::fprintf(stdout, " - Ok!\n");
- flushout(stdout);
-# endif//__WATCOMC__
-
- unsigned long BIOStimer_begin = BIOStimer;
- double tick_delay = 0.0;
#endif//HARDWARE_OPL3
- double total = adl_totalTimeLength(myDevice);
+ s_timeCounter.setTotal(adl_totalTimeLength(myDevice));
#ifndef OUTPUT_WAVE_ONLY
- double loopStart = adl_loopStartTime(myDevice);
- double loopEnd = adl_loopEndTime(myDevice);
- char totalHMS[25];
- char loopStartHMS[25];
- char loopEndHMS[25];
- secondsToHMSM(total, totalHMS, 25);
- if(loopStart >= 0.0 && loopEnd >= 0.0)
- {
- secondsToHMSM(loopStart, loopStartHMS, 25);
- secondsToHMSM(loopEnd, loopEndHMS, 25);
- }
+ s_timeCounter.setLoop(adl_loopStartTime(myDevice), adl_loopEndTime(myDevice));
# ifndef HARDWARE_OPL3
if(!recordWave)
# endif
{
std::fprintf(stdout, " - Loop is turned %s\n", loopEnabled ? "ON" : "OFF");
- if(loopStart >= 0.0 && loopEnd >= 0.0)
- std::fprintf(stdout, " - Has loop points: %s ... %s\n", loopStartHMS, loopEndHMS);
+ if(s_timeCounter.hasLoop)
+ std::fprintf(stdout, " - Has loop points: %s ... %s\n", s_timeCounter.loopStartHMS, s_timeCounter.loopEndHMS);
std::fprintf(stdout, "\n==========================================\n");
flushout(stdout);
# ifndef HARDWARE_OPL3
- audio_start();
-# endif
-
-# ifdef DEBUG_SONG_CHANGE
- int delayBeforeSongChange = 50;
- std::fprintf(stdout, "DEBUG: === Random song set test is active! ===\n");
- flushout(stdout);
-# endif
-
-# ifdef DEBUG_SEEKING_TEST
- int delayBeforeSeek = 50;
- std::fprintf(stdout, "DEBUG: === Random position set test is active! ===\n");
- flushout(stdout);
-# endif
-
-# ifndef HARDWARE_OPL3
- uint8_t buff[16384];
-# endif
- char posHMS[25];
- uint64_t milliseconds_prev = ~0u;
- int printsCounter = 0;
- int printsCounterPeriod = 1;
-# ifdef HARDWARE_OPL3
- printsCounterPeriod = 500;
-# endif
-
- std::fprintf(stdout, " \r");
-
- while(!stop)
- {
-# ifndef HARDWARE_OPL3
- size_t got = (size_t)adl_playFormat(myDevice, 4096,
- buff,
- buff + g_audioFormat.containerSize,
- &g_audioFormat) * g_audioFormat.containerSize;
- if(got <= 0)
- break;
-# endif
-
-# ifdef DEBUG_TRACE_ALL_CHANNELS
- enum { TerminalColumns = 80 };
- char channelText[TerminalColumns + 1];
- char channelAttr[TerminalColumns + 1];
- adl_describeChannels(myDevice, channelText, channelAttr, sizeof(channelText));
- std::fprintf(stdout, "%*s\r", TerminalColumns, ""); // erase the line
- std::fprintf(stdout, "%s\n", channelText);
-# endif
-
-# ifndef DEBUG_TRACE_ALL_EVENTS
- double time_pos = adl_positionTell(myDevice);
- uint64_t milliseconds = static_cast<uint64_t>(time_pos * 1000.0);
-
- if(milliseconds != milliseconds_prev)
- {
- if(printsCounter >= printsCounterPeriod)
- {
- printsCounter = -1;
- secondsToHMSM(time_pos, posHMS, 25);
- std::fprintf(stdout, " \r");
- std::fprintf(stdout, "Time position: %s / %s\r", posHMS, totalHMS);
- flushout(stdout);
- milliseconds_prev = milliseconds;
- }
- printsCounter++;
- }
-# endif
-
-# ifndef HARDWARE_OPL3
- g_audioBuffer_lock.Lock();
- size_t pos = g_audioBuffer.size();
- g_audioBuffer.resize(pos + got);
- for(size_t p = 0; p < got; ++p)
- g_audioBuffer[pos + p] = buff[p];
- g_audioBuffer_lock.Unlock();
-
- const AudioOutputSpec &spec = obtained;
- while(!stop && (g_audioBuffer.size() > static_cast<size_t>(spec.samples + (spec.freq * g_audioFormat.sampleOffset) * OurHeadRoomLength)))
- {
- audio_delay(1);
- }
-
-# ifdef DEBUG_SONG_SWITCHING
- if(kbhit())
- {
- int code = getch();
- if(code == '\033' && kbhit())
- {
- getch();
- switch(getch())
- {
- case 'C':
- // code for arrow right
- songNumLoad++;
- if(songNumLoad >= songsCount)
- songNumLoad = songsCount;
- adl_selectSongNum(myDevice, songNumLoad);
- std::fprintf(stdout, "\rSwitching song to %d/%d... \r\n", songNumLoad, songsCount);
- flushout(stdout);
- break;
- case 'D':
- // code for arrow left
- songNumLoad--;
- if(songNumLoad < 0)
- songNumLoad = 0;
- adl_selectSongNum(myDevice, songNumLoad);
- std::fprintf(stdout, "\rSwitching song to %d/%d... \r\n", songNumLoad, songsCount);
- flushout(stdout);
- break;
- }
- }
- else if(code == 27) // Quit by ESC key
- stop = 1;
- }
-# endif
-
-# ifdef DEBUG_SEEKING_TEST
- if(delayBeforeSeek-- <= 0)
- {
- delayBeforeSeek = rand() % 50;
- double seekTo = double((rand() % int(adl_totalTimeLength(myDevice)) - delayBeforeSeek - 1 ));
- adl_positionSeek(myDevice, seekTo);
- }
-# endif
-
-# ifdef DEBUG_SONG_CHANGE
- if(delayBeforeSongChange-- <= 0)
- {
- delayBeforeSongChange = rand() % 100;
- adl_selectSongNum(myDevice, rand() % 10);
- }
+# ifdef ADLMIDI_ENABLE_HW_SERIAL
+ if(hwSerial)
+ runHWSerialLoop(myDevice);
+ else
# endif
-
-# ifdef DEBUG_SONG_CHANGE_BY_HOOK
- if(gotXmiTrigger)
+ {
+ int ret = runAudioLoop(myDevice, spec);
+ if (ret != 0)
{
- gotXmiTrigger = false;
- adl_selectSongNum(myDevice, (rand() % 10) + 1);
- }
-# endif
-
-# else//HARDWARE_OPL3
- const double mindelay = 1.0 / newTimerFreq;
-
- //__asm__ volatile("sti\nhlt");
- //usleep(10000);
-# ifdef __DJGPP__
- if(haveYield)
- __dpmi_yield();
- else
- __asm__ volatile("hlt");
-# endif
-# ifdef __WATCOMC__
- //dpmi_dos_yield();
- mch_delay((unsigned int)(tick_delay * 1000.0));
-# endif
- static unsigned long PrevTimer = BIOStimer;
- const unsigned long CurTimer = BIOStimer;
- const double eat_delay = (CurTimer - PrevTimer) / (double)newTimerFreq;
- PrevTimer = CurTimer;
- tick_delay = adl_tickEvents(myDevice, eat_delay, mindelay);
- if(adl_atEnd(myDevice) && tick_delay <= 0)
- stop = true;
-
- if(kbhit())
- { // Quit on ESC key!
- int c = getch();
- if(c == 27)
- stop = true;
+ adl_close(myDevice);
+ return ret;
}
-
-# endif//HARDWARE_OPL3
}
- std::fprintf(stdout, " \n\n");
-# ifndef HARDWARE_OPL3
- audio_stop();
- audio_close();
+# else
+ runDOSLoop(myDevice);
# endif
+
+ s_timeCounter.clearLine();
}
#endif //OUTPUT_WAVE_ONLY
@@ -1241,77 +1829,20 @@ int main(int argc, char **argv)
else
# endif //OUTPUT_WAVE_ONLY
{
- std::string wave_out = musPath + ".wav";
- std::fprintf(stdout, " - Recording WAV file %s...\n", wave_out.c_str());
- std::fprintf(stdout, "\n==========================================\n");
- flushout(stdout);
-
- if(wave_open(static_cast<long>(sampleRate), wave_out.c_str()) == 0)
- {
- wave_enable_stereo();
- short buff[4096];
- int complete_prev = -1;
- while(!stop)
- {
- size_t got = static_cast<size_t>(adl_play(myDevice, 4096, buff));
- if(got <= 0)
- break;
- wave_write(buff, static_cast<long>(got));
-
- int complete = static_cast<int>(std::floor(100.0 * adl_positionTell(myDevice) / total));
- flushout(stdout);
- if(complete_prev != complete)
- {
- std::fprintf(stdout, " \r");
- std::fprintf(stdout, "Recording WAV... [%d%% completed]\r", complete);
- std::fflush(stdout);
- complete_prev = complete;
- }
- }
- wave_close();
- std::fprintf(stdout, " \n\n");
-
- if(stop)
- std::fprintf(stdout, "Interrupted! Recorded WAV is incomplete, but playable!\n");
- else
- std::fprintf(stdout, "Completed!\n");
- flushout(stdout);
- }
- else
+ int ret = runWaveOutLoopLoop(myDevice, musPath, sampleRate);
+ if(ret != 0)
{
adl_close(myDevice);
- return 1;
+ return ret;
}
}
#endif //HARDWARE_OPL3
#ifdef HARDWARE_OPL3
-
-# ifdef __DJGPP__
- // Fix the skewed clock and reset BIOS tick rate
- _farpokel(_dos_ds, 0x46C, BIOStimer_begin +
- (BIOStimer - BIOStimer_begin)
- * (0x1234DD / 65536.0) / newTimerFreq);
-
- //disable();
- outportb(0x43, 0x34);
- outportb(0x40, 0);
- outportb(0x40, 0);
- //enable();
-# endif
-
-# ifdef __WATCOMC__
- outp(0x43, 0x34);
- outp(0x40, 0);
- outp(0x40, 0);
-# endif
-
- adl_panic(myDevice); //Shut up all sustaining notes
-
+ s_timeCounter.restoreDosTimer();
#endif
adl_close(myDevice);
return 0;
}
-
diff --git a/utils/midiplay/utf8main.cmake b/utils/midiplay/utf8main.cmake
new file mode 100644
index 0000000..1dcac78
--- /dev/null
+++ b/utils/midiplay/utf8main.cmake
@@ -0,0 +1,10 @@
+# message("Path to UTF8-Main is [${CMAKE_CURRENT_LIST_DIR}]")
+include_directories(${CMAKE_CURRENT_LIST_DIR}/)
+
+set(UTF8MAIN_SRCS)
+
+if(WIN32)
+ list(APPEND UTF8MAIN_SRCS
+ ${CMAKE_CURRENT_LIST_DIR}/utf8main_win32.cpp
+ )
+endif()
diff --git a/utils/midiplay/utf8main.h b/utils/midiplay/utf8main.h
new file mode 100644
index 0000000..8cab2cc
--- /dev/null
+++ b/utils/midiplay/utf8main.h
@@ -0,0 +1,41 @@
+/*
+ * utf8main - a small wrapper over entry points
+ * to provide the UTF8 encoded main function
+ *
+ * Copyright (c) 2017-2023 Vitaly Novichkov <admin@wohlnet.ru>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef UTF8MAIN_H
+#define UTF8MAIN_H
+
+#ifndef PGE_ENGINE//Don't use in PGE Engine where SDL Main is already in use
+//Windows platform
+#ifdef _WIN32
+//Avoid Qt to steal Main function
+#if defined(QT_NEEDS_QMAIN)
+#undef main
+#endif
+//Define custom UTF8 main function which will convert command line arguments into UTF8 and will pass them
+#define main UTF8_Main
+#endif
+
+#endif
+
+#endif // UTF8MAIN_H
diff --git a/utils/midiplay/utf8main_win32.cpp b/utils/midiplay/utf8main_win32.cpp
new file mode 100644
index 0000000..1159b97
--- /dev/null
+++ b/utils/midiplay/utf8main_win32.cpp
@@ -0,0 +1,95 @@
+/*
+ * utf8main - a small wrapper over entry points
+ * to provide the UTF8 encoded main function
+ *
+ * Copyright (c) 2017-2023 Vitaly Novichkov <admin@wohlnet.ru>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef _WIN32
+#include <windows.h>
+#include <shellapi.h>
+#include <string>
+#include <vector>
+
+#ifdef PGE_ENGINE
+# include <SDL2/SDL_main.h>
+#else
+# include "utf8main.h"
+#endif
+
+#ifndef _In_
+# define _In_
+#endif
+#ifndef _In_opt_
+# define _In_opt_
+#endif
+
+extern int main(int argc, char *argv[]);
+
+static void buildUtf8Args(std::vector<std::string> &utf8_args)
+{
+ //Get raw UTF-16 command line
+ wchar_t *cmdLineW = GetCommandLineW();
+ int argc = 0;
+ //Convert it into array of strings
+ wchar_t **argvW = CommandLineToArgvW(cmdLineW, &argc);
+
+ utf8_args.reserve(argc);
+ //Convert every argument into UTF-8
+ for(int i = 0; i < argc; i++)
+ {
+ wchar_t *argW = argvW[i];
+ size_t argWlen = wcslen(argW);
+ std::string arg;
+ arg.resize(argWlen * 2);
+ size_t newLen = WideCharToMultiByte(CP_UTF8, 0, argW, static_cast<int>(argWlen), &arg[0], static_cast<int>(arg.size()), 0, 0);
+ arg.resize(newLen);
+ utf8_args.push_back(arg);
+ }
+}
+
+#ifdef WIN32_CONSOLE
+#undef main
+int main()
+# define main UTF8_Main
+#else
+int WINAPI WinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PSTR, _In_ int)
+#endif
+{
+ //! Storage of UTF8-encoded command line arguments
+ std::vector<std::string> g_utf8_args;
+ //! Storage of the pointers to begin of every argument
+ std::vector<char *> g_utf8_argvV;
+
+ buildUtf8Args(g_utf8_args);
+
+#ifdef UTF8Main_Debug
+ printf("UTF8 ARGS RAN!\n");
+ fflush(stdout);
+#endif
+
+ size_t argc = g_utf8_args.size();
+ g_utf8_argvV.reserve(argc);
+ for(size_t i = 0; i < argc; i++)
+ g_utf8_argvV.push_back(&g_utf8_args[i][0]);
+
+ return main((int)argc, g_utf8_argvV.data());
+}
+#endif
diff --git a/utils/vlc_codec/libadlmidi.c b/utils/vlc_codec/libadlmidi.c
index fe13237..be8664e 100644
--- a/utils/vlc_codec/libadlmidi.c
+++ b/utils/vlc_codec/libadlmidi.c
@@ -89,6 +89,36 @@
#define ENABLE_AUTO_ARPEGGIO_LONGTEXT N_( \
"Enables an automatical arpeggio to keep chords playing when there is no enough free physical voices of chips.")
+
+#ifdef ADLMIDI_ENABLE_HW_SERIAL
+
+#define SERIAL_ENABLE_TEXT N_("Enable Serial hardware mode")
+#define SERIAL_ENABLE_LONGTEXT N_( \
+ "Use the hardware OPL3 chip via Serial port.")
+
+#define SERIAL_NAME_TEXT N_("Serial device name")
+#define SERIAL_NAME_LONGTEXT N_( \
+ "Name of the Serial device to use.")
+
+#define SERIAL_BAUD_TEXT N_("Serial baud speed")
+#define SERIAL_BAUD_LONGTEXT N_( \
+ "The baud speed value of the Serial device.")
+
+#define SERIAL_PROTO_TEXT N_("Serial protocol")
+#define SERIAL_PROTO_LONGTEXT N_( \
+ "The protocol to use the Serial device.")
+
+static const int serial_protos_values[] = { 1, 2, 3 };
+static const char * const serial_protos_descriptions[] =
+ {
+ N_("Arduino OPL2"),
+ N_("NukeYkt OPL3"),
+ N_("RetroWave OPL3"),
+ NULL
+};
+#endif // ADLMIDI_ENABLE_HW_SERIAL
+
+
static const int volume_models_values[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
static const char * const volume_models_descriptions[] =
{
@@ -192,6 +222,17 @@ vlc_module_begin ()
add_bool( CONFIG_PREFIX "full-panning", true, FULL_PANNING_TEXT,
FULL_PANNING_LONGTEXT, true )
+#ifdef ADLMIDI_ENABLE_HW_SERIAL
+ add_bool(CONFIG_PREFIX "serial-enable", false, SERIAL_ENABLE_TEXT, SERIAL_ENABLE_LONGTEXT, true)
+ add_string(CONFIG_PREFIX "serial-name", "", SERIAL_NAME_TEXT, SERIAL_NAME_LONGTEXT, true)
+ add_integer(CONFIG_PREFIX "serial-baud", 115200, SERIAL_BAUD_TEXT, SERIAL_BAUD_LONGTEXT, true)
+ change_integer_range (9600, 115200)
+
+ add_integer (CONFIG_PREFIX "serial-proto", 3, SERIAL_PROTO_TEXT, SERIAL_PROTO_LONGTEXT, false)
+ change_integer_list( serial_protos_values, serial_protos_descriptions )
+#endif
+
+
vlc_module_end ()
@@ -201,6 +242,7 @@ struct decoder_sys_t
int sample_rate;
int soundfont;
date_t end_date;
+ int is_serial;
};
static const struct ADLMIDI_AudioFormat g_output_format =
@@ -234,15 +276,25 @@ static int Open (vlc_object_t *p_this)
p_sys->sample_rate = var_InheritInteger (p_this, CONFIG_PREFIX "sample-rate");
p_sys->synth = adl_init( p_sys->sample_rate );
- adl_switchEmulator(p_sys->synth, var_InheritInteger(p_this, CONFIG_PREFIX "emulator-type"));
-
- adl_setNumChips(p_sys->synth, var_InheritInteger(p_this, CONFIG_PREFIX "emulated-chips"));
+ if(var_InheritBool(p_this, CONFIG_PREFIX "serial-enable"))
+ {
+ adl_switchSerialHW(p_sys->synth,
+ var_InheritString(p_this, CONFIG_PREFIX "serial-name"),
+ var_InheritInteger(p_this, CONFIG_PREFIX "serial-baud"),
+ var_InheritInteger(p_this, CONFIG_PREFIX "serial-proto")
+ );
+ p_sys->is_serial = 1;
+ }
+ else
+ {
+ adl_switchEmulator(p_sys->synth, var_InheritInteger(p_this, CONFIG_PREFIX "emulator-type"));
+ adl_setNumChips(p_sys->synth, var_InheritInteger(p_this, CONFIG_PREFIX "emulated-chips"));
+ adl_setSoftPanEnabled(p_sys->synth, var_InheritBool(p_this, CONFIG_PREFIX "full-panning"));
+ p_sys->is_serial = 0;
+ }
adl_setVolumeRangeModel(p_sys->synth, var_InheritInteger(p_this, CONFIG_PREFIX "volume-model"));
-
- adl_setChannelAllocMode(p_sys->synth, var_InheritInteger(p_this, CONFIG_PREFIX "channel-allocation"));
-
- adl_setSoftPanEnabled(p_sys->synth, var_InheritBool(p_this, CONFIG_PREFIX "full-panning"));
+ adl_setChannelAllocMode(p_sys->synth, var_InheritInteger(p_this, CONFIG_PREFIX "channel-allocation"));
adl_setFullRangeBrightness(p_sys->synth, var_InheritBool(p_this, CONFIG_PREFIX "full-range-brightness"));
@@ -290,6 +342,9 @@ static void Close (vlc_object_t *p_this)
{
decoder_sys_t *p_sys = ((decoder_t *)p_this)->p_sys;
+ if(p_sys->is_serial)
+ adl_panic(p_sys->synth);
+
adl_close(p_sys->synth);
free (p_sys);
}
@@ -417,8 +472,11 @@ static block_t *DecodeBlock (decoder_t *p_dec, block_t **pp_block)
break;
}
- unsigned samples =
- (p_block->i_pts - date_Get (&p_sys->end_date)) * 441 / 10000;
+ unsigned samples = (p_block->i_pts - date_Get (&p_sys->end_date)) * 441 / 10000;
+#ifdef ADLMIDI_ENABLE_HW_SERIAL
+ double delay = ((p_block->i_pts - date_Get (&p_sys->end_date)) / 1000000.0) / 2.0;
+#endif
+
if (samples == 0)
goto drop;
@@ -432,13 +490,27 @@ static block_t *DecodeBlock (decoder_t *p_dec, block_t **pp_block)
goto drop;
p_out->i_pts = date_Get (&p_sys->end_date );
- samples = adl_generateFormat(p_sys->synth, (int)samples * 2,
- (ADL_UInt8*)p_out->p_buffer,
- (ADL_UInt8*)(p_out->p_buffer + g_output_format.containerSize),
- &g_output_format);
- for (it = 0; it < samples; ++it)
- ((float*)p_out->p_buffer)[it] *= 2.0f;
+#ifdef ADLMIDI_ENABLE_HW_SERIAL
+ if(p_sys->is_serial)
+ {
+ if(delay > 0.0)
+ adl_tickIterators(p_sys->synth, delay);
+
+ for (it = 0; it < samples * 2; ++it)
+ ((float*)p_out->p_buffer)[it] = 0.0f;
+ }
+ else
+#endif
+ {
+ samples = adl_generateFormat(p_sys->synth, (int)samples * 2,
+ (ADL_UInt8*)p_out->p_buffer,
+ (ADL_UInt8*)(p_out->p_buffer + g_output_format.containerSize),
+ &g_output_format);
+
+ for (it = 0; it < samples; ++it)
+ ((float*)p_out->p_buffer)[it] *= 2.0f;
+ }
samples /= 2;
p_out->i_length = date_Increment (&p_sys->end_date, samples) - p_out->i_pts;