Skip to content

Commit

Permalink
Added chip channel allocation mode option
Browse files Browse the repository at this point in the history
This option allows to select one of three different chip channel allocation modes.
  • Loading branch information
Wohlstand committed Jun 29, 2022
1 parent d237ce5 commit 8c8f28e
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 4 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ To remove MIDI Sequencer, define `OPNMIDI_DISABLE_MIDI_SEQUENCER` macro and remo
* Added an ability to set number of loops
* Added an ability to disable/enable playing of selected MIDI channels
* Fixed memory damages and crashes while playing XMI files
* Added the chip channels allocation mode option

## 1.5.0.1 2020-10-11
* Fixed an incorrect timer processing when using a real-time interface
Expand Down
35 changes: 34 additions & 1 deletion include/opnmidi.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,26 @@ enum OPNMIDI_VolumeModels
/*! Logarithmic volume scale, used in Apogee Sound System. */
OPNMIDI_VolumeModel_APOGEE,
/*! Aproximated and shorted volume map table. Similar to general, but has less granularity. */
OPNMIDI_VolumeModel_9X
OPNMIDI_VolumeModel_9X,
/*! Count of available volume model modes */
OPNMIDI_VolumeModel_Count
};

/*!
* \brief Algorithms of channel allocation for new notes
*/
enum OPNMIDI_ChannelAlloc
{
/*! Automatical choise of the method according to the volume model and internal preferrences */
OPNMIDI_ChanAlloc_AUTO = -1,
/*! Take only channels that has expired sounding delay */
OPNMIDI_ChanAlloc_OffDelay,
/*! Take any first released channel with the same instrument */
OPNMIDI_ChanAlloc_SameInst,
/*! Take any first released channel */
OPNMIDI_ChanAlloc_AnyReleased,
/*! Count of available channel allocation modes */
OPNMIDI_ChanAlloc_Count
};

/**
Expand Down Expand Up @@ -477,6 +496,20 @@ extern OPNMIDI_DECLSPEC void opn2_setVolumeRangeModel(struct OPN2_MIDIPlayer *de
*/
extern OPNMIDI_DECLSPEC int opn2_getVolumeRangeModel(struct OPN2_MIDIPlayer *device);

/**
* @brief Set the channel allocation mode
* @param device Instance of the library
* @param chanalloc Channel allocation mode (#ADLMIDI_ChannelAlloc)
*/
extern OPNMIDI_DECLSPEC void opn2_setChannelAllocMode(struct OPN2_MIDIPlayer *device, int chanalloc);

/**
* @brief Get the current channel allocation mode
* @param device Instance of the library
* @return Channel allocation mode (#ADLMIDI_ChannelAlloc)
*/
extern OPNMIDI_DECLSPEC int opn2_getChannelAllocMode(struct OPN2_MIDIPlayer *device);

/**
* @brief Load WOPN bank file from File System
*
Expand Down
23 changes: 22 additions & 1 deletion src/opnmidi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ OPNMIDI_EXPORT void opn2_setLogarithmicVolumes(struct OPN2_MIDIPlayer *device, i
}
}

OPNMIDI_EXPORT void opn2_setVolumeRangeModel(OPN2_MIDIPlayer *device, int volumeModel)
OPNMIDI_EXPORT void opn2_setVolumeRangeModel(struct OPN2_MIDIPlayer *device, int volumeModel)
{
if(!device)
return;
Expand All @@ -476,6 +476,27 @@ OPNMIDI_EXPORT void opn2_setVolumeRangeModel(OPN2_MIDIPlayer *device, int volume
}
}

OPNMIDI_EXPORT void opn2_setChannelAllocMode(struct OPN2_MIDIPlayer *device, int chanalloc)
{
if(!device)
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
Synth &synth = *play->m_synth;
if(chanalloc < -1 || chanalloc >= OPNMIDI_ChanAlloc_Count)
chanalloc = OPNMIDI_ChanAlloc_AUTO;
synth.m_channelAlloc = static_cast<OPNMIDI_ChannelAlloc>(chanalloc);
}

OPNMIDI_EXPORT int opn2_getChannelAllocMode(struct OPN2_MIDIPlayer *device)
{
if(!device)
return -1;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return static_cast<int>(play->m_synth->m_channelAlloc);
}

OPNMIDI_EXPORT int opn2_getVolumeRangeModel(struct OPN2_MIDIPlayer *device)
{
if(!device)
Expand Down
29 changes: 27 additions & 2 deletions src/opnmidi_midiplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1274,14 +1274,39 @@ int64_t OPNMIDIplay::calculateChipChannelGoodness(size_t c, const MIDIchannel::N
const OpnChannel &chan = m_chipChannels[c];
int64_t koff_ms = chan.koff_time_until_neglible_us / 1000;
int64_t s = -koff_ms;
OPNMIDI_ChannelAlloc allocType = synth.m_channelAlloc;

if(allocType == OPNMIDI_ChanAlloc_AUTO)
{
if(synth.m_musicMode == Synth::MODE_CMF)
allocType = OPNMIDI_ChanAlloc_SameInst;
else
allocType = OPNMIDI_ChanAlloc_OffDelay;
}

// Rate channel with a releasing note
if(s < 0 && chan.users.empty())
{
bool isSame = (chan.recent_ins == ins);
s -= 40000;

// If it's same instrument, better chance to get it when no free channels
if(chan.recent_ins == ins)
s = (synth.m_musicMode == Synth::MODE_CMF) ? 0 : -koff_ms;
switch(allocType)
{
case OPNMIDI_ChanAlloc_SameInst:
if(isSame)
s = 0; // Re-use releasing channel with the same instrument
break;
case OPNMIDI_ChanAlloc_AnyReleased:
s = 0; // Re-use any releasing channel
break;
default:
case OPNMIDI_ChanAlloc_OffDelay:
if(isSame)
s = -koff_ms; // Wait until releasing sound will complete
break;
}

return s;
}

Expand Down
2 changes: 2 additions & 0 deletions src/opnmidi_opn2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ OPN2::OPN2() :
m_masterVolume(MasterVolumeDefault),
m_musicMode(MODE_MIDI),
m_volumeScale(VOLUME_Generic),
m_channelAlloc(OPNMIDI_ChanAlloc_AUTO),
m_lfoEnable(false),
m_lfoFrequency(0),
m_chipFamily(OPNChip_OPN2)
Expand Down Expand Up @@ -541,6 +542,7 @@ void OPN2::setVolumeScaleModel(OPNMIDI_VolumeModels volumeModel)
{
switch(volumeModel)
{
default:
case OPNMIDI_VolumeModel_AUTO://Do nothing until restart playing
break;

Expand Down
3 changes: 3 additions & 0 deletions src/opnmidi_opn2.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ class OPN2
VOLUME_9X
} m_volumeScale;

//! Channel allocation algorithm
OPNMIDI_ChannelAlloc m_channelAlloc;

//! Reserved
bool m_lfoEnable;
uint8_t m_lfoFrequency;
Expand Down
51 changes: 51 additions & 0 deletions utils/midi2vgm/midi2vgm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,40 @@ extern "C"
extern OPNMIDI_DECLSPEC void opn2_set_vgm_out_path(const char *path);
}

const char* volume_model_to_str(int vm)
{
switch(vm)
{
default:
case OPNMIDI_VolumeModel_Generic:
return "Generic";
case OPNMIDI_VolumeModel_NativeOPN2:
return "Native OPN2";
case OPNMIDI_VolumeModel_DMX:
return "DMX";
case OPNMIDI_VolumeModel_APOGEE:
return "Apogee Sound System";
case OPNMIDI_VolumeModel_9X:
return "9X";
}
}

const char* chanalloc_to_str(int vm)
{
switch(vm)
{
default:
case OPNMIDI_ChanAlloc_AUTO:
return "<auto>";
case OPNMIDI_ChanAlloc_OffDelay:
return "Off Delay";
case OPNMIDI_ChanAlloc_SameInst:
return "Same instrument";
case OPNMIDI_ChanAlloc_AnyReleased:
return "Any released";
}
}

static void printError(const char *err)
{
std::fprintf(stderr, "\nERROR: %s\n\n", err);
Expand Down Expand Up @@ -199,6 +233,10 @@ int main(int argc, char **argv)
" 3 DMX\n"
" 4 Apogee Sound System\n"
" 5 9x\n"
" -ca <num> Chooses one of chip channel allocation modes: \n"
" 0 Sounding delay"
" 1 Released channel with the same instrument"
" 2 Any released channel"
" -na Disables the automatical arpeggio\n"
" -z Make a compressed VGZ file\n"
" -frb Enables full-ranged CC74 XG Brightness controller\n"
Expand Down Expand Up @@ -236,6 +274,7 @@ int main(int argc, char **argv)
int loopEnabled = 0;
int volumeModel = OPNMIDI_VolumeModel_AUTO;
int autoArpeggioEnabled = 0;
int chanAlloc = OPNMIDI_ChanAlloc_AUTO;
size_t soloTrack = ~static_cast<size_t>(0);
int chipsCount = 1;// Single-chip by default
std::vector<int> muteChannels;
Expand Down Expand Up @@ -267,6 +306,15 @@ int main(int argc, char **argv)
}
volumeModel = static_cast<int>(std::strtol(argv[++arg], NULL, 10));
}
else if(!std::strcmp("-ca", argv[arg]))
{
if(arg + 1 >= argc)
{
printError("The option -carequires an argument!\n");
return 1;
}
chanAlloc = static_cast<int>(std::strtol(argv[++arg], NULL, 10));
}
else if(!std::strcmp("--chips", argv[arg]))
{
if(arg + 1 >= argc)
Expand Down Expand Up @@ -350,6 +398,7 @@ int main(int argc, char **argv)
opn2_setSoftPanEnabled(myDevice, 0);
opn2_setLoopEnabled(myDevice, loopEnabled);
opn2_setAutoArpeggio(myDevice, autoArpeggioEnabled);
opn2_setChannelAllocMode(myDevice, chanAlloc);

#ifdef DEBUG_TRACE_ALL_EVENTS
//Hook all MIDI events are ticking while generating an output buffer
Expand Down Expand Up @@ -397,6 +446,8 @@ int main(int argc, char **argv)

std::fprintf(stdout, " - Number of chips %d\n", opn2_getNumChipsObtained(myDevice));
std::fprintf(stdout, " - Track count: %lu\n", (unsigned long)opn2_trackCount(myDevice));
std::fprintf(stdout, " - Volume model: %s\n", volume_model_to_str(opn2_getVolumeRangeModel(myDevice)));
std::fprintf(stdout, " - Channel allocation mode: %s\n", chanalloc_to_str(opn2_getChannelAllocMode(myDevice)));

if(soloTrack != ~(size_t)0)
{
Expand Down
32 changes: 32 additions & 0 deletions utils/midiplay/opnplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,22 @@ const char* volume_model_to_str(int vm)
}
}

const char* chanalloc_to_str(int vm)
{
switch(vm)
{
default:
case OPNMIDI_ChanAlloc_AUTO:
return "<auto>";
case OPNMIDI_ChanAlloc_OffDelay:
return "Off Delay";
case OPNMIDI_ChanAlloc_SameInst:
return "Same instrument";
case OPNMIDI_ChanAlloc_AnyReleased:
return "Any released";
}
}

static void printError(const char *err)
{
std::fprintf(stderr, "\nERROR: %s\n\n", err);
Expand Down Expand Up @@ -254,6 +270,10 @@ int main(int argc, char **argv)
" 3 DMX\n"
" 4 Apogee Sound System\n"
" 5 9x\n"
" -ca <num> Chooses one of chip channel allocation modes: \n"
" 0 Sounding delay"
" 1 Released channel with the same instrument"
" 2 Any released channel"
" -frb Enables full-ranged CC74 XG Brightness controller\n"
" -mc <nums> Mute selected MIDI channels"
" where <num> - space separated numbers list (0-based!):"
Expand Down Expand Up @@ -307,6 +327,7 @@ int main(int argc, char **argv)
bool fullRangedBrightness = false;
int loopEnabled = 1;
int autoArpeggioEnabled = 0;
int chanAlloc = OPNMIDI_ChanAlloc_AUTO;
bool fullPanEnabled = false;
int emulator = OPNMIDI_EMU_MAME;
int volumeModel = OPNMIDI_VolumeModel_AUTO;
Expand Down Expand Up @@ -357,6 +378,15 @@ int main(int argc, char **argv)
}
volumeModel = static_cast<int>(std::strtol(argv[++arg], NULL, 10));
}
else if(!std::strcmp("-ca", argv[arg]))
{
if(arg + 1 >= argc)
{
printError("The option -carequires an argument!\n");
return 1;
}
chanAlloc = static_cast<int>(std::strtol(argv[++arg], NULL, 10));
}
else if(!std::strcmp("--chips", argv[arg]))
{
if(arg + 1 >= argc)
Expand Down Expand Up @@ -457,6 +487,7 @@ int main(int argc, char **argv)
opn2_setLoopEnabled(myDevice, recordWave ? 0 : loopEnabled);
opn2_setVolumeRangeModel(myDevice, OPNMIDI_VolumeModel_Generic);
opn2_setAutoArpeggio(myDevice, autoArpeggioEnabled);
opn2_setChannelAllocMode(myDevice, chanAlloc);
#ifdef DEBUG_TRACE_ALL_EVENTS
//Hook all MIDI events are ticking while generating an output buffer
if(!recordWave)
Expand Down Expand Up @@ -503,6 +534,7 @@ int main(int argc, char **argv)
std::fprintf(stdout, " - Number of chips %d\n", opn2_getNumChipsObtained(myDevice));
std::fprintf(stdout, " - Track count: %lu\n", static_cast<unsigned long>(opn2_trackCount(myDevice)));
std::fprintf(stdout, " - Volume model: %s\n", volume_model_to_str(opn2_getVolumeRangeModel(myDevice)));
std::fprintf(stdout, " - Channel allocation mode: %s\n", chanalloc_to_str(opn2_getChannelAllocMode(myDevice)));

if(soloTrack != ~static_cast<size_t>(0u))
{
Expand Down
19 changes: 19 additions & 0 deletions utils/vlc_codec/libopnmidi.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@
#define VOLUME_MODEL_LONGTEXT N_( \
"Declares volume scaling model which will affect volume levels.")

#define CHANNEL_ALLOCATION_TEXT N_("Channel allocation mode")
#define CHANNEL_ALLOCATION_LONGTEXT N_( \
"Declares the method of chip channel allocation for new notes.")

#define FULL_RANGE_CC74_TEXT N_("Full-range of brightness")
#define FULL_RANGE_CC74_LONGTEXT N_( \
"Scale range of CC-74 \"Brightness\" with full 0~127 range. By default is only 0~64 affects the sounding.")
Expand All @@ -98,6 +102,16 @@ static const char * const volume_models_descriptions[] =
NULL
};

static const int channel_alloc_values[] = { -1, 0, 1, 2 };
static const char * const channel_alloc_descriptions[] =
{
N_("Auto (defined by bank)"),
N_("By sounding delays"),
N_("Released channel of same instrument"),
N_("Any released channel"),
NULL
};

#define EMULATOR_TYPE_TEXT N_("OPN2 Emulation core")
#define EMULATOR_TYPE_LINGTEXT N_( \
"OPN2 Emulator that will be used to generate final sound.")
Expand Down Expand Up @@ -150,6 +164,9 @@ vlc_module_begin ()
add_integer (CONFIG_PREFIX "volume-model", 0, VOLUME_MODEL_TEXT, VOLUME_MODEL_LONGTEXT, false )
change_integer_list( volume_models_values, volume_models_descriptions )

add_integer (CONFIG_PREFIX "channel-allocation", -1, CHANNEL_ALLOCATION_TEXT, CHANNEL_ALLOCATION_LONGTEXT, false )
change_integer_list( channel_alloc_values, channel_alloc_descriptions )

add_integer (CONFIG_PREFIX "emulator-type", 0, EMULATOR_TYPE_TEXT, EMULATOR_TYPE_LINGTEXT, false)
change_integer_list( emulator_type_values, emulator_type_descriptions )

Expand Down Expand Up @@ -216,6 +233,8 @@ static int Open (vlc_object_t *p_this)

opn2_setVolumeRangeModel(p_sys->synth, var_InheritInteger(p_this, CONFIG_PREFIX "volume-model"));

opn2_setChannelAllocMode(p_sys->synth, var_InheritInteger(p_this, CONFIG_PREFIX "channel-allocation"));

opn2_setSoftPanEnabled(p_sys->synth, var_InheritBool(p_this, CONFIG_PREFIX "full-panning"));

opn2_setFullRangeBrightness(p_sys->synth, var_InheritBool(p_this, CONFIG_PREFIX "full-range-brightness"));
Expand Down

0 comments on commit 8c8f28e

Please sign in to comment.