Skip to content

Commit

Permalink
mt32emu: Extended ReportHandler interface with two callbacks (#111)
Browse files Browse the repository at this point in the history
* Added callbacks onNoteOnIgnored and onPlayingPolySilenced to be invoked
  when insufficient partials condition is detected upon playing a new poly
* Added related support in Synth and Part
* Bumped target version mt32emu 2.8.0
  • Loading branch information
sergm committed Jun 10, 2023
1 parent 813e8f0 commit d1d2ca7
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 11 deletions.
5 changes: 5 additions & 0 deletions mt32emu/NEWS.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
HEAD:

* Provided callbacks to retrieve notifications about events when the synth engine runs
out of available partials. (#111)

2022-07-23:

2.7.0 released.
Expand Down
2 changes: 1 addition & 1 deletion mt32emu/cmake/project_data.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ set(libmt32emu_URL "http://munt.sourceforge.net/")
set(libmt32emu_CONTACT "sergm@muntemu.org")

set(libmt32emu_VERSION_MAJOR 2)
set(libmt32emu_VERSION_MINOR 7)
set(libmt32emu_VERSION_MINOR 8)
set(libmt32emu_VERSION_PATCH 0)
set(libmt32emu_VERSION "${libmt32emu_VERSION_MAJOR}.${libmt32emu_VERSION_MINOR}.${libmt32emu_VERSION_PATCH}")
6 changes: 5 additions & 1 deletion mt32emu/src/Part.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -498,9 +498,13 @@ void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhyt
synth->printDebug("%s (%s): Insufficient free partials to play key %d (velocity %d); needed=%d, free=%d, assignMode=%d", name, currentInstr, midiKey, velocity, needPartials, synth->partialManager->getFreePartialCount(), patchTemp->patch.assignMode);
synth->printPartialUsage();
#endif
synth->getReportHandler3()->onNoteOnIgnored(needPartials, synth->partialManager->getFreePartialCount());
return;
}
if (synth->isAbortingPoly()) {
synth->getReportHandler3()->onPlayingPolySilenced(needPartials, synth->partialManager->getFreePartialCount());
return;
}
if (synth->isAbortingPoly()) return;

Poly *poly = synth->partialManager->assignPolyToPart(this);
if (poly == NULL) {
Expand Down
21 changes: 20 additions & 1 deletion mt32emu/src/Synth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,9 @@ class Extensions {
Display *display;
bool oldMT32DisplayFeatures;

ReportHandler2 defaultReportHandler;
ReportHandler3 defaultReportHandler;
ReportHandler2 *reportHandler2;
ReportHandler3 *reportHandler3;
};

Bit32u Synth::getLibraryVersionInt() {
Expand Down Expand Up @@ -292,6 +293,7 @@ Synth::Synth(ReportHandler *useReportHandler) :

reportHandler = useReportHandler != NULL ? useReportHandler : &extensions.defaultReportHandler;
extensions.reportHandler2 = &extensions.defaultReportHandler;
extensions.reportHandler3 = &extensions.defaultReportHandler;

extensions.preallocatedReverbMemory = false;
for (int i = REVERB_MODE_ROOM; i <= REVERB_MODE_TAP_DELAY; i++) {
Expand Down Expand Up @@ -349,6 +351,23 @@ void Synth::setReportHandler2(ReportHandler2 *reportHandler2) {
reportHandler = &extensions.defaultReportHandler;
extensions.reportHandler2 = &extensions.defaultReportHandler;
}
extensions.reportHandler3 = &extensions.defaultReportHandler;
}

void Synth::setReportHandler3(ReportHandler3 *reportHandler3) {
if (reportHandler3 != NULL) {
reportHandler = reportHandler3;
extensions.reportHandler2 = reportHandler3;
extensions.reportHandler3 = reportHandler3;
} else {
reportHandler = &extensions.defaultReportHandler;
extensions.reportHandler2 = &extensions.defaultReportHandler;
extensions.reportHandler3 = &extensions.defaultReportHandler;
}
}

ReportHandler3 *Synth::getReportHandler3() {
return extensions.reportHandler3;
}

void ReportHandler::showLCDMessage(const char *data) {
Expand Down
17 changes: 17 additions & 0 deletions mt32emu/src/Synth.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,18 @@ class MT32EMU_EXPORT_V(2.6) ReportHandler2 : public ReportHandler {
virtual void onMidiMessageLEDStateUpdated(bool /* ledState */) {}
};

// Extends ReportHandler for delivering signals about insufficient partials conditions to the client.
class MT32EMU_EXPORT_V(2.8) ReportHandler3 : public ReportHandler2 {
public:
virtual ~ReportHandler3() {}

// Invoked in case the NoteOn MIDI message currently being processed cannot be played due to insufficient free partials.
virtual void onNoteOnIgnored(Bit32u /* partialsNeeded */, Bit32u /* partialsFree */) {}
// Invoked in case the partial allocator starts premature silencing a currently playing poly to free partials necessary
// for processing the preceding NoteOn MIDI message.
virtual void onPlayingPolySilenced(Bit32u /* partialsNeeded */, Bit32u /* partialsFree */) {}
};

class Synth {
friend class DefaultMidiStreamParser;
friend class Display;
Expand Down Expand Up @@ -256,6 +268,8 @@ friend class TVP;
void resetMasterTunePitchDelta();
Bit32s getMasterTunePitchDelta() const;

ReportHandler3 *getReportHandler3();

public:
static inline Bit16s clipSampleEx(Bit32s sampleEx) {
// Clamp values above 32767 to 32767, and values below -32768 to -32768
Expand Down Expand Up @@ -313,6 +327,9 @@ friend class TVP;
// Sets an implementation of ReportHandler2 interface for reporting various errors, information and debug messages.
// If the argument is NULL, the default implementation is installed as a fallback.
MT32EMU_EXPORT_V(2.6) void setReportHandler2(ReportHandler2 *reportHandler2);
// Sets an implementation of ReportHandler3 interface for reporting various errors, information and debug messages.
// If the argument is NULL, the default implementation is installed as a fallback.
MT32EMU_EXPORT_V(2.8) void setReportHandler3(ReportHandler3 *reportHandler3);

// Used to initialise the MT-32. Must be called before any other function.
// Returns true if initialization was successful, otherwise returns false.
Expand Down
3 changes: 2 additions & 1 deletion mt32emu/src/VersionTagging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ extern "C" {
MT32EMU_EXPORT_V(2.5) extern const volatile char mt32emu_2_5 = 0;
MT32EMU_EXPORT_V(2.6) extern const volatile char mt32emu_2_6 = 0;
MT32EMU_EXPORT_V(2.7) extern const volatile char mt32emu_2_7 = 0;
MT32EMU_EXPORT_V(2.8) extern const volatile char mt32emu_2_8 = 0;

#if MT32EMU_VERSION_MAJOR > 2 || MT32EMU_VERSION_MINOR > 7
#if MT32EMU_VERSION_MAJOR > 2 || MT32EMU_VERSION_MINOR > 8
#error "Missing version tag definition for current library version"
#endif
}
22 changes: 19 additions & 3 deletions mt32emu/src/c_interface/c_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ static const mt32emu_service_i_v6 SERVICE_VTABLE = {
} // namespace MT32Emu

struct mt32emu_data {
ReportHandler2 *reportHandler;
ReportHandler3 *reportHandler;
Synth *synth;
const ROMImage *controlROMImage;
const ROMImage *pcmROMImage;
Expand All @@ -156,7 +156,7 @@ struct mt32emu_data {

namespace MT32Emu {

class DelegatingReportHandlerAdapter : public ReportHandler2 {
class DelegatingReportHandlerAdapter : public ReportHandler3 {
public:
DelegatingReportHandlerAdapter(mt32emu_report_handler_i useReportHandler, void *useInstanceData) :
delegate(useReportHandler), instanceData(useInstanceData) {}
Expand Down Expand Up @@ -295,6 +295,22 @@ class DelegatingReportHandlerAdapter : public ReportHandler2 {
delegate.v1->onMidiMessageLEDStateUpdated(instanceData, ledState ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE);
}
}

void onNoteOnIgnored(Bit32u partialsNeeded, Bit32u partialsFree) {
if (isVersionLess(MT32EMU_REPORT_HANDLER_VERSION_2) || delegate.v2->onNoteOnIgnored == NULL) {
ReportHandler3::onNoteOnIgnored(partialsNeeded, partialsFree);
} else {
delegate.v2->onNoteOnIgnored(instanceData, partialsNeeded, partialsFree);
}
}

void onPlayingPolySilenced(Bit32u partialsNeeded, Bit32u partialsFree) {
if (isVersionLess(MT32EMU_REPORT_HANDLER_VERSION_2) || delegate.v2->onPlayingPolySilenced == NULL) {
ReportHandler3::onPlayingPolySilenced(partialsNeeded, partialsFree);
} else {
delegate.v2->onPlayingPolySilenced(instanceData, partialsNeeded, partialsFree);
}
}
};

class DelegatingMidiStreamParser : public DefaultMidiStreamParser {
Expand Down Expand Up @@ -546,7 +562,7 @@ mt32emu_context MT32EMU_C_CALL mt32emu_create_context(mt32emu_report_handler_i r
data->synth = new Synth;
if (report_handler.v0 != NULL) {
data->reportHandler = new DelegatingReportHandlerAdapter(report_handler, instance_data);
data->synth->setReportHandler2(data->reportHandler);
data->synth->setReportHandler3(data->reportHandler);
} else {
data->reportHandler = NULL;
}
Expand Down
21 changes: 20 additions & 1 deletion mt32emu/src/c_interface/c_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ typedef struct {
typedef enum {
MT32EMU_REPORT_HANDLER_VERSION_0 = 0,
MT32EMU_REPORT_HANDLER_VERSION_1 = 1,
MT32EMU_REPORT_HANDLER_VERSION_CURRENT = MT32EMU_REPORT_HANDLER_VERSION_1
MT32EMU_REPORT_HANDLER_VERSION_2 = 2,
MT32EMU_REPORT_HANDLER_VERSION_CURRENT = MT32EMU_REPORT_HANDLER_VERSION_2
} mt32emu_report_handler_version;

/** MIDI receiver interface versions */
Expand Down Expand Up @@ -188,6 +189,17 @@ typedef union mt32emu_report_handler_i mt32emu_report_handler_i;
/** Invoked when the emulated MIDI MESSAGE LED changes state. The led_state parameter represents whether the LED is ON. */ \
void (MT32EMU_C_CALL *onMidiMessageLEDStateUpdated)(void *instance_data, mt32emu_boolean led_state);

#define MT32EMU_REPORT_HANDLER_I_V2 \
/**
* Invoked in case the NoteOn MIDI message currently being processed cannot be played due to insufficient free partials.
*/ \
void (MT32EMU_C_CALL *onNoteOnIgnored)(void *instance_data, mt32emu_bit32u partialsNeeded, mt32emu_bit32u partialsFree); \
/**
* Invoked in case the partial allocator starts premature silencing a currently playing poly to free partials necessary
* for processing the preceding NoteOn MIDI message.
*/ \
void (MT32EMU_C_CALL *onPlayingPolySilenced)(void *instance_data, mt32emu_bit32u partialsNeeded, mt32emu_bit32u partialsFree);

typedef struct {
MT32EMU_REPORT_HANDLER_I_V0
} mt32emu_report_handler_i_v0;
Expand All @@ -197,6 +209,12 @@ typedef struct {
MT32EMU_REPORT_HANDLER_I_V1
} mt32emu_report_handler_i_v1;

typedef struct {
MT32EMU_REPORT_HANDLER_I_V0
MT32EMU_REPORT_HANDLER_I_V1
MT32EMU_REPORT_HANDLER_I_V2
} mt32emu_report_handler_i_v2;

/**
* Extensible interface for handling reported events.
* Union intended to view an interface of any subsequent version as any parent interface not requiring a cast.
Expand All @@ -205,6 +223,7 @@ typedef struct {
union mt32emu_report_handler_i {
const mt32emu_report_handler_i_v0 *v0;
const mt32emu_report_handler_i_v1 *v1;
const mt32emu_report_handler_i_v2 *v2;
};

#undef MT32EMU_REPORT_HANDLER_I_V0
Expand Down
42 changes: 39 additions & 3 deletions mt32emu/src/c_interface/cpp_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,17 @@ class IReportHandlerV1 : public IReportHandler {
~IReportHandlerV1() {}
};

// Extends IReportHandlerV1 for delivering signals about insufficient partials conditions to the client.
// Corresponds to the mt32emu_report_handler_i_v2 interface.
class IReportHandlerV2 : public IReportHandlerV1 {
public:
virtual void onNoteOnIgnored(Bit32u partialsNeeded, Bit32u partialsFree) = 0;
virtual void onPlayingPolySilenced(Bit32u partialsNeeded, Bit32u partialsFree) = 0;

protected:
~IReportHandlerV2() {}
};

// Defines the interface for receiving MIDI messages generated by MIDI stream parser.
// Corresponds to the current version of mt32emu_midi_receiver_i interface.
class IMidiReceiver {
Expand Down Expand Up @@ -234,6 +245,7 @@ class Service {
void createContext(mt32emu_report_handler_i report_handler = CppInterfaceImpl::NULL_REPORT_HANDLER, void *instance_data = NULL) { freeContext(); c = mt32emu_create_context(report_handler, instance_data); }
void createContext(IReportHandler &report_handler) { createContext(CppInterfaceImpl::getReportHandlerThunk(MT32EMU_REPORT_HANDLER_VERSION_0), &report_handler); }
void createContext(IReportHandlerV1 &report_handler) { createContext(CppInterfaceImpl::getReportHandlerThunk(MT32EMU_REPORT_HANDLER_VERSION_1), &report_handler); }
void createContext(IReportHandlerV2 &report_handler) { createContext(CppInterfaceImpl::getReportHandlerThunk(MT32EMU_REPORT_HANDLER_VERSION_2), &report_handler); }
void freeContext() { if (c != NULL) { mt32emu_free_context(c); c = NULL; } }
mt32emu_return_code addROMData(const Bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest = NULL) { return mt32emu_add_rom_data(c, data, data_size, sha1_digest); }
mt32emu_return_code addROMFile(const char *filename) { return mt32emu_add_rom_file(c, filename); }
Expand Down Expand Up @@ -420,6 +432,13 @@ static void MT32EMU_C_CALL onMidiMessageLEDStateUpdated(void *instance_data, mt3
static_cast<IReportHandlerV1 *>(instance_data)->onMidiMessageLEDStateUpdated(led_state != MT32EMU_BOOL_FALSE);
}

static void MT32EMU_C_CALL onNoteOnIgnored(void *instance_data, Bit32u partialsNeeded, Bit32u partialsFree) {
static_cast<IReportHandlerV2 *>(instance_data)->onNoteOnIgnored(partialsNeeded, partialsFree);
}
static void MT32EMU_C_CALL onPlayingPolySilenced(void *instance_data, Bit32u partialsNeeded, Bit32u partialsFree) {
static_cast<IReportHandlerV2 *>(instance_data)->onPlayingPolySilenced(partialsNeeded, partialsFree);
}

#define MT32EMU_REPORT_HANDLER_V0_THUNK \
getReportHandlerVersionID, \
printDebug, \
Expand All @@ -437,27 +456,44 @@ static void MT32EMU_C_CALL onMidiMessageLEDStateUpdated(void *instance_data, mt3
onPolyStateChanged, \
onProgramChanged

#define MT32EMU_REPORT_HANDLER_V1_THUNK \
onLCDStateUpdated, \
onMidiMessageLEDStateUpdated

#define MT32EMU_REPORT_HANDLER_V2_THUNK \
onNoteOnIgnored, \
onPlayingPolySilenced

static const mt32emu_report_handler_i_v0 REPORT_HANDLER_V0_THUNK = {
MT32EMU_REPORT_HANDLER_V0_THUNK
};

static const mt32emu_report_handler_i_v1 REPORT_HANDLER_V1_THUNK = {
MT32EMU_REPORT_HANDLER_V0_THUNK,
onLCDStateUpdated,
onMidiMessageLEDStateUpdated
MT32EMU_REPORT_HANDLER_V1_THUNK
};

static const mt32emu_report_handler_i_v2 REPORT_HANDLER_V2_THUNK = {
MT32EMU_REPORT_HANDLER_V0_THUNK,
MT32EMU_REPORT_HANDLER_V1_THUNK,
MT32EMU_REPORT_HANDLER_V2_THUNK
};

#undef MT32EMU_REPORT_HANDLER_THUNK_V0
#undef MT32EMU_REPORT_HANDLER_THUNK_V1
#undef MT32EMU_REPORT_HANDLER_THUNK_V2

static mt32emu_report_handler_version MT32EMU_C_CALL getReportHandlerVersionID(mt32emu_report_handler_i thunk) {
if (thunk.v0 == &REPORT_HANDLER_V0_THUNK) return MT32EMU_REPORT_HANDLER_VERSION_0;
if (thunk.v1 == &REPORT_HANDLER_V1_THUNK) return MT32EMU_REPORT_HANDLER_VERSION_1;
return MT32EMU_REPORT_HANDLER_VERSION_CURRENT;
}

static mt32emu_report_handler_i getReportHandlerThunk(mt32emu_report_handler_version versionID) {
mt32emu_report_handler_i thunk;
if (versionID == MT32EMU_REPORT_HANDLER_VERSION_0) thunk.v0 = &REPORT_HANDLER_V0_THUNK;
else thunk.v1 = &REPORT_HANDLER_V1_THUNK;
else if (versionID == MT32EMU_REPORT_HANDLER_VERSION_1) thunk.v1 = &REPORT_HANDLER_V1_THUNK;
else thunk.v2 = &REPORT_HANDLER_V2_THUNK;
return thunk;
}

Expand Down

0 comments on commit d1d2ca7

Please sign in to comment.