diff options
-rw-r--r-- | CMakeLists.txt | 57 | ||||
-rw-r--r-- | README.md | 6 | ||||
-rw-r--r-- | include/adlmidi.h | 20 | ||||
-rw-r--r-- | include/adlmidi.hpp | 57 | ||||
-rw-r--r-- | src/adlmidi.cpp | 91 | ||||
-rw-r--r-- | src/adlmidi_load.cpp | 42 | ||||
-rw-r--r-- | src/adlmidi_midiplay.cpp | 204 | ||||
-rw-r--r-- | src/adlmidi_mus2mid.h | 9 | ||||
-rw-r--r-- | src/adlmidi_opl3.cpp | 32 | ||||
-rw-r--r-- | src/adlmidi_private.cpp | 4 | ||||
-rw-r--r-- | src/adlmidi_private.hpp | 92 | ||||
-rw-r--r-- | src/adlmidi_xmi2mid.h | 9 | ||||
-rw-r--r-- | src/fraction.hpp (renamed from src/fraction.h) | 0 | ||||
-rw-r--r-- | utils/adlmidi-2/adlmidi-2.pro | 5 | ||||
-rw-r--r-- | utils/adlmidi-2/midiplay.cc | 480 |
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}") @@ -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); |