Skip to content

Commit

Permalink
Updated libADLMIDI
Browse files Browse the repository at this point in the history
  • Loading branch information
Wohlstand committed Dec 31, 2024
1 parent d21a5af commit a241664
Show file tree
Hide file tree
Showing 12 changed files with 835 additions and 20 deletions.
14 changes: 13 additions & 1 deletion ADLMIDI-Player/src/main/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 3.2...3.5)
cmake_minimum_required (VERSION 3.2...3.10)
project (libADLMIDI VERSION 1.5.1 LANGUAGES C CXX)

include(GNUInstallDirs)
Expand Down Expand Up @@ -189,6 +189,7 @@ option(USE_DOSBOX_EMULATOR "Use DosBox 0.74 OPL3 emulator (semi-accurate, sugge
option(USE_NUKED_EMULATOR "Use Nuked OPL3 emulator (most accurate, powerful)" ${DEFAULT_HEAVY_EMULATORS})
option(USE_OPAL_EMULATOR "Use Opal emulator (inaccurate)" ${DEFAULT_HEAVY_EMULATORS})
option(USE_JAVA_EMULATOR "Use JavaOPL emulator" ${DEFAULT_HEAVY_EMULATORS})
option(USE_HW_SERIAL "Use the hardware OPL3 chip via Serial on modern systems" OFF)

option(WITH_GENADLDATA "Build and run full rebuild of embedded banks cache" OFF)
option(WITH_GENADLDATA_COMMENTS "Enable comments in a generated embedded instruments cache file" OFF)
Expand Down Expand Up @@ -286,6 +287,15 @@ function(handle_options targetLib)
target_compile_definitions(${targetLib} PUBLIC ADLMIDI_DISABLE_JAVA_EMULATOR)
endif()

if(USE_HW_SERIAL)
set(HAS_EMULATOR TRUE)
target_sources(${targetLib} PRIVATE
${libADLMIDI_SOURCE_DIR}/src/chips/opl_serial_port.cpp
)
target_compile_definitions(${targetLib} PRIVATE ENABLE_HW_OPL_SERIAL_PORT)
target_compile_definitions(${targetLib} PUBLIC ADLMIDI_ENABLE_HW_SERIAL)
endif()

if(NOT HAS_EMULATOR)
message(FATAL_ERROR "No emulators enabled! You must enable at least one emulator!")
endif()
Expand Down Expand Up @@ -414,6 +424,7 @@ function(libADLMIDI_find_SDL2)
add_library(ADLMIDI_SDL2 INTERFACE)
if(TARGET SDL2::SDL2)
target_link_libraries(ADLMIDI_SDL2 INTERFACE SDL2::SDL2)
target_include_directories(ADLMIDI_SDL2 INTERFACE ${SDL2_INCLUDE_DIRS})
else()
string(STRIP ${SDL2_LIBRARIES} SDL2_LIBRARIES)
target_include_directories(ADLMIDI_SDL2 INTERFACE ${SDL2_INCLUDE_DIRS})
Expand Down Expand Up @@ -547,6 +558,7 @@ message("USE_DOSBOX_EMULATOR = ${USE_DOSBOX_EMULATOR}")
message("USE_NUKED_EMULATOR = ${USE_NUKED_EMULATOR}")
message("USE_OPAL_EMULATOR = ${USE_OPAL_EMULATOR}")
message("USE_JAVA_EMULATOR = ${USE_JAVA_EMULATOR}")
message("USE_HW_SERIAL = ${USE_HW_SERIAL}")

message("===== Utils and extras =====")
message("WITH_GENADLDATA = ${WITH_GENADLDATA}")
Expand Down
57 changes: 57 additions & 0 deletions ADLMIDI-Player/src/main/cpp/include/adlmidi.h
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,30 @@ typedef struct {
*/
extern ADLMIDI_DECLSPEC int adl_setRunAtPcmRate(struct ADL_MIDIPlayer *device, int enabled);

/**
* @brief The list of serial port protocols
*/
enum ADL_SerialProtocol
{
ADLMIDI_SerialProtocol_Unknown = 0,
ADLMIDI_SerialProtocol_ArduinoOPL2,
ADLMIDI_SerialProtocol_NukeYktOPL3,
ADLMIDI_SerialProtocol_RetroWaveOPL3,
ADLMIDI_SerialProtocol_END
};

/**
* @brief Switch the synthesizer into hardware mode using Serial port
* @param name The name of the serial port device (it may look different on various platforms. On UNIX-like systems don't type the /dev/ prefix: only name).
* @param baud The baud speed of the serial port
* @param protocol The binary protocol used to communicate the device (#ADL_SerialProtocol)
* @return 0 on success, <0 when any error has occurred
*/
extern ADLMIDI_DECLSPEC int adl_switchSerialHW(struct ADL_MIDIPlayer *device,
const char *name,
unsigned baud,
unsigned protocol);

/**
* @brief Set 4-bit device identifier. Used by the SysEx processor.
* @param device Instance of the library
Expand Down Expand Up @@ -1152,6 +1176,39 @@ extern ADLMIDI_DECLSPEC int adl_generateFormat(struct ADL_MIDIPlayer *device, i
*/
extern ADLMIDI_DECLSPEC double adl_tickEvents(struct ADL_MIDIPlayer *device, double seconds, double granulality);

/**
* @brief Periodic tick handler without iterators.
*
* Unlike adl_tickEvents(), it doesn't handles iterators, you need to perform
* them naually via adl_tickIterators().
*
* Notice: The function is provided to use it with Hardware OPL3 mode or for the purpose to iterate
* MIDI playback without of sound generation.
*
* DON'T USE IT TOGETHER WITH adl_play() and adl_playFormat() calls
* as there are all using this function internally!!!
*
* @param device Instance of the library
* @param seconds Previous delay. On a first moment, pass the `0.0`
* @param granulality Minimal size of one MIDI tick in seconds.
* @return desired number of seconds until next call. Pass this value into `seconds` field in next time
*/
extern ADLMIDI_DECLSPEC double adl_tickEventsOnly(struct ADL_MIDIPlayer *device, double seconds, double granulality);


/**
* @brief Periodic tick hander for the real-time hardware output
*
* This function runs a single step of vibrato, auto-arpeggio, and the portamento of @seconds duration.
*
* When running the libADLMIDI as a real-time driver for the ral hardware, call
* this function from the timer and specify the @seconds value with a delay of the single cycle.
*
* @param device Instance of the library
* @param seconds Previous delay. On a first moment, pass the `0.0`
*/
extern ADLMIDI_DECLSPEC void adl_tickIterators(struct ADL_MIDIPlayer *device, double seconds);




Expand Down
70 changes: 69 additions & 1 deletion ADLMIDI-Player/src/main/cpp/src/adlmidi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,20 @@ ADLMIDI_EXPORT int adl_setNumChips(ADL_MIDIPlayer *device, int numChips)

MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);

#ifdef ADLMIDI_ENABLE_HW_SERIAL
if(play->m_setup.serial)
numChips = 1;
#endif

#ifdef ADLMIDI_HW_OPL
ADL_UNUSED(numChips);
play->m_setup.numChips = 1;
#else
play->m_setup.numChips = static_cast<unsigned int>(numChips);
#endif
if(play->m_setup.numChips < 1 || play->m_setup.numChips > ADL_MAX_CHIPS)

if((play->m_setup.numChips < 1) || (play->m_setup.numChips > ADL_MAX_CHIPS))
{
play->setErrorString("number of chips may only be 1.." ADL_MAX_CHIPS_STR ".\n");
return -1;
Expand Down Expand Up @@ -618,6 +625,10 @@ ADLMIDI_EXPORT void adl_setSoftPanEnabled(ADL_MIDIPlayer *device, int softPanEn)
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_synth->m_softPanning = (softPanEn != 0);
#ifdef ADLMIDI_ENABLE_HW_SERIAL
if(play->m_setup.serial) // Soft-panning won't work while serial is active
play->m_synth->m_softPanning = false;
#endif
}

/* !!!DEPRECATED!!! */
Expand Down Expand Up @@ -846,6 +857,9 @@ ADLMIDI_EXPORT int adl_switchEmulator(struct ADL_MIDIPlayer *device, int emulato
if(adl_isEmulatorAvailable(emulator))
{
play->m_setup.emulator = emulator;
#ifdef ADLMIDI_ENABLE_HW_SERIAL
play->m_setup.serial = false;
#endif
play->partialReset();
return 0;
}
Expand All @@ -870,6 +884,31 @@ ADLMIDI_EXPORT int adl_setRunAtPcmRate(ADL_MIDIPlayer *device, int enabled)
return -1;
}

ADLMIDI_EXPORT int adl_switchSerialHW(struct ADL_MIDIPlayer *device,
const char *name,
unsigned baud,
unsigned protocol)
{
if(device)
{
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
#ifdef ADLMIDI_ENABLE_HW_SERIAL
play->m_setup.serial = true;
play->m_setup.serialName = std::string(name);
play->m_setup.serialBaud = baud;
play->m_setup.serialProtocol = protocol;
play->partialReset();
return 0;
#else
(void)name; (void)baud; (void)protocol;
play->setErrorString("ADLMIDI: The hardware serial mode is not enabled in this build");
return -1;
#endif
}

return -1;
}

ADLMIDI_EXPORT const char *adl_linkedLibraryVersion()
{
Expand Down Expand Up @@ -1498,7 +1537,10 @@ ADLMIDI_EXPORT int adl_playFormat(ADL_MIDIPlayer *device, int sampleCount,
hasSkipped = setup.tick_skip_samples_delay > 0;
}
else
{
setup.delay = player->Tick(eat_delay, setup.mindelay);
player->TickIterators(eat_delay);
}
}

return static_cast<int>(gotten_len);
Expand Down Expand Up @@ -1588,6 +1630,24 @@ ADLMIDI_EXPORT int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleC

ADLMIDI_EXPORT double adl_tickEvents(struct ADL_MIDIPlayer *device, double seconds, double granulality)
{
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
if(!device)
return -1.0;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
double ret = play->Tick(seconds, granulality);
play->TickIterators(seconds);
return ret;
#else
ADL_UNUSED(device);
ADL_UNUSED(seconds);
ADL_UNUSED(granulality);
return -1.0;
#endif
}

ADLMIDI_EXPORT double adl_tickEventsOnly(struct ADL_MIDIPlayer *device, double seconds, double granulality)
{
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
if(!device)
return -1.0;
Expand All @@ -1602,6 +1662,14 @@ ADLMIDI_EXPORT double adl_tickEvents(struct ADL_MIDIPlayer *device, double secon
#endif
}

ADLMIDI_EXPORT void adl_tickIterators(struct ADL_MIDIPlayer *device, double seconds)
{
if(!device)
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
play->TickIterators(seconds);
}

ADLMIDI_EXPORT int adl_atEnd(struct ADL_MIDIPlayer *device)
{
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
Expand Down
2 changes: 1 addition & 1 deletion ADLMIDI-Player/src/main/cpp/src/adlmidi_load.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ bool MIDIplay::LoadMIDI_post()
resetMIDIDefaults();

m_setup.tick_skip_samples_delay = 0;
synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPL3 chip
chipReset(); // Reset OPL3 chip
//opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously)
m_chipChannels.clear();
m_chipChannels.resize(synth.m_numChannels);
Expand Down
30 changes: 28 additions & 2 deletions ADLMIDI-Player/src/main/cpp/src/adlmidi_midiplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ MIDIplay::MIDIplay(unsigned long sampleRate):
m_setup.emulator = adl_getLowestEmulator();
m_setup.runAtPcmRate = false;

#ifdef ADLMIDI_ENABLE_HW_SERIAL
m_setup.serial = false;
m_setup.serialName = std::string();
m_setup.serialBaud = 0;
m_setup.serialProtocol = 0;
#endif

m_setup.PCM_RATE = sampleRate;
m_setup.mindelay = 1.0 / (double)m_setup.PCM_RATE;
m_setup.maxdelay = 512.0 / (double)m_setup.PCM_RATE;
Expand Down Expand Up @@ -155,7 +162,7 @@ void MIDIplay::applySetup()
else
adlCalculateFourOpChannels(this, true);

synth.reset(m_setup.emulator, m_setup.PCM_RATE, this);
chipReset();
m_chipChannels.clear();
m_chipChannels.resize(synth.m_numChannels);

Expand All @@ -169,7 +176,7 @@ void MIDIplay::partialReset()
realTime_panic();
m_setup.tick_skip_samples_delay = 0;
synth.m_runAtPcmRate = m_setup.runAtPcmRate;
synth.reset(m_setup.emulator, m_setup.PCM_RATE, this);
chipReset();
m_chipChannels.clear();
m_chipChannels.resize((size_t)synth.m_numChannels);
resetMIDIDefaults();
Expand All @@ -193,6 +200,21 @@ void MIDIplay::resetMIDI()
caugh_missing_banks_percussion.clear();
}

void MIDIplay::chipReset()
{
Synth &synth = *m_synth;

#ifdef ADLMIDI_ENABLE_HW_SERIAL
if(m_setup.serial)
{
synth.resetSerial(m_setup.serialName, m_setup.serialBaud, m_setup.serialProtocol);
return;
}
#endif

synth.reset(m_setup.emulator, m_setup.PCM_RATE, this);
}

void MIDIplay::resetMIDIDefaults(int offset)
{
Synth &synth = *m_synth;
Expand Down Expand Up @@ -252,7 +274,11 @@ void MIDIplay::TickIterators(double s)

updateVibrato(s);
updateArpeggio(s);

#if !defined(ADLMIDI_AUDIO_TICK_HANDLER)
# ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
s *= m_sequencer->getTempoMultiplier(); // Glide will follow the tempo
# endif
updateGlide(s);
#endif
}
Expand Down
9 changes: 9 additions & 0 deletions ADLMIDI-Player/src/main/cpp/src/adlmidi_midiplay.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class MIDIplay
void resetMIDI();

private:
void chipReset();
void resetMIDIDefaults(int offset = 0);

public:
Expand Down Expand Up @@ -526,6 +527,14 @@ class MIDIplay
{
int emulator;
bool runAtPcmRate;

#ifdef ADLMIDI_ENABLE_HW_SERIAL
bool serial;
std::string serialName;
unsigned int serialBaud;
unsigned int serialProtocol;
#endif

unsigned int bankId;
int numFourOps;
unsigned int numChips;
Expand Down
Loading

0 comments on commit a241664

Please sign in to comment.