aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt57
-rw-r--r--README.md6
-rw-r--r--include/adlmidi.h20
-rw-r--r--include/adlmidi.hpp57
-rw-r--r--src/adlmidi.cpp91
-rw-r--r--src/adlmidi_load.cpp42
-rw-r--r--src/adlmidi_midiplay.cpp204
-rw-r--r--src/adlmidi_mus2mid.h9
-rw-r--r--src/adlmidi_opl3.cpp32
-rw-r--r--src/adlmidi_private.cpp4
-rw-r--r--src/adlmidi_private.hpp92
-rw-r--r--src/adlmidi_xmi2mid.h9
-rw-r--r--src/fraction.hpp (renamed from src/fraction.h)0
-rw-r--r--utils/adlmidi-2/adlmidi-2.pro5
-rw-r--r--utils/adlmidi-2/midiplay.cc480
15 files changed, 793 insertions, 315 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4d9c39b..3be77fb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -34,10 +34,12 @@ ENDIF()
#===========================================================================================
option(WITH_MIDIPLAY "Build also demo MIDI player" OFF)
+option(WITH_ADLMIDI2 "Build also classic ADLMIDI player [EXPERIMENTAL]" OFF)
option(WITH_OLD_UTILS "Build also old utilities" OFF)
option(WITH_EMBEDDED_BANKS "Use embedded banks" ON)
option(WITH_GENADLDATA "Build and run full rebuild of embedded banks cache" OFF)
option(USE_DOSBOX_EMULATOR "Use DosBox emulator" OFF)
+option(WITH_CPP_EXTRAS "Build with support for C++ extras (features are can be found in 'adlmidi.hpp' header)" OFF)
option(libADLMIDI_STATIC "Build static library of libADLMIDI" ON)
option(libADLMIDI_SHARED "Build shared library of libADLMIDI" OFF)
@@ -130,6 +132,10 @@ else()
add_definitions(-DDISABLE_EMBEDDED_BANKS)
endif()
+if(NOT WITH_CPP_EXTRAS)
+ add_definitions(-DADLMIDI_DISABLE_CPP_EXTRAS)
+endif()
+
if(libADLMIDI_STATIC)
add_library(ADLMIDI_static STATIC ${libADLMIDI_SOURCES})
set_target_properties(ADLMIDI_static PROPERTIES OUTPUT_NAME ADLMIDI)
@@ -189,6 +195,54 @@ if(WITH_MIDIPLAY)
list(APPEND libADLMIDI_INSTALLS adlmidiplay)
endif()
+if(WITH_ADLMIDI2)
+ find_library(SDL2_LIBRARY SDL2 REQUIRED)
+ include_directories(${SDL2_INCLUDE_DIR})
+ message("Found ${SDL2_LIBRARY}")
+
+ add_executable(adlmidi2
+ ${libADLMIDI_SOURCE_DIR}/utils/adlmidi-2/midiplay.cc
+ )
+
+ if(WIN32)
+ if(MSVC)
+ target_link_libraries(adlmidi2 ADLMIDI ${SDL2_LIBRARY})
+ else()
+ target_link_libraries(adlmidi2 ADLMIDI ${SDL2_LIBRARY} pthread)
+ endif()
+ else()
+ target_link_libraries(adlmidi2 ADLMIDI ${SDL2_LIBRARY} pthread dl)
+ endif()
+
+ if(NOT WITH_CPP_EXTRAS)
+ message(FATAL_ERROR "ADLMIDI2 Requires C++ Extras. Please enable WITH_CPP_EXTRAS option!")
+ endif()
+
+ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR
+ CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR
+ "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
+ message("Turned on C++11 on GCC/CLang/Intel")
+ target_compile_options(adlmidi2 PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-std=c++11>)
+ else(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+ message("Turned on C++11 on MSVC")
+ target_compile_options(adlmidi2 PUBLIC "/std:c++latest")
+ endif()
+
+ if(libADLMIDI_SHARED)
+ add_dependencies(adlmidi2 ADLMIDI_shared)
+ # ========= WIP =========
+ # set_target_properties(adlmidiplay PROPERTIES COMPILE_FLAGS "-Wl,-rpath='$$ORIGIN/../lib'")
+ else()
+ if(NOT libADLMIDI_STATIC)
+ message(FATAL_ERROR "libADLMIDI is required to be built!")
+ endif()
+ add_dependencies(adlmidi2 ADLMIDI_static)
+ endif()
+
+ list(APPEND libADLMIDI_INSTALLS adlmidi2)
+endif()
+
+
install(TARGETS ${libADLMIDI_INSTALLS}
RUNTIME DESTINATION "bin"
LIBRARY DESTINATION "lib"
@@ -197,14 +251,17 @@ install(TARGETS ${libADLMIDI_INSTALLS}
install(FILES
include/adlmidi.h
+ include/adlmidi.hpp
DESTINATION include/)
message("==== libADLMIDI options ====")
message("WITH_MIDIPLAY = ${WITH_MIDIPLAY}")
+message("WITH_ADLMIDI2 = ${WITH_ADLMIDI2}")
message("WITH_OLD_UTILS = ${WITH_OLD_UTILS}")
message("WITH_EMBEDDED_BANKS = ${WITH_EMBEDDED_BANKS}")
message("WITH_GENADLDATA = ${WITH_GENADLDATA}")
message("USE_DOSBOX_EMULATOR = ${USE_DOSBOX_EMULATOR}")
+message("WITH_CPP_EXTRAS = ${WITH_CPP_EXTRAS}")
message("libADLMIDI_STATIC = ${libADLMIDI_STATIC}")
message("libADLMIDI_SHARED = ${libADLMIDI_SHARED}")
diff --git a/README.md b/README.md
index 37599d5..b7e4635 100644
--- a/README.md
+++ b/README.md
@@ -66,6 +66,7 @@ You need to make in the any IDE a library project and put into it next files
### Public header (include)
* adlmidi.h - Library Public API header, use it to control library
+* adlmidi.hpp - Public additional C++ API header, optional
### Internal code (src)
* adldata.hh - bank structures definition
@@ -73,7 +74,7 @@ You need to make in the any IDE a library project and put into it next files
* adlmidi_private.hpp - header of internal private APIs
* adlmidi_xmi2mid.h - XMI2MID converter header
* dbopl.h - DOSBOX OPL Emulation header
-* fraction.h - Fraction number handling
+* fraction.hpp - Fraction number handling
* nukedopl3.h - Nuked OPL3 Emulation header
* adldata.cpp - Automatically generated database of FM banks from "fm_banks" directory via "gen_adldata" tool. **Don't build it if you have defined `DISABLE_EMBEDDED_BANKS` macro!**
@@ -134,6 +135,9 @@ To build that example you will need to have installed SDL2 library.
* Fixed the ability to merge two equal pseudo-4-operator voices as one voice without damaging the result!
* Added auto-increasing of percussion note lengths when there are too short and playing an incorrect sound on various banks
* Tri-state support for deep-tremolo/vibrato, scale modulators, and legacy adlib percussion mode. -1 means "auto", I.e. default and specified by bank.
+ * Added new functions: adl_linkedLibraryVersion(), adl_errorInfo(), adl_tickEvents(), and adl_generate()
+ * Error string is no more global, now every ADL_MIDIPlayer instance has own thread-safe error info that can be retreived by using adl_errorInfo() function. The adl_errorString() will return library initialization errors only;
+ * Added ะก++ Extra public API which now includes instrument testing feature (which is required by classic ADLMIDI utility)
* ...
## 1.2.1 2017-07-30
diff --git a/include/adlmidi.h b/include/adlmidi.h
index 24fa4c9..1073721 100644
--- a/include/adlmidi.h
+++ b/include/adlmidi.h
@@ -104,9 +104,15 @@ extern int adl_openBankData(struct ADL_MIDIPlayer *device, void *mem, long size)
/*Returns name of currently used OPL3 emulator*/
extern const char *adl_emulatorName();
-/*Returns string which contains last error message*/
+/*Returns string which contains a version number*/
+extern const char *adl_linkedLibraryVersion();
+
+/*Returns string which contains last error message of initialization*/
extern const char *adl_errorString();
+/*Returns string which contains last error message on specific device*/
+extern const char *adl_errorInfo(ADL_MIDIPlayer *device);
+
/*Initialize ADLMIDI Player device*/
extern struct ADL_MIDIPlayer *adl_init(long sample_rate);
@@ -181,6 +187,18 @@ extern int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short out[]
/*Generate audio output from chip emulators without iteration of MIDI timers. 512 samples per channel is a maximum*/
extern int adl_generate(ADL_MIDIPlayer *device, int sampleCount, short *out);
+/**
+ * @brief Periodic tick handler.
+ * @param device
+ * @param seconds seconds since last call
+ * @param granularity don't expect intervals smaller than this, in seconds
+ * @return desired number of seconds until next call
+ *
+ * Use it for Hardware OPL3 mode or when you want to process events differently from adl_play() function.
+ * DON'T USE IT TOGETHER WITH adl_play()!!!
+ */
+extern double adl_tickEvents(ADL_MIDIPlayer *device, double seconds, double granuality);
+
/**Hooks**/
typedef void (*ADL_RawEventHook)(void *userdata, ADL_UInt8 type, ADL_UInt8 subtype, ADL_UInt8 channel, ADL_UInt8 *data, size_t len);
diff --git a/include/adlmidi.hpp b/include/adlmidi.hpp
new file mode 100644
index 0000000..c589e3b
--- /dev/null
+++ b/include/adlmidi.hpp
@@ -0,0 +1,57 @@
+/*
+ * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation
+ *
+ * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
+ * ADLMIDI Library API: Copyright (c) 2017 Vitaly Novichkov <admin@wohlnet.ru>
+ *
+ * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
+ * http://iki.fi/bisqwit/source/adlmidi.html
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ADLMIDI_HPP
+#define ADLMIDI_HPP
+
+#include "adlmidi.h"
+
+#include <stdint.h>
+#include <vector>
+
+class OPL3;
+class MIDIplay;
+
+class AdlInstrumentTester
+{
+ uint32_t cur_gm;
+ uint32_t ins_idx;
+ std::vector<uint32_t> adl_ins_list;
+ OPL3 *opl;
+ MIDIplay * play;
+
+public:
+ AdlInstrumentTester(ADL_MIDIPlayer *device);
+ virtual ~AdlInstrumentTester();
+
+ // Find list of adlib instruments that supposedly implement this GM
+ void FindAdlList();
+ void Touch(unsigned c, unsigned volume);
+ void DoNote(int note);
+ void NextGM(int offset);
+ void NextAdl(int offset);
+ bool HandleInputChar(char ch);
+};
+
+#endif //ADLMIDI_HPP
+
diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp
index 59d7a7e..dc9c980 100644
--- a/src/adlmidi.cpp
+++ b/src/adlmidi.cpp
@@ -23,7 +23,11 @@
#include "adlmidi_private.hpp"
+#ifdef ADLMIDI_HW_OPL
+static const unsigned MaxCards = 1;
+#else
static const unsigned MaxCards = 100;
+#endif
/*---------------------------EXPORTS---------------------------*/
@@ -31,7 +35,20 @@ ADLMIDI_EXPORT struct ADL_MIDIPlayer *adl_init(long sample_rate)
{
ADL_MIDIPlayer *midi_device;
midi_device = (ADL_MIDIPlayer *)malloc(sizeof(ADL_MIDIPlayer));
+ if(!midi_device)
+ {
+ ADLMIDI_ErrorString = "Can't initialize ADLMIDI: out of memory!";
+ return NULL;
+ }
+
MIDIplay *player = new MIDIplay;
+ if(!player)
+ {
+ free(midi_device);
+ ADLMIDI_ErrorString = "Can't initialize ADLMIDI: out of memory!";
+ return NULL;
+ }
+
midi_device->adl_midiPlayer = player;
player->m_setup.PCM_RATE = static_cast<unsigned long>(sample_rate);
player->m_setup.mindelay = 1.0 / (double)player->m_setup.PCM_RATE;
@@ -52,7 +69,7 @@ ADLMIDI_EXPORT int adl_setNumCards(ADL_MIDIPlayer *device, int numCards)
{
std::stringstream s;
s << "number of cards may only be 1.." << MaxCards << ".\n";
- ADLMIDI_ErrorString = s.str();
+ play->setErrorString(s.str());
return -1;
}
@@ -83,7 +100,7 @@ ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank)
{
std::stringstream s;
s << "bank number may only be 0.." << (NumBanks - 1) << ".\n";
- ADLMIDI_ErrorString = s.str();
+ play->setErrorString(s.str());
return -1;
}
@@ -113,7 +130,7 @@ ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4)
{
std::stringstream s;
s << "number of four-op channels may only be 0.." << (6 * (play->m_setup.NumCards)) << " when " << play->m_setup.NumCards << " OPL3 cards are used.\n";
- ADLMIDI_ErrorString = s.str();
+ play->setErrorString(s.str());
return -1;
}
@@ -178,8 +195,6 @@ ADLMIDI_EXPORT void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int v
ADLMIDI_EXPORT int adl_openBankFile(struct ADL_MIDIPlayer *device, char *filePath)
{
- ADLMIDI_ErrorString.clear();
-
if(device && device->adl_midiPlayer)
{
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
@@ -187,8 +202,9 @@ ADLMIDI_EXPORT int adl_openBankFile(struct ADL_MIDIPlayer *device, char *filePat
play->m_setup.backup_samples_size = 0;
if(!play->LoadBank(filePath))
{
- if(ADLMIDI_ErrorString.empty())
- ADLMIDI_ErrorString = "ADL MIDI: Can't load file";
+ std::string err = play->getErrorString();
+ if(err.empty())
+ play->setErrorString("ADL MIDI: Can't load file");
return -1;
}
else return 0;
@@ -200,8 +216,6 @@ ADLMIDI_EXPORT int adl_openBankFile(struct ADL_MIDIPlayer *device, char *filePat
ADLMIDI_EXPORT int adl_openBankData(struct ADL_MIDIPlayer *device, void *mem, long size)
{
- ADLMIDI_ErrorString.clear();
-
if(device && device->adl_midiPlayer)
{
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
@@ -209,8 +223,9 @@ ADLMIDI_EXPORT int adl_openBankData(struct ADL_MIDIPlayer *device, void *mem, lo
play->m_setup.backup_samples_size = 0;
if(!play->LoadBank(mem, static_cast<size_t>(size)))
{
- if(ADLMIDI_ErrorString.empty())
- ADLMIDI_ErrorString = "ADL MIDI: Can't load data from memory";
+ std::string err = play->getErrorString();
+ if(err.empty())
+ play->setErrorString("ADL MIDI: Can't load data from memory");
return -1;
}
else return 0;
@@ -222,8 +237,6 @@ ADLMIDI_EXPORT int adl_openBankData(struct ADL_MIDIPlayer *device, void *mem, lo
ADLMIDI_EXPORT int adl_openFile(ADL_MIDIPlayer *device, char *filePath)
{
- ADLMIDI_ErrorString.clear();
-
if(device && device->adl_midiPlayer)
{
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
@@ -231,8 +244,9 @@ ADLMIDI_EXPORT int adl_openFile(ADL_MIDIPlayer *device, char *filePath)
play->m_setup.backup_samples_size = 0;
if(!play->LoadMIDI(filePath))
{
- if(ADLMIDI_ErrorString.empty())
- ADLMIDI_ErrorString = "ADL MIDI: Can't load file";
+ std::string err = play->getErrorString();
+ if(err.empty())
+ play->setErrorString("ADL MIDI: Can't load file");
return -1;
}
else return 0;
@@ -244,8 +258,6 @@ ADLMIDI_EXPORT int adl_openFile(ADL_MIDIPlayer *device, char *filePath)
ADLMIDI_EXPORT int adl_openData(ADL_MIDIPlayer *device, void *mem, long size)
{
- ADLMIDI_ErrorString.clear();
-
if(device && device->adl_midiPlayer)
{
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
@@ -253,13 +265,13 @@ ADLMIDI_EXPORT int adl_openData(ADL_MIDIPlayer *device, void *mem, long size)
play->m_setup.backup_samples_size = 0;
if(!play->LoadMIDI(mem, static_cast<size_t>(size)))
{
- if(ADLMIDI_ErrorString.empty())
- ADLMIDI_ErrorString = "ADL MIDI: Can't load data from memory";
+ std::string err = play->getErrorString();
+ if(err.empty())
+ play->setErrorString("ADL MIDI: Can't load data from memory");
return -1;
}
else return 0;
}
-
ADLMIDI_ErrorString = "Can't load file: ADL MIDI is not initialized";
return -1;
}
@@ -274,16 +286,35 @@ ADLMIDI_EXPORT const char *adl_emulatorName()
#endif
}
+ADLMIDI_EXPORT const char *adl_linkedLibraryVersion()
+{
+ return ADLMIDI_VERSION;
+}
+
+
ADLMIDI_EXPORT const char *adl_errorString()
{
return ADLMIDI_ErrorString.c_str();
}
+ADLMIDI_EXPORT const char *adl_errorInfo(ADL_MIDIPlayer *device)
+{
+ if(!device)
+ return adl_errorString();
+ MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
+ if(!play)
+ return adl_errorString();
+ return play->getErrorString().c_str();
+}
+
ADLMIDI_EXPORT const char *adl_getMusicTitle(ADL_MIDIPlayer *device)
{
if(!device)
return "";
- return reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->musTitle.c_str();
+ MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
+ if(!play)
+ return "";
+ return play->musTitle.c_str();
}
ADLMIDI_EXPORT void adl_close(ADL_MIDIPlayer *device)
@@ -477,6 +508,9 @@ inline static void SendStereoAudio(MIDIplay::Setup &device,
ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out)
{
+ #ifdef ADLMIDI_HW_OPL
+ return 0;
+ #else
sampleCount -= sampleCount % 2; //Avoid even sample requests
if(sampleCount < 0)
return 0;
@@ -573,11 +607,15 @@ ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out)
}
return static_cast<int>(gotten_len);
+ #endif
}
ADLMIDI_EXPORT int adl_generate(ADL_MIDIPlayer *device, int sampleCount, short *out)
{
+ #ifdef ADLMIDI_HW_OPL
+ return 0;
+ #else
sampleCount -= sampleCount % 2; //Avoid even sample requests
if(sampleCount < 0)
return 0;
@@ -617,4 +655,15 @@ ADLMIDI_EXPORT int adl_generate(ADL_MIDIPlayer *device, int sampleCount, short *
}
return sampleCount;
+ #endif
+}
+
+ADLMIDI_EXPORT double adl_tickEvents(ADL_MIDIPlayer *device, double seconds, double granuality)
+{
+ if(!device)
+ return -1.0;
+ MIDIplay *player = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
+ if(!player)
+ return -1.0;
+ return player->Tick(seconds, granuality);
}
diff --git a/src/adlmidi_load.cpp b/src/adlmidi_load.cpp
index 16881d4..76765dc 100644
--- a/src/adlmidi_load.cpp
+++ b/src/adlmidi_load.cpp
@@ -176,7 +176,7 @@ bool MIDIplay::LoadBank(MIDIplay::fileReader &fr)
ADL_UNUSED(fsize);
if(!fr.isValid())
{
- ADLMIDI_ErrorString = "Custom bank: Invalid data stream!";
+ errorStringOut = "Custom bank: Invalid data stream!";
return false;
}
@@ -190,27 +190,27 @@ bool MIDIplay::LoadBank(MIDIplay::fileReader &fr)
if(fr.read(magic, 1, 11) != 11)
{
- ADLMIDI_ErrorString = "Custom bank: Can't read magic number!";
+ errorStringOut = "Custom bank: Can't read magic number!";
return false;
}
if(strncmp(magic, wopl3_magic, 11) != 0)
{
- ADLMIDI_ErrorString = "Custom bank: Invalid magic number!";
+ errorStringOut = "Custom bank: Invalid magic number!";
return false;
}
uint8_t version_raw[2];
if(fr.read(version_raw, 1, 2) != 2)
{
- ADLMIDI_ErrorString = "Custom bank: Can't read version!";
+ errorStringOut = "Custom bank: Can't read version!";
return false;
}
version = toUint16LE(version_raw);
if(version > wopl_latest_version)
{
- ADLMIDI_ErrorString = "Custom bank: Unsupported WOPL version!";
+ errorStringOut = "Custom bank: Unsupported WOPL version!";
return false;
}
@@ -218,7 +218,7 @@ bool MIDIplay::LoadBank(MIDIplay::fileReader &fr)
memset(head, 0, 6);
if(fr.read(head, 1, 6) != 6)
{
- ADLMIDI_ErrorString = "Custom bank: Can't read header!";
+ errorStringOut = "Custom bank: Can't read header!";
return false;
}
@@ -227,7 +227,7 @@ bool MIDIplay::LoadBank(MIDIplay::fileReader &fr)
if((count_melodic_banks < 1) || (count_percusive_banks < 1))
{
- ADLMIDI_ErrorString = "Custom bank: Too few banks in this file!";
+ errorStringOut = "Custom bank: Too few banks in this file!";
return false;
}
@@ -248,7 +248,7 @@ bool MIDIplay::LoadBank(MIDIplay::fileReader &fr)
uint8_t bank_meta[34];
if(fr.read(bank_meta, 1, 34) != 34)
{
- ADLMIDI_ErrorString = "Custom bank: Fail to read melodic bank meta-data!";
+ errorStringOut = "Custom bank: Fail to read melodic bank meta-data!";
return false;
}
//strncpy(bankMeta.name, char_p(bank_meta), 32);
@@ -261,7 +261,7 @@ bool MIDIplay::LoadBank(MIDIplay::fileReader &fr)
uint8_t bank_meta[34];
if(fr.read(bank_meta, 1, 34) != 34)
{
- ADLMIDI_ErrorString = "Custom bank: Fail to read melodic bank meta-data!";
+ errorStringOut = "Custom bank: Fail to read melodic bank meta-data!";
return false;
}
//strncpy(bankMeta.name, char_p(bank_meta), 32);
@@ -286,7 +286,7 @@ tryAgain:
{
opl.dynamic_metainstruments.clear();
opl.dynamic_instruments.clear();
- ADLMIDI_ErrorString = "Custom bank: Fail to read instrument!";
+ errorStringOut = "Custom bank: Fail to read instrument!";
return false;
}
@@ -356,9 +356,9 @@ bool MIDIplay::LoadMIDI(MIDIplay::fileReader &fr)
if(!fr.isValid())
{
- ADLMIDI_ErrorString = "Invalid data stream!\n";
+ errorStringOut = "Invalid data stream!\n";
#ifndef _WIN32
- ADLMIDI_ErrorString += std::strerror(errno);
+ errorStringOut += std::strerror(errno);
#endif
return false;
}
@@ -423,7 +423,7 @@ riffskip:
uint8_t *mus = (uint8_t *)malloc(mus_len);
if(!mus)
{
- ADLMIDI_ErrorString = "Out of memory!";
+ errorStringOut = "Out of memory!";
return false;
}
fr.read(mus, 1, mus_len);
@@ -437,7 +437,7 @@ riffskip:
if(mus) free(mus);
if(m2mret < 0)
{
- ADLMIDI_ErrorString = "Invalid MUS/DMX data format!";
+ errorStringOut = "Invalid MUS/DMX data format!";
return false;
}
cvt_buf.reset(mid);
@@ -451,7 +451,7 @@ riffskip:
if(std::memcmp(HeaderBuf + 8, "XDIR", 4) != 0)
{
fr.close();
- ADLMIDI_ErrorString = fr._fileName + ": Invalid format\n";
+ errorStringOut = fr._fileName + ": Invalid format\n";
return false;
}
@@ -461,7 +461,7 @@ riffskip:
uint8_t *mus = (uint8_t *)malloc(mus_len);
if(!mus)
{
- ADLMIDI_ErrorString = "Out of memory!";
+ errorStringOut = "Out of memory!";
return false;
}
fr.read(mus, 1, mus_len);
@@ -475,7 +475,7 @@ riffskip:
if(mus) free(mus);
if(m2mret < 0)
{
- ADLMIDI_ErrorString = "Invalid XMI data format!";
+ errorStringOut = "Invalid XMI data format!";
return false;
}
cvt_buf.reset(mid);
@@ -613,7 +613,7 @@ riffskip:
if(std::memcmp(HeaderBuf, "MThd\0\0\0\6", 8) != 0)
{
fr.close();
- ADLMIDI_ErrorString = fr._fileName + ": Invalid format, Header signature is unknown!\n";
+ errorStringOut = fr._fileName + ": Invalid format, Header signature is unknown!\n";
return false;
}
@@ -710,7 +710,7 @@ riffskip:
if(std::memcmp(HeaderBuf, "MTrk", 4) != 0)
{
fr.close();
- ADLMIDI_ErrorString = fr._fileName + ": Invalid format, MTrk signature is not found!\n";
+ errorStringOut = fr._fileName + ": Invalid format, MTrk signature is not found!\n";
return false;
}
TrackLength = (size_t)ReadBEint(HeaderBuf + 4, 4);
@@ -746,14 +746,14 @@ riffskip:
if(totalGotten == 0)
{
- ADLMIDI_ErrorString = fr._fileName + ": Empty track data";
+ errorStringOut = fr._fileName + ": Empty track data";
return false;
}
//Build new MIDI events table (ALPHA!!!)
if(!buildTrackData())
{
- ADLMIDI_ErrorString = fr._fileName + ": MIDI data parsing error has occouped!\n" + errorString;
+ errorStringOut = fr._fileName + ": MIDI data parsing error has occouped!\n" + errorString;
return false;
}
diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp
index fa95e32..7d1ba75 100644
--- a/src/adlmidi_midiplay.cpp
+++ b/src/adlmidi_midiplay.cpp
@@ -529,9 +529,9 @@ bool MIDIplay::buildTrackData()
#if 1 //Use this to record WAVEs for comparison before/after implementing of this
if(opl.m_musicMode == OPL3::MODE_MIDI)//Percussion fix is needed for MIDI only, not for IMF/RSXX or CMF
{
-//! Minimal real time in seconds
+ //! Minimal real time in seconds
#define DRUM_NOTE_MIN_TIME 0.03
-//! Minimal ticks count
+ //! Minimal ticks count
#define DRUM_NOTE_MIN_TICKS 15
struct NoteState
{
@@ -1778,6 +1778,16 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t **pptr, uint8_t *end, int &stat
return evt;
}
+const std::string &MIDIplay::getErrorString()
+{
+ return errorStringOut;
+}
+
+void MIDIplay::setErrorString(const std::string &err)
+{
+ errorStringOut = err;
+}
+
void MIDIplay::HandleEvent(size_t tk, MIDIplay::MidiEvent &evt, int &status)
{
@@ -2323,3 +2333,193 @@ retry_arpeggio:
}
}
}
+
+
+#ifndef ADLMIDI_DISABLE_CPP_EXTRAS
+
+ADLMIDI_EXPORT AdlInstrumentTester::AdlInstrumentTester(ADL_MIDIPlayer *device)
+{
+ cur_gm = 0;
+ ins_idx = 0;
+ play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
+ if(!play)
+ return;
+ opl = &play->opl;
+}
+
+ADLMIDI_EXPORT AdlInstrumentTester::~AdlInstrumentTester()
+{}
+
+ADLMIDI_EXPORT void AdlInstrumentTester::FindAdlList()
+{
+ const unsigned NumBanks = (unsigned)adl_getBanksCount();
+ std::set<unsigned> adl_ins_set;
+ for(unsigned bankno = 0; bankno < NumBanks; ++bankno)
+ adl_ins_set.insert(banks[bankno][cur_gm]);
+ adl_ins_list.assign(adl_ins_set.begin(), adl_ins_set.end());
+ ins_idx = 0;
+ NextAdl(0);
+ opl->Silence();
+}
+
+
+
+ADLMIDI_EXPORT void AdlInstrumentTester::Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127
+{
+ if(opl->LogarithmicVolumes)// !!!ADL PRIVATE!!!
+ opl->Touch_Real(c, volume * 127 / (127 * 127 * 127) / 2);// !!!ADL PRIVATE!!!
+ else
+ {
+ // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A)
+ opl->Touch_Real(c, volume > 8725 ? static_cast<unsigned int>(std::log(volume) * 11.541561 + (0.5 - 104.22845)) : 0);// !!!ADL PRIVATE!!!
+ // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A)
+ //Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0);
+ }
+}
+
+ADLMIDI_EXPORT void AdlInstrumentTester::DoNote(int note)
+{
+ if(adl_ins_list.empty()) FindAdlList();
+ const unsigned meta = adl_ins_list[ins_idx];
+ const adlinsdata &ains = opl->GetAdlMetaIns(meta);// !!!ADL PRIVATE!!!
+
+ int tone = (cur_gm & 128) ? (cur_gm & 127) : (note + 50);
+ if(ains.tone)
+ {
+ /*if(ains.tone < 20)
+ tone += ains.tone;
+ else */
+ if(ains.tone < 128)
+ tone = ains.tone;
+ else
+ tone -= ains.tone - 128;
+ }
+ double hertz = 172.00093 * std::exp(0.057762265 * (tone + 0.0));
+ int i[2] = { ains.adlno1, ains.adlno2 };
+ int32_t adlchannel[2] = { 0, 3 };
+ if(i[0] == i[1])
+ {
+ adlchannel[1] = -1;
+ adlchannel[0] = 6; // single-op
+ if(play->hooks.onDebugMessage)
+ {
+ play->hooks.onDebugMessage(play->hooks.onDebugMessage_userData,
+ "noteon at %d(%d) for %g Hz\n", adlchannel[0], i[0], hertz);
+ }
+ }
+ else
+ {
+ if(play->hooks.onDebugMessage)
+ {
+ play->hooks.onDebugMessage(play->hooks.onDebugMessage_userData,
+ "noteon at %d(%d) and %d(%d) for %g Hz\n", adlchannel[0], i[0], adlchannel[1], i[1], hertz);
+ }
+ }
+
+ opl->NoteOff(0);
+ opl->NoteOff(3);
+ opl->NoteOff(6);
+ for(unsigned c = 0; c < 2; ++c)
+ {
+ if(adlchannel[c] < 0) continue;
+ opl->Patch((uint16_t)adlchannel[c], (uint16_t)i[c]);
+ opl->Touch_Real((uint16_t)adlchannel[c], 127 * 127 * 100);
+ opl->Pan((uint16_t)adlchannel[c], 0x30);
+ opl->NoteOn((uint16_t)adlchannel[c], hertz);
+ }
+}
+
+ADLMIDI_EXPORT void AdlInstrumentTester::NextGM(int offset)
+{
+ cur_gm = (cur_gm + 256 + (uint32_t)offset) & 0xFF;
+ FindAdlList();
+}
+
+ADLMIDI_EXPORT void AdlInstrumentTester::NextAdl(int offset)
+{
+ if(adl_ins_list.empty()) FindAdlList();
+ const unsigned NumBanks = (unsigned)adl_getBanksCount();
+ ins_idx = (uint32_t)((int32_t)ins_idx + (int32_t)adl_ins_list.size() + offset) % adl_ins_list.size();
+
+ #if 0
+ UI.Color(15);
+ std::fflush(stderr);
+ std::printf("SELECTED G%c%d\t%s\n",
+ cur_gm < 128 ? 'M' : 'P', cur_gm < 128 ? cur_gm + 1 : cur_gm - 128,
+ "<-> select GM, ^v select ins, qwe play note");
+ std::fflush(stdout);
+ UI.Color(7);
+ std::fflush(stderr);
+ #endif
+
+ for(unsigned a = 0; a < adl_ins_list.size(); ++a)
+ {
+ const unsigned i = adl_ins_list[a];
+ const adlinsdata &ains = opl->GetAdlMetaIns(i);
+
+ char ToneIndication[8] = " ";
+ if(ains.tone)
+ {
+ /*if(ains.tone < 20)
+ std::sprintf(ToneIndication, "+%-2d", ains.tone);
+ else*/
+ if(ains.tone < 128)
+ std::sprintf(ToneIndication, "=%-2d", ains.tone);
+ else
+ std::sprintf(ToneIndication, "-%-2d", ains.tone - 128);
+ }
+ std::printf("%s%s%s%u\t",
+ ToneIndication,
+ ains.adlno1 != ains.adlno2 ? "[2]" : " ",
+ (ins_idx == a) ? "->" : "\t",
+ i
+ );
+
+ for(unsigned bankno = 0; bankno < NumBanks; ++bankno)
+ if(banks[bankno][cur_gm] == i)
+ std::printf(" %u", bankno);
+
+ std::printf("\n");
+ }
+}
+
+ADLMIDI_EXPORT bool AdlInstrumentTester::HandleInputChar(char ch)
+{
+ static const char notes[] = "zsxdcvgbhnjmq2w3er5t6y7ui9o0p";
+ // c'd'ef'g'a'bC'D'EF'G'A'Bc'd'e
+ switch(ch)
+ {
+ case '/':
+ case 'H':
+ case 'A':
+ NextAdl(-1);
+ break;
+ case '*':
+ case 'P':
+ case 'B':
+ NextAdl(+1);
+ break;
+ case '-':
+ case 'K':
+ case 'D':
+ NextGM(-1);
+ break;
+ case '+':
+ case 'M':
+ case 'C':
+ NextGM(+1);
+ break;
+ case 3:
+ #if !((!defined(__WIN32__) || defined(__CYGWIN__)) && !defined(__DJGPP__))
+ case 27:
+ #endif
+ return false;
+ default:
+ const char *p = strchr(notes, ch);
+ if(p && *p)
+ DoNote((p - notes) - 12);
+ }
+ return true;
+}
+
+#endif//ADLMIDI_DISABLE_CPP_EXTRAS
diff --git a/src/adlmidi_mus2mid.h b/src/adlmidi_mus2mid.h
index c56c359..cc41b87 100644
--- a/src/adlmidi_mus2mid.h
+++ b/src/adlmidi_mus2mid.h
@@ -24,6 +24,15 @@
#include <stdint.h>
+#ifdef __DJGPP__
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef signed short int16_t;
+typedef unsigned short uint16_t;
+typedef signed long int32_t;
+typedef unsigned long uint32_t;
+#endif
+
#ifdef __cplusplus
extern "C"
{
diff --git a/src/adlmidi_opl3.cpp b/src/adlmidi_opl3.cpp
index ed005b1..ce26ba6 100644
--- a/src/adlmidi_opl3.cpp
+++ b/src/adlmidi_opl3.cpp
@@ -23,6 +23,10 @@
#include "adlmidi_private.hpp"
+#ifdef ADLMIDI_HW_OPL
+static const unsigned OPLBase = 0x388;
+#endif
+
#ifdef DISABLE_EMBEDDED_BANKS
/*
Dummy data which replaces adldata.cpp banks database
@@ -151,20 +155,38 @@ OPL3::OPL3() :
void OPL3::Poke(size_t card, uint32_t index, uint32_t value)
{
+ #ifdef ADLMIDI_HW_OPL
+ unsigned o = index >> 8;
+ unsigned port = OPLBase + o * 2;
+ outportb(port, index);
+ for(unsigned c = 0; c < 6; ++c) inportb(port);
+ outportb(port + 1, value);
+ for(unsigned c = 0; c < 35; ++c) inportb(port);
+ #else
#ifdef ADLMIDI_USE_DOSBOX_OPL
cards[card].WriteReg(index, static_cast<uint8_t>(value));
#else
OPL3_WriteReg(&cards[card], static_cast<Bit16u>(index), static_cast<Bit8u>(value));
#endif
+ #endif
}
void OPL3::PokeN(size_t card, uint16_t index, uint8_t value)
{
+ #ifdef ADLMIDI_HW_OPL
+ unsigned o = index >> 8;
+ unsigned port = OPLBase + o * 2;
+ outportb(port, index);
+ for(unsigned c = 0; c < 6; ++c) inportb(port);
+ outportb(port + 1, value);
+ for(unsigned c = 0; c < 35; ++c) inportb(port);
+ #else
#ifdef ADLMIDI_USE_DOSBOX_OPL
cards[card].WriteReg(static_cast<Bit32u>(index), value);
#else
OPL3_WriteReg(&cards[card], index, value);
#endif
+ #endif
}
@@ -427,11 +449,15 @@ void OPL3::Reset(unsigned long PCM_RATE)
_opl3_chip emptyChip;
std::memset(&emptyChip, 0, sizeof(_opl3_chip));
#endif
+ #ifndef ADLMIDI_HW_OPL
cards.clear();
+ #endif
ins.clear();
pit.clear();
regBD.clear();
+ #ifndef ADLMIDI_HW_OPL
cards.resize(NumCards, emptyChip);
+ #endif
NumChannels = NumCards * 23;
ins.resize(NumChannels, 189);
pit.resize(NumChannels, 0);
@@ -454,10 +480,12 @@ void OPL3::Reset(unsigned long PCM_RATE)
for(unsigned card = 0; card < NumCards; ++card)
{
- #ifdef ADLMIDI_USE_DOSBOX_OPL
+ #ifndef ADLMIDI_HW_OPL
+ # ifdef ADLMIDI_USE_DOSBOX_OPL
cards[card].Init(PCM_RATE);
- #else
+ # else
OPL3_Reset(&cards[card], static_cast<Bit32u>(PCM_RATE));
+ # endif
#endif
for(unsigned a = 0; a < 18; ++a) Poke(card, 0xB0 + Channels[a], 0x00);
diff --git a/src/adlmidi_private.cpp b/src/adlmidi_private.cpp
index 04bb481..f13aa8c 100644
--- a/src/adlmidi_private.cpp
+++ b/src/adlmidi_private.cpp
@@ -65,9 +65,9 @@ int adlRefreshNumCards(ADL_MIDIPlayer *device)
if(n_fourop[0] >= n_total[0] * 15 / 16 && play->m_setup.NumFourOps == 0)
{
- ADLMIDI_ErrorString = "ERROR: You have selected a bank that consists almost exclusively of four-op patches.\n"
+ play->setErrorString("ERROR: You have selected a bank that consists almost exclusively of four-op patches.\n"
" The results (silence + much cpu load) would be probably\n"
- " not what you want, therefore ignoring the request.\n";
+ " not what you want, therefore ignoring the request.\n");
return -1;
}
diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp
index 650291a..698dad5 100644
--- a/src/adlmidi_private.hpp
+++ b/src/adlmidi_private.hpp
@@ -24,29 +24,43 @@
#ifndef ADLMIDI_PRIVATE_HPP
#define ADLMIDI_PRIVATE_HPP
+#ifndef ADLMIDI_VERSION
+#define ADLMIDI_VERSION "1.3.0"
+#endif
+
// Setup compiler defines useful for exporting required public API symbols in gme.cpp
#ifndef ADLMIDI_EXPORT
- #if defined (_WIN32) && defined(ADLMIDI_BUILD_DLL)
- #define ADLMIDI_EXPORT __declspec(dllexport)
- #elif defined (LIBADLMIDI_VISIBILITY)
- #define ADLMIDI_EXPORT __attribute__((visibility ("default")))
- #else
- #define ADLMIDI_EXPORT
- #endif
+# if defined (_WIN32) && defined(ADLMIDI_BUILD_DLL)
+# define ADLMIDI_EXPORT __declspec(dllexport)
+# elif defined (LIBADLMIDI_VISIBILITY)
+# define ADLMIDI_EXPORT __attribute__((visibility ("default")))
+# else
+# define ADLMIDI_EXPORT
+# endif
#endif
#ifdef _WIN32
- #undef NO_OLDNAMES
-
- #ifdef _MSC_VER
- #ifdef _WIN64
- typedef __int64 ssize_t;
- #else
- typedef __int32 ssize_t;
- #endif
- #define NOMINMAX //Don't override std::min and std::max
- #endif
- #include <windows.h>
+# undef NO_OLDNAMES
+
+# ifdef _MSC_VER
+# ifdef _WIN64
+typedef __int64 ssize_t;
+# else
+typedef __int32 ssize_t;
+# endif
+# define NOMINMAX //Don't override std::min and std::max
+# endif
+# include <windows.h>
+#endif
+
+#ifdef __DJGPP__
+#define ADLMIDI_HW_OPL
+#include <conio.h>
+#include <pc.h>
+#include <dpmi.h>
+#include <go32.h>
+#include <sys/farptr.h>
+#include <dos.h>
#endif
#include <vector>
@@ -72,15 +86,19 @@
#include <deque>
#include <algorithm>
-#include "fraction.h"
+#include "fraction.hpp"
+
#ifdef ADLMIDI_USE_DOSBOX_OPL
- #include "dbopl.h"
+#include "dbopl.h"
#else
- #include "nukedopl3.h"
+#include "nukedopl3.h"
#endif
#include "adldata.hh"
-#include "adlmidi.h"
+#include "adlmidi.h" //Main API
+#ifndef ADLMIDI_DISABLE_CPP_EXTRAS
+#include "adlmidi.hpp" //Extra C++ API
+#endif
#define ADL_UNUSED(x) (void)x
@@ -93,7 +111,7 @@ extern std::string ADLMIDI_ErrorString;
template<class PTR>
class AdlMIDI_CPtr
{
- PTR* m_p;
+ PTR *m_p;
public:
AdlMIDI_CPtr() : m_p(NULL) {}
~AdlMIDI_CPtr()
@@ -108,9 +126,18 @@ public:
m_p = p;
}
- PTR* get() { return m_p;}
- PTR& operator*() { return *m_p; }
- PTR* operator->() { return m_p; }
+ PTR *get()
+ {
+ return m_p;
+ }
+ PTR &operator*()
+ {
+ return *m_p;
+ }
+ PTR *operator->()
+ {
+ return m_p;
+ }
};
class MIDIplay;
@@ -120,11 +147,13 @@ struct OPL3
uint32_t NumChannels;
char ____padding[4];
ADL_MIDIPlayer *_parent;
+ #ifndef ADLMIDI_HW_OPL
#ifdef ADLMIDI_USE_DOSBOX_OPL
std::vector<DBOPL::Handler> cards;
#else
std::vector<_opl3_chip> cards;
#endif
+ #endif
private:
std::vector<size_t> ins; // index to adl[], cached, needed by Touch()
std::vector<uint8_t> pit; // value poked to B0, cached, needed by NoteOff)(
@@ -134,7 +163,7 @@ private:
std::vector<adlinsdata> dynamic_metainstruments; // Replaces adlins[] when CMF file
std::vector<adldata> dynamic_instruments; // Replaces adl[] when CMF file
const unsigned DynamicInstrumentTag /* = 0x8000u*/,
- DynamicMetaInstrumentTag /* = 0x4000000u*/;
+ DynamicMetaInstrumentTag /* = 0x4000000u*/;
public:
const adlinsdata &GetAdlMetaIns(unsigned n);
unsigned GetAdlMetaNumber(unsigned midiins);
@@ -540,9 +569,9 @@ public:
ST_ENDTRACK = 0x2F,//size == 0
ST_TEMPOCHANGE = 0x51,//size == 3
ST_SMPTEOFFSET = 0x54,//size == 5
- ST_TIMESIGNATURE= 0x55,//size == 4
+ ST_TIMESIGNATURE = 0x55, //size == 4
ST_KEYSIGNATURE = 0x59,//size == 2
- ST_SEQUENCERSPEC= 0x7F,//size == len
+ ST_SEQUENCERSPEC = 0x7F, //size == len
/* Non-standard, internal ADLMIDI usage only */
ST_LOOPSTART = 0xE1,//size == 0 <CUSTOM>
@@ -690,6 +719,8 @@ private:
double loopEndTime;
//! Local error string
std::string errorString;
+ //! Local error string
+ std::string errorStringOut;
//! Pre-processed track data storage
std::vector<MidiTrackQueue > trackDataNew;
@@ -714,6 +745,9 @@ private:
public:
+ const std::string &getErrorString();
+ void setErrorString(const std::string &err);
+
std::string musTitle;
std::string musCopyright;
std::vector<std::string> musTrackTitles;
diff --git a/src/adlmidi_xmi2mid.h b/src/adlmidi_xmi2mid.h
index 523e8d7..950d58c 100644
--- a/src/adlmidi_xmi2mid.h
+++ b/src/adlmidi_xmi2mid.h
@@ -27,6 +27,15 @@
#include <stdint.h>
+#ifdef __DJGPP__
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef signed short int16_t;
+typedef unsigned short uint16_t;
+typedef signed long int32_t;
+typedef unsigned long uint32_t;
+#endif
+
#ifdef __cplusplus
extern "C"
{
diff --git a/src/fraction.h b/src/fraction.hpp
index 1ec10ab..1ec10ab 100644
--- a/src/fraction.h
+++ b/src/fraction.hpp
diff --git a/utils/adlmidi-2/adlmidi-2.pro b/utils/adlmidi-2/adlmidi-2.pro
index 2b797ff..f4214c1 100644
--- a/utils/adlmidi-2/adlmidi-2.pro
+++ b/utils/adlmidi-2/adlmidi-2.pro
@@ -23,13 +23,13 @@ linux-*: {
#DEFINES += DISABLE_EMBEDDED_BANKS
#DEFINES += ADLMIDI_USE_DOSBOX_OPL
#DEFINES += ENABLE_BEGIN_SILENCE_SKIPPING
-#DEFINES += DEBUG_TRACE_ALL_EVENTS
QMAKE_CFLAGS += -std=c90 -pedantic
QMAKE_CXXFLAGS += -std=c++98 -pedantic
HEADERS += \
$$PWD/../../include/adlmidi.h \
+ $$PWD/../../include/adlmidi.hpp \
$$PWD/../../src/adlbank.h \
$$PWD/../../src/adldata.hh \
$$PWD/../../src/adlmidi_mus2mid.h \
@@ -56,8 +56,7 @@ SOURCES += \
$$PWD/../../src/adlmidi_xmi2mid.c \
$$PWD/../../src/nukedopl3.c \
$$PWD/../../src/dbopl.cpp \
- #$$PWD/../../utils/midiplay/adlmidiplay.cpp \
- #$$PWD/../../utils/midiplay/wave_writer.c \
+ \
midiplay.cc
diff --git a/utils/adlmidi-2/midiplay.cc b/utils/adlmidi-2/midiplay.cc
index bd7c9d6..8ede11b 100644
--- a/utils/adlmidi-2/midiplay.cc
+++ b/utils/adlmidi-2/midiplay.cc
@@ -8,7 +8,7 @@
#include <set>
#include <cstdlib>
#include <cstring>
-#include <chrono>
+#include <ctime>
#include <cstdarg>
#include <cmath>
#include <unistd.h>
@@ -21,7 +21,7 @@
#include <assert.h>
#define SUPPORT_VIDEO_OUTPUT
-//#define SUPPORT_PUZZLE_GAME
+#define SUPPORT_PUZZLE_GAME
#ifdef __WIN32__
# include <cctype>
@@ -35,7 +35,10 @@ typedef unsigned char Uint8;
typedef unsigned short Uint16;
typedef unsigned Uint32;
#else
+
+#define SDL_MAIN_HANDLED
#include <SDL2/SDL.h>
+
class MutexType
{
SDL_mutex *mut;
@@ -57,13 +60,14 @@ public:
#endif
#ifdef __DJGPP__
-# include <conio.h>
-# include <pc.h>
-# include <dpmi.h>
-# include <go32.h>
-# include <sys/farptr.h>
-# include <dos.h>
-# define BIOStimer _farpeekl(_dos_ds, 0x46C)
+#include <conio.h>
+#include <pc.h>
+#include <dpmi.h>
+#include <go32.h>
+#include <sys/farptr.h>
+#include <dos.h>
+#include <stdlib.h>
+#define BIOStimer _farpeekl(_dos_ds, 0x46C)
static const unsigned NewTimerFreq = 209;
#elif !defined(__WIN32__) || defined(__CYGWIN__)
# include <termios.h>
@@ -77,16 +81,10 @@ static const unsigned NewTimerFreq = 209;
#include <signal.h>
-
#include "adlmidi.h"
-#include "adlmidi_private.hpp" // For OPL3 and MIDIplay classes
-
-#include "fraction.h"
+#include "adlmidi.hpp"
#ifndef __DJGPP__
-#include "dbopl.h"
-
-#include "adldata.hh"
static const unsigned long PCM_RATE = 48000;
static const unsigned MaxCards = 100;
@@ -136,7 +134,7 @@ static void SigWinchHandler(int) {}
static void GuessInitialWindowHeight()
{
- auto s = std::getenv("LINES");
+ char *s = std::getenv("LINES");
if(s && std::atoi(s)) WindowLines = (unsigned)std::atoi(s);
SigWinchHandler(0);
}
@@ -438,9 +436,10 @@ public:
VidPut(c);
++x; // One letter drawn. Update cursor position.
}
- #ifdef __DJGPP__
-#define RawPrn cprintf
- #else
+
+#ifdef __DJGPP__
+# define RawPrn cprintf
+#else
static void RawPrn(const char *fmt, ...) __attribute__((format(printf, 1, 2)))
{
// Note: should essentially match PutC, except without updates to x
@@ -449,7 +448,8 @@ public:
vfprintf(stderr, fmt, ap);
va_end(ap);
}
- #endif
+#endif
+
int Print(unsigned beginx, unsigned color, bool ln, const char *fmt, va_list ap)
{
char Line[1024];
@@ -1270,192 +1270,194 @@ static void SendStereoAudio(unsigned long count, short *samples)
/*
* THIS CLASS USES !!!ADL PRIVATE!!!
*/
-class AdlInstrumentTester
-{
- uint32_t cur_gm;
- uint32_t ins_idx;
- std::vector<uint32_t> adl_ins_list;
- OPL3 &opl;
-
-public:
- AdlInstrumentTester(OPL3 &o)
- : opl(o)
- {
- cur_gm = 0;
- ins_idx = 0;
- }
-
- ~AdlInstrumentTester()
- {}
-
- // Find list of adlib instruments that supposedly implement this GM
- void FindAdlList()
- {
- const unsigned NumBanks = (unsigned)adl_getBanksCount();
- std::set<unsigned> adl_ins_set;
- for(unsigned bankno = 0; bankno < NumBanks; ++bankno)
- adl_ins_set.insert(banks[bankno][cur_gm]);
- adl_ins_list.assign(adl_ins_set.begin(), adl_ins_set.end());
- ins_idx = 0;
- NextAdl(0);
- opl.Silence();
- }
-
-
- void Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127
- {
- if(opl.LogarithmicVolumes)// !!!ADL PRIVATE!!!
- opl.Touch_Real(c, volume * 127 / (127 * 127 * 127) / 2);// !!!ADL PRIVATE!!!
- else
- {
- // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A)
- opl.Touch_Real(c, volume > 8725 ? static_cast<unsigned int>(std::log(volume) * 11.541561 + (0.5 - 104.22845)) : 0);// !!!ADL PRIVATE!!!
- // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A)
- //Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0);
- }
- }
-
- void DoNote(int note)
- {
- if(adl_ins_list.empty()) FindAdlList();
- const unsigned meta = adl_ins_list[ins_idx];
- const adlinsdata &ains = opl.GetAdlMetaIns(meta);// !!!ADL PRIVATE!!!
-
- int tone = (cur_gm & 128) ? (cur_gm & 127) : (note + 50);
- if(ains.tone)
- {
- /*if(ains.tone < 20)
- tone += ains.tone;
- else */
- if(ains.tone < 128)
- tone = ains.tone;
- else
- tone -= ains.tone - 128;
- }
- double hertz = 172.00093 * std::exp(0.057762265 * (tone + 0.0));
- int i[2] = { ains.adlno1, ains.adlno2 };
- int32_t adlchannel[2] = { 0, 3 };
- if(i[0] == i[1])
- {
- adlchannel[1] = -1;
- adlchannel[0] = 6; // single-op
- std::printf("noteon at %d(%d) for %g Hz\n",
- adlchannel[0], i[0], hertz);
- }
- else
- {
- std::printf("noteon at %d(%d) and %d(%d) for %g Hz\n",
- adlchannel[0], i[0], adlchannel[1], i[1], hertz);
- }
-
- opl.NoteOff(0);
- opl.NoteOff(3);
- opl.NoteOff(6);
- for(unsigned c = 0; c < 2; ++c)
- {
- if(adlchannel[c] < 0) continue;
- opl.Patch((uint16_t)adlchannel[c], (uint16_t)i[c]);
- opl.Touch_Real((uint16_t)adlchannel[c], 127 * 127 * 100);
- opl.Pan((uint16_t)adlchannel[c], 0x30);
- opl.NoteOn((uint16_t)adlchannel[c], hertz);
- }
- }
-
- void NextGM(int offset)
- {
- cur_gm = (cur_gm + 256 + (uint32_t)offset) & 0xFF;
- FindAdlList();
- }
-
- void NextAdl(int offset)
- {
- if(adl_ins_list.empty()) FindAdlList();
- const unsigned NumBanks = (unsigned)adl_getBanksCount();
- ins_idx = (uint32_t)((int32_t)ins_idx + (int32_t)adl_ins_list.size() + offset) % adl_ins_list.size();
-
- UI.Color(15);
- std::fflush(stderr);
- std::printf("SELECTED G%c%d\t%s\n",
- cur_gm < 128 ? 'M' : 'P', cur_gm < 128 ? cur_gm + 1 : cur_gm - 128,
- "<-> select GM, ^v select ins, qwe play note");
- std::fflush(stdout);
- UI.Color(7);
- std::fflush(stderr);
- for(unsigned a = 0; a < adl_ins_list.size(); ++a)
- {
- const unsigned i = adl_ins_list[a];
- const adlinsdata &ains = opl.GetAdlMetaIns(i);
-
- char ToneIndication[8] = " ";
- if(ains.tone)
- {
- if(ains.tone < 20)
- sprintf(ToneIndication, "+%-2d", ains.tone);
- else if(ains.tone < 128)
- sprintf(ToneIndication, "=%-2d", ains.tone);
- else
- sprintf(ToneIndication, "-%-2d", ains.tone - 128);
- }
- std::printf("%s%s%s%u\t",
- ToneIndication,
- ains.adlno1 != ains.adlno2 ? "[2]" : " ",
- (ins_idx == a) ? "->" : "\t",
- i
- );
-
- for(unsigned bankno = 0; bankno < NumBanks; ++bankno)
- if(banks[bankno][cur_gm] == i)
- std::printf(" %u", bankno);
-
- std::printf("\n");
- }
- }
-
- void HandleInputChar(char ch)
- {
- static const char notes[] = "zsxdcvgbhnjmq2w3er5t6y7ui9o0p";
- // c'd'ef'g'a'bC'D'EF'G'A'Bc'd'e
- switch(ch)
- {
- case '/':
- case 'H':
- case 'A':
- NextAdl(-1);
- break;
- case '*':
- case 'P':
- case 'B':
- NextAdl(+1);
- break;
- case '-':
- case 'K':
- case 'D':
- NextGM(-1);
- break;
- case '+':
- case 'M':
- case 'C':
- NextGM(+1);
- break;
- case 3:
- #if !((!defined(__WIN32__) || defined(__CYGWIN__)) && !defined(__DJGPP__))
- case 27:
- #endif
- QuitFlag = true;
- break;
- default:
- const char *p = strchr(notes, ch);
- if(p && *p) DoNote((p - notes) - 12);
- }
- }
- double Tick(double /*eat_delay*/, double /*mindelay*/)
- {
- HandleInputChar(Input.PeekInput());
- //return eat_delay;
- return 0.1;
- }
-};
+//class OPL3;
+//class AdlInstrumentTester
+//{
+// uint32_t cur_gm;
+// uint32_t ins_idx;
+// std::vector<uint32_t> adl_ins_list;
+// OPL3 *opl;
+// MIDIplay * play;
+
+//public:
+// AdlInstrumentTester(ADL_MIDIPlayer *device)
+// {
+// cur_gm = 0;
+// ins_idx = 0;
+// play = reinterpret_cast<MIDIplay*>(device->adl_midiPlayer);
+// if(!play)
+// return;
+// opl = &play->opl;
+// }
+
+// ~AdlInstrumentTester()
+// {}
+
+// // Find list of adlib instruments that supposedly implement this GM
+// void FindAdlList()
+// {
+// const unsigned NumBanks = (unsigned)adl_getBanksCount();
+// std::set<unsigned> adl_ins_set;
+// for(unsigned bankno = 0; bankno < NumBanks; ++bankno)
+// adl_ins_set.insert(banks[bankno][cur_gm]);
+// adl_ins_list.assign(adl_ins_set.begin(), adl_ins_set.end());
+// ins_idx = 0;
+// NextAdl(0);
+// opl->Silence();
+// }
+
+
+// void Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127
+// {
+// if(opl->LogarithmicVolumes)// !!!ADL PRIVATE!!!
+// opl->Touch_Real(c, volume * 127 / (127 * 127 * 127) / 2);// !!!ADL PRIVATE!!!
+// else
+// {
+// // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A)
+// opl->Touch_Real(c, volume > 8725 ? static_cast<unsigned int>(std::log(volume) * 11.541561 + (0.5 - 104.22845)) : 0);// !!!ADL PRIVATE!!!
+// // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A)
+// //Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0);
+// }
+// }
+
+// void DoNote(int note)
+// {
+// if(adl_ins_list.empty()) FindAdlList();
+// const unsigned meta = adl_ins_list[ins_idx];
+// const adlinsdata &ains = opl->GetAdlMetaIns(meta);// !!!ADL PRIVATE!!!
+
+// int tone = (cur_gm & 128) ? (cur_gm & 127) : (note + 50);
+// if(ains.tone)
+// {
+// /*if(ains.tone < 20)
+// tone += ains.tone;
+// else */
+// if(ains.tone < 128)
+// tone = ains.tone;
+// else
+// tone -= ains.tone - 128;
+// }
+// double hertz = 172.00093 * std::exp(0.057762265 * (tone + 0.0));
+// int i[2] = { ains.adlno1, ains.adlno2 };
+// int32_t adlchannel[2] = { 0, 3 };
+// if(i[0] == i[1])
+// {
+// adlchannel[1] = -1;
+// adlchannel[0] = 6; // single-op
+// std::printf("noteon at %d(%d) for %g Hz\n",
+// adlchannel[0], i[0], hertz);
+// }
+// else
+// {
+// std::printf("noteon at %d(%d) and %d(%d) for %g Hz\n",
+// adlchannel[0], i[0], adlchannel[1], i[1], hertz);
+// }
+
+// opl->NoteOff(0);
+// opl->NoteOff(3);
+// opl->NoteOff(6);
+// for(unsigned c = 0; c < 2; ++c)
+// {
+// if(adlchannel[c] < 0) continue;
+// opl->Patch((uint16_t)adlchannel[c], (uint16_t)i[c]);
+// opl->Touch_Real((uint16_t)adlchannel[c], 127 * 127 * 100);
+// opl->Pan((uint16_t)adlchannel[c], 0x30);
+// opl->NoteOn((uint16_t)adlchannel[c], hertz);
+// }
+// }
+
+// void NextGM(int offset)
+// {
+// cur_gm = (cur_gm + 256 + (uint32_t)offset) & 0xFF;
+// FindAdlList();
+// }
+
+// void NextAdl(int offset)
+// {
+// if(adl_ins_list.empty()) FindAdlList();
+// const unsigned NumBanks = (unsigned)adl_getBanksCount();
+// ins_idx = (uint32_t)((int32_t)ins_idx + (int32_t)adl_ins_list.size() + offset) % adl_ins_list.size();
+
+// UI.Color(15);
+// std::fflush(stderr);
+// std::printf("SELECTED G%c%d\t%s\n",
+// cur_gm < 128 ? 'M' : 'P', cur_gm < 128 ? cur_gm + 1 : cur_gm - 128,
+// "<-> select GM, ^v select ins, qwe play note");
+// std::fflush(stdout);
+// UI.Color(7);
+// std::fflush(stderr);
+// for(unsigned a = 0; a < adl_ins_list.size(); ++a)
+// {
+// const unsigned i = adl_ins_list[a];
+// const adlinsdata &ains = opl->GetAdlMetaIns(i);
+
+// char ToneIndication[8] = " ";
+// if(ains.tone)
+// {
+// /*if(ains.tone < 20)
+// std::sprintf(ToneIndication, "+%-2d", ains.tone);
+// else*/
+// if(ains.tone < 128)
+// std::sprintf(ToneIndication, "=%-2d", ains.tone);
+// else
+// std::sprintf(ToneIndication, "-%-2d", ains.tone - 128);
+// }
+// std::printf("%s%s%s%u\t",
+// ToneIndication,
+// ains.adlno1 != ains.adlno2 ? "[2]" : " ",
+// (ins_idx == a) ? "->" : "\t",
+// i
+// );
+
+// for(unsigned bankno = 0; bankno < NumBanks; ++bankno)
+// if(banks[bankno][cur_gm] == i)
+// std::printf(" %u", bankno);
+
+// std::printf("\n");
+// }
+// }
+
+// bool HandleInputChar(char ch)
+// {
+// static const char notes[] = "zsxdcvgbhnjmq2w3er5t6y7ui9o0p";
+// // c'd'ef'g'a'bC'D'EF'G'A'Bc'd'e
+// switch(ch)
+// {
+// case '/':
+// case 'H':
+// case 'A':
+// NextAdl(-1);
+// break;
+// case '*':
+// case 'P':
+// case 'B':
+// NextAdl(+1);
+// break;
+// case '-':
+// case 'K':
+// case 'D':
+// NextGM(-1);
+// break;
+// case '+':
+// case 'M':
+// case 'C':
+// NextGM(+1);
+// break;
+// case 3:
+// #if !((!defined(__WIN32__) || defined(__CYGWIN__)) && !defined(__DJGPP__))
+// case 27:
+// #endif
+// return false;
+// break;
+// default:
+// const char *p = strchr(notes, ch);
+// if(p && *p)
+// DoNote((p - notes) - 12);
+// }
+// return true;
+// }
+//};
static void TidyupAndExit(int)
{
@@ -1529,17 +1531,17 @@ static int ParseCommandLine(char *cmdline, char **argv)
return(argc);
}
+extern int main(int argc, char **argv);
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
{
- extern int main(int, char **);
+ //extern int main(int, char **);
char *cmdline = GetCommandLine();
int argc = ParseCommandLine(cmdline, NULL);
char **argv = new char *[argc + 1];
ParseCommandLine(cmdline, argv);
-#else
-#undef main
-
-
+ return main(argc, argv);
+}
+#endif
static void adlEventHook(void *ui, ADL_UInt8 type, ADL_UInt8 subtype, ADL_UInt8 /*channel*/, ADL_UInt8 *data, size_t len)
{
@@ -1577,7 +1579,6 @@ static void adlDebugMsgHook(void *userdata, const char *fmt, ...)
int main(int argc, char **argv)
{
-#endif
// How long is SDL buffer, in seconds?
// The smaller the value, the more often AdlAudioCallBack()
// is called.
@@ -1648,10 +1649,16 @@ int main(int argc, char **argv)
return 0;
}
+#ifndef __DJGPP__
std::srand((unsigned int)std::time(0));
+#endif
ADL_MIDIPlayer *myDevice;
+ #ifndef __DJGPP__
myDevice = adl_init(PCM_RATE);
+ #else
+ myDevice = adl_init(48000);
+ #endif
// Set hooks
adl_setNoteHook(myDevice, adlNoteHook, (void *)&UI);
@@ -1764,6 +1771,7 @@ int main(int argc, char **argv)
adl_setBank(myDevice, bankno);
}
+#if 0
unsigned n_fourop[2] = {0, 0}, n_total[2] = {0, 0};
for(unsigned a = 0; a < 256; ++a)
{
@@ -1778,6 +1786,7 @@ int main(int argc, char **argv)
UI.PrintLn("This bank has %u/%u four-op melodic instruments", n_fourop[0], n_total[0]);
UI.PrintLn(" and %u/%u percussive ones.", n_fourop[1], n_total[1]);
}
+#endif
if(argc >= 4)
{
@@ -1790,6 +1799,7 @@ int main(int argc, char **argv)
}
adl_setNumCards(myDevice, (int)NumCards);
}
+
if(argc >= 5)
{
NumFourOps = (unsigned)std::atoi(argv[4]);
@@ -1800,14 +1810,21 @@ int main(int argc, char **argv)
UI.ShowCursor();
return 0;
}
- adl_setNumFourOpsChn(myDevice, (int)NumFourOps);
+ if(adl_setNumFourOpsChn(myDevice, (int)NumFourOps) < 0)
+ {
+ UI.ShowCursor();
+ std::fprintf(stderr, "%s\n", adl_errorInfo(myDevice));
+ return 0;
+ }
}
+ /*
else
NumFourOps =
DoingInstrumentTesting ? 2
: (n_fourop[0] >= n_total[0] * 7 / 8) ? NumCards * 6
: (n_fourop[0] < n_total[0] * 1 / 8) ? 0
: (NumCards == 1 ? 1 : NumCards * 4);
+ */
if(WritingToTTY)
{
UI.PrintLn("Simulating %u OPL3 cards for a total of %u operators.", NumCards, NumCards * 36);
@@ -1825,19 +1842,11 @@ int main(int argc, char **argv)
UI.Color(7);
if(adl_openFile(myDevice, argv[1]) != 0)
{
+ std::fprintf(stderr, "%s\n", adl_errorInfo(myDevice));
UI.ShowCursor();
return 2;
}
- if(n_fourop[0] >= n_total[0] * 15 / 16 && NumFourOps == 0)
- {
- std::fprintf(stderr,
- "ERROR: You have selected a bank that consists almost exclusively of four-op patches.\n"
- " The results (silence + much cpu load) would be probably\n"
- " not what you want, therefore ignoring the request.\n");
- return 0;
- }
-
#ifdef __DJGPP__
unsigned TimerPeriod = 0x1234DDul / NewTimerFreq;
@@ -1850,8 +1859,7 @@ int main(int argc, char **argv)
#else
- const double mindelay = 1 / (double)PCM_RATE;
- const double maxdelay = MaxSamplesAtTime / (double)PCM_RATE;
+ //const double maxdelay = MaxSamplesAtTime / (double)PCM_RATE;
#ifdef __WIN32
WindowsAudio::Open(PCM_RATE, 2, 16);
@@ -1861,20 +1869,19 @@ int main(int argc, char **argv)
#endif /* djgpp */
- // !!!ADL PRIVATE!!!
- AdlInstrumentTester InstrumentTester(((MIDIplay *)myDevice->adl_midiPlayer)->opl);
+ AdlInstrumentTester InstrumentTester(myDevice);
//static std::vector<int> sample_buf;
- double delay = 0.0;
+ //double delay = 0.0;
//sample_buf.resize(1024);
short buff[1024];
UI.TetrisLaunched = true;
while(!QuitFlag)
{
- #ifndef __DJGPP__
- const double eat_delay = delay < maxdelay ? delay : maxdelay;
- delay -= eat_delay;
+#ifndef __DJGPP__
+ //const double eat_delay = delay < maxdelay ? delay : maxdelay;
+ //delay -= eat_delay;
size_t got = 0;
if(!DoingInstrumentTesting)
@@ -1950,7 +1957,7 @@ int main(int argc, char **argv)
#endif
//fprintf(stderr, "Exit: %u\n", (unsigned)AudioBuffer.size());
//}
- #else /* DJGPP */
+#else /* DJGPP */
UI.IllustrateVolumes(0, 0);
const double mindelay = 1.0 / NewTimerFreq;
@@ -1962,11 +1969,18 @@ int main(int argc, char **argv)
const unsigned long CurTimer = BIOStimer;
const double eat_delay = (CurTimer - PrevTimer) / (double)NewTimerFreq;
PrevTimer = CurTimer;
- #endif
+#endif
//double nextdelay =
if(DoingInstrumentTesting)
- InstrumentTester.Tick(eat_delay, mindelay);
+ {
+ if(!InstrumentTester.HandleInputChar(Input.PeekInput()))
+ QuitFlag = true;
+ #ifdef __DJGPP__
+ else
+ delay = adl_tickEvents(myDevice, eat_delay, mindelay);
+ #endif
+ }
//: player.Tick(eat_delay, mindelay);
UI.GotoXY(0, 0);