Upgrade libADLMIDI and libOPNMIDI

Added ability to switch emulator and it's accuracy level ("enabling of 'run at PCM rate' reduces accuracy, and also reduces CPU usage")
Added draft code for future external banks support (WOPL format for ADLMIDI and WOPN format for OPNMIDI)

ADLMIDI 1.3.3   2018-06-19
 * Fixed an inability to load another custom bank without of library re-initialization
 * Optimizing the MIDI banks management system for MultiBanks (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)
 * Fixed incorrect 4-op counter which is still catch 4-op instruments on 2-op banks
 * Fixed an incorrect processing of auto-flags
 * Fixed incorrect initial MIDI tempo when MIDI file doesn't includes the tempo event
 * Channel and Note Aftertouch features are now supported correctly! Aftertouch is the tremolo / vibrato, NOT A VOLUME!
 * Updated DosBox OPL3 emulator up to r4111 of official DosBox trunk (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)
 * The automatical choosing of 4 operator channels count has been improved (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)
 * Added optional HQ resampler for Nuked OPL3 emulators which does usage of Zita-Resampler library (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)

ADLMIDI 1.3.2   2018-04-24
 * Added ability to disable MUS and XMI converters
 * Added ability to disable embedded MIDI sequencer to use library as RealTime synthesizer only or use any custom MIDI sequencer plugins.
 * Fixed blank instruments fallback in multi-bank support. When using non-zero bank, if instrument is blank, then, instrument will be taken from a root (I.e. zero bank).
 * Added support for real-time switching the emulator
 * Added support for CC-120 - "All sound off" on the MIDI channel
 * Changed logic of CC-74 Brightness to affect sound only between 0 and 64 like real XG synthesizers. Ability to turn on a full-ranged brightness (to use full 0...127 range) is kept.
 * Added support for different output sample formats (PCM8, PCM8U, PCM16, PCM16U, PCM32, PCM32U, Float32, and Float64) (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)
 * Reworked MIDI channels management to avoid any memory reallocations while music processing for a hard real time. (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)

OPNMIDI 1.3.0   2018-06-19
 * Optimizing the MIDI banks management system for MultiBanks (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)
 * Fixed incorrect initial MIDI tempo when MIDI file doesn't includes the tempo event
 * Fixed an incorrect processing of auto-flags
 * MAME YM2612 now results a more accurate sound as internal using of native sample rate makes more correct sound generation
 * Channel and Note Aftertouch features are now supported correctly! Aftertouch is the tremolo / vibrato, NOT A VOLUME!
 * Added optional HQ resampler for Nuked OPL3 emulators which does usage of Zita-Resampler library (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)

OPNMIDI 1.2.0   2018-04-24
 * Added ability to disable MUS and XMI converters
 * Added ability to disable embedded MIDI sequencer to use library as RealTime synthesizer only or use any custom MIDI sequencer plugins.
 * Fixed blank instruments fallback in multi-bank support. When using non-zero bank, if instrument is blank, then, instrument will be taken from a root (I.e. zero bank).
 * Added support for real-time switching the emulator
 * Added support for MAME YM2612 Emulator
 * Added support for CC-120 - "All sound off" on the MIDI channel
 * Changed logic of CC-74 Brightness to affect sound only between 0 and 64 like real XG synthesizers. Ability to turn on a full-ranged brightness (to use full 0...127 range) is kept.
 * Added support for different output sample formats (PCM8, PCM8U, PCM16, PCM16U, PCM32, PCM32U, Float32, and Float64) (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)
 * Reworked MIDI channels management to avoid any memory reallocations while music processing for a hard real time. (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)
This commit is contained in:
Vitaly Novichkov 2018-06-20 00:48:42 +03:00 committed by Christoph Oelckers
parent 9b4e8efcb9
commit ceec12056a
65 changed files with 20498 additions and 8987 deletions

View file

@ -638,8 +638,8 @@ add_definitions(-DOPNMIDI_USE_LEGACY_EMULATOR)
add_definitions(-DADLMIDI_DISABLE_MUS_SUPPORT -DADLMIDI_DISABLE_XMI_SUPPORT -DADLMIDI_DISABLE_MIDI_SEQUENCER)
add_definitions(-DOPNMIDI_DISABLE_MUS_SUPPORT -DOPNMIDI_DISABLE_XMI_SUPPORT -DOPNMIDI_DISABLE_MIDI_SEQUENCER)
# Disable ADLMIDI's MIDI Sequencer, MUS and XMI converters
add_definitions(-DADLMIDI_DISABLE_MUS_SUPPORT -DADLMIDI_DISABLE_XMI_SUPPORT -DADLMIDI_DISABLE_MIDI_SEQUENCER)
# Disable OPNMIDI's experimental yet emulator (using of it has some issues and missing notes in playback)
add_definitions(-DOPNMIDI_DISABLE_GX_EMULATOR)
# Project files should be aware of the header files. We can GLOB these since
# there's generally a new cpp for every header so this file will get changed
@ -855,14 +855,25 @@ set( FASTMATH_SOURCES
sound/adlmidi/adlmidi_midiplay.cpp
sound/adlmidi/adlmidi_opl3.cpp
sound/adlmidi/adlmidi_private.cpp
sound/adlmidi/dbopl.cpp
sound/adlmidi/nukedopl3.c
sound/adlmidi/chips/dosbox/dbopl.cpp
sound/adlmidi/chips/dosbox_opl3.cpp
sound/adlmidi/chips/nuked/nukedopl3_174.c
sound/adlmidi/chips/nuked/nukedopl3.c
sound/adlmidi/chips/nuked_opl3.cpp
sound/adlmidi/chips/nuked_opl3_v174.cpp
sound/adlmidi/wopl/wopl_file.c
sound/opnmidi/chips/gens_opn2.cpp
sound/opnmidi/chips/gens/Ym2612_Emu.cpp
sound/opnmidi/chips/mame/mame_ym2612fm.c
sound/opnmidi/chips/mame_opn2.cpp
sound/opnmidi/chips/nuked_opn2.cpp
sound/opnmidi/chips/nuked/ym3438.c
sound/opnmidi/opnmidi.cpp
sound/opnmidi/opnmidi_load.cpp
sound/opnmidi/opnmidi_midiplay.cpp
sound/opnmidi/opnmidi_opn2.cpp
sound/opnmidi/opnmidi_private.cpp
sound/opnmidi/Ym2612_ChipEmu.cpp
)
set (PCH_SOURCES

File diff suppressed because it is too large Load diff

View file

@ -24,8 +24,16 @@
#ifndef ADLDATA_H
#define ADLDATA_H
#include <string.h>
#include <stdint.h>
#pragma pack(push, 1)
#define ADLDATA_BYTE_COMPARABLE(T) \
inline bool operator==(const T &a, const T &b) \
{ return !memcmp(&a, &b, sizeof(T)); } \
inline bool operator!=(const T &a, const T &b) \
{ return !operator==(a, b); }
extern const struct adldata
{
uint32_t modulator_E862, carrier_E862; // See below
@ -34,22 +42,46 @@ extern const struct adldata
int8_t finetune;
} adl[];
ADLDATA_BYTE_COMPARABLE(struct adldata)
enum { adlDefaultNumber = 189 };
extern const struct adlinsdata
{
enum { Flag_Pseudo4op = 0x01, Flag_NoSound = 0x02 };
enum { Flag_Pseudo4op = 0x01, Flag_NoSound = 0x02, Flag_Real4op = 0x04 };
uint16_t adlno1, adlno2;
uint8_t tone;
uint8_t flags;
uint16_t ms_sound_kon; // Number of milliseconds it produces sound;
uint16_t ms_sound_koff;
double voice2_fine_tune;
double voice2_fine_tune;
} adlins[];
ADLDATA_BYTE_COMPARABLE(struct adlinsdata)
int maxAdlBanks();
extern const unsigned short banks[][256];
extern const char* const banknames[];
enum { adlNoteOnMaxTime = 40000 };
/**
* @brief Instrument data with operators included
*/
struct adlinsdata2
{
adldata adl[2];
uint8_t tone;
uint8_t flags;
uint16_t ms_sound_kon; // Number of milliseconds it produces sound;
uint16_t ms_sound_koff;
double voice2_fine_tune;
adlinsdata2() {}
explicit adlinsdata2(const adlinsdata &d);
};
ADLDATA_BYTE_COMPARABLE(struct adlinsdata2)
#undef ADLDATA_BYTE_COMPARABLE
#pragma pack(pop)
/**
* @brief Bank global setup
*/
@ -62,4 +94,26 @@ extern const struct AdlBankSetup
bool scaleModulators;
} adlbanksetup[];
/**
* @brief Conversion of storage formats
*/
inline adlinsdata2::adlinsdata2(const adlinsdata &d)
: tone(d.tone), flags(d.flags),
ms_sound_kon(d.ms_sound_kon), ms_sound_koff(d.ms_sound_koff),
voice2_fine_tune(d.voice2_fine_tune)
{
adl[0] = ::adl[d.adlno1];
adl[1] = ::adl[d.adlno2];
}
/**
* @brief Convert external instrument to internal instrument
*/
void cvt_ADLI_to_FMIns(adlinsdata2 &dst, const struct ADL_Instrument &src);
/**
* @brief Convert internal instrument to external instrument
*/
void cvt_FMIns_to_ADLI(struct ADL_Instrument &dst, const adlinsdata2 &src);
#endif //ADLDATA_H

View file

@ -37,6 +37,13 @@ static ADL_Version adl_version = {
ADLMIDI_VERSION_PATCHLEVEL
};
static const ADLMIDI_AudioFormat adl_DefaultAudioFormat =
{
ADLMIDI_SampleType_S16,
sizeof(int16_t),
2 * sizeof(int16_t),
};
/*---------------------------EXPORTS---------------------------*/
ADLMIDI_EXPORT struct ADL_MIDIPlayer *adl_init(long sample_rate)
@ -67,7 +74,12 @@ ADLMIDI_EXPORT int adl_setNumChips(ADL_MIDIPlayer *device, int numCards)
return -2;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
#ifdef ADLMIDI_HW_OPL
(void)numCards;
play->m_setup.NumCards = 1;
#else
play->m_setup.NumCards = static_cast<unsigned int>(numCards);
#endif
if(play->m_setup.NumCards < 1 || play->m_setup.NumCards > MaxCards)
{
play->setErrorString("number of chips may only be 1.." MaxCards_STR ".\n");
@ -108,7 +120,7 @@ ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank)
if(static_cast<uint32_t>(bankno) >= NumBanks)
{
char errBuf[150];
snprintf(errBuf, 150, "Embedded bank number may only be 0..%u!\n", (NumBanks - 1));
snprintf(errBuf, 150, "Embedded bank number may only be 0..%u!\n", static_cast<unsigned int>(NumBanks - 1));
play->setErrorString(errBuf);
return -1;
}
@ -131,6 +143,142 @@ ADLMIDI_EXPORT const char *const *adl_getBankNames()
return banknames;
}
ADLMIDI_EXPORT int adl_reserveBanks(ADL_MIDIPlayer *device, unsigned banks)
{
if(!device)
return -1;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
OPL3::BankMap &map = play->opl.dynamic_banks;
map.reserve(banks);
return (int)map.capacity();
}
ADLMIDI_EXPORT int adl_getBank(ADL_MIDIPlayer *device, const ADL_BankId *idp, int flags, ADL_Bank *bank)
{
if(!device || !idp || !bank)
return -1;
ADL_BankId id = *idp;
if(id.lsb > 127 || id.msb > 127 || id.percussive > 1)
return -1;
unsigned idnumber = (id.msb << 8) | id.lsb | (id.percussive ? OPL3::PercussionTag : 0);
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
OPL3::BankMap &map = play->opl.dynamic_banks;
OPL3::BankMap::iterator it;
if(!(flags & ADLMIDI_Bank_Create))
{
it = map.find(idnumber);
if(it == map.end())
return -1;
}
else
{
std::pair<uint16_t, OPL3::Bank> value;
value.first = idnumber;
memset(&value.second, 0, sizeof(value.second));
for (unsigned i = 0; i < 128; ++i)
value.second.ins[i].flags = adlinsdata::Flag_NoSound;
std::pair<OPL3::BankMap::iterator, bool> ir;
if(flags & ADLMIDI_Bank_CreateRt)
{
ir = map.insert(value, OPL3::BankMap::do_not_expand_t());
if(ir.first == map.end())
return -1;
}
else
ir = map.insert(value);
it = ir.first;
}
it.to_ptrs(bank->pointer);
return 0;
}
ADLMIDI_EXPORT int adl_getBankId(ADL_MIDIPlayer *device, const ADL_Bank *bank, ADL_BankId *id)
{
if(!device || !bank)
return -1;
OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
unsigned idnumber = it->first;
id->msb = (idnumber >> 8) & 127;
id->lsb = idnumber & 127;
id->percussive = (idnumber & OPL3::PercussionTag) ? 1 : 0;
return 0;
}
ADLMIDI_EXPORT int adl_removeBank(ADL_MIDIPlayer *device, ADL_Bank *bank)
{
if(!device || !bank)
return -1;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
OPL3::BankMap &map = play->opl.dynamic_banks;
OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
size_t size = map.size();
map.erase(it);
return (map.size() != size) ? 0 : -1;
}
ADLMIDI_EXPORT int adl_getFirstBank(ADL_MIDIPlayer *device, ADL_Bank *bank)
{
if(!device)
return -1;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
OPL3::BankMap &map = play->opl.dynamic_banks;
OPL3::BankMap::iterator it = map.begin();
if(it == map.end())
return -1;
it.to_ptrs(bank->pointer);
return 0;
}
ADLMIDI_EXPORT int adl_getNextBank(ADL_MIDIPlayer *device, ADL_Bank *bank)
{
if(!device)
return -1;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
OPL3::BankMap &map = play->opl.dynamic_banks;
OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
if(++it == map.end())
return -1;
it.to_ptrs(bank->pointer);
return 0;
}
ADLMIDI_EXPORT int adl_getInstrument(ADL_MIDIPlayer *device, const ADL_Bank *bank, unsigned index, ADL_Instrument *ins)
{
if(!device || !bank || index > 127 || !ins)
return 1;
OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
cvt_FMIns_to_ADLI(*ins, it->second.ins[index]);
ins->version = 0;
return 0;
}
ADLMIDI_EXPORT int adl_setInstrument(ADL_MIDIPlayer *device, ADL_Bank *bank, unsigned index, const ADL_Instrument *ins)
{
if(!device || !bank || index > 127 || !ins)
return 1;
if(ins->version != 0)
return 1;
OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
cvt_ADLI_to_FMIns(it->second.ins[index], *ins);
return 0;
}
ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4)
{
if(!device)
@ -165,7 +313,9 @@ ADLMIDI_EXPORT void adl_setPercMode(ADL_MIDIPlayer *device, int percmod)
if(!device) return;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
play->m_setup.AdlPercussionMode = percmod;
play->opl.AdlPercussionMode = play->m_setup.AdlPercussionMode;
play->opl.AdlPercussionMode = play->m_setup.AdlPercussionMode < 0 ?
play->opl.dynamic_bank_setup.adLibPercussions :
(play->m_setup.AdlPercussionMode != 0);
play->opl.updateFlags();
}
@ -174,7 +324,9 @@ ADLMIDI_EXPORT void adl_setHVibrato(ADL_MIDIPlayer *device, int hvibro)
if(!device) return;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
play->m_setup.HighVibratoMode = hvibro;
play->opl.HighVibratoMode = play->m_setup.HighVibratoMode;
play->opl.HighVibratoMode = play->m_setup.HighVibratoMode < 0 ?
play->opl.dynamic_bank_setup.deepVibrato :
(play->m_setup.HighVibratoMode != 0);
play->opl.updateDeepFlags();
}
@ -183,7 +335,9 @@ ADLMIDI_EXPORT void adl_setHTremolo(ADL_MIDIPlayer *device, int htremo)
if(!device) return;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
play->m_setup.HighTremoloMode = htremo;
play->opl.HighTremoloMode = play->m_setup.HighTremoloMode;
play->opl.HighTremoloMode = play->m_setup.HighTremoloMode < 0 ?
play->opl.dynamic_bank_setup.deepTremolo :
(play->m_setup.HighTremoloMode != 0);
play->opl.updateDeepFlags();
}
@ -192,7 +346,16 @@ ADLMIDI_EXPORT void adl_setScaleModulators(ADL_MIDIPlayer *device, int smod)
if(!device) return;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
play->m_setup.ScaleModulators = smod;
play->opl.ScaleModulators = play->m_setup.ScaleModulators;
play->opl.ScaleModulators = play->m_setup.ScaleModulators < 0 ?
play->opl.dynamic_bank_setup.scaleModulators :
(play->m_setup.ScaleModulators != 0);
}
ADLMIDI_EXPORT void adl_setFullRangeBrightness(struct ADL_MIDIPlayer *device, int fr_brightness)
{
if(!device) return;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
play->m_setup.fullRangeBrightnessCC74 = (fr_brightness != 0);
}
ADLMIDI_EXPORT void adl_setLoopEnabled(ADL_MIDIPlayer *device, int loopEn)
@ -202,12 +365,16 @@ ADLMIDI_EXPORT void adl_setLoopEnabled(ADL_MIDIPlayer *device, int loopEn)
play->m_setup.loopingIsEnabled = (loopEn != 0);
}
/* !!!DEPRECATED!!! */
ADLMIDI_EXPORT void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol)
{
if(!device) return;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
play->m_setup.LogarithmicVolumes = logvol;
play->opl.LogarithmicVolumes = play->m_setup.LogarithmicVolumes;
play->m_setup.LogarithmicVolumes = (logvol != 0);
if(play->m_setup.LogarithmicVolumes)
play->opl.ChangeVolumeRangesModel(ADLMIDI_VolumeModel_NativeOPL3);
else
play->opl.ChangeVolumeRangesModel(static_cast<ADLMIDI_VolumeModels>(play->opl.m_volumeScale));
}
ADLMIDI_EXPORT void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int volumeModel)
@ -215,7 +382,10 @@ ADLMIDI_EXPORT void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int v
if(!device) return;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
play->m_setup.VolumeModel = volumeModel;
play->opl.ChangeVolumeRangesModel(static_cast<ADLMIDI_VolumeModels>(volumeModel));
if(play->m_setup.VolumeModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model
play->opl.m_volumeScale = (OPL3::VolumesScale)play->opl.dynamic_bank_setup.volumeModel;
else
play->opl.ChangeVolumeRangesModel(static_cast<ADLMIDI_VolumeModels>(volumeModel));
}
ADLMIDI_EXPORT int adl_openBankFile(struct ADL_MIDIPlayer *device, const char *filePath)
@ -312,16 +482,64 @@ ADLMIDI_EXPORT int adl_openData(ADL_MIDIPlayer *device, const void *mem, unsigne
ADLMIDI_EXPORT const char *adl_emulatorName()
{
#ifdef ADLMIDI_USE_DOSBOX_OPL
return "DosBox";
#else
return "Nuked";
#endif
return "<adl_emulatorName() is deprecated! Use adl_chipEmulatorName() instead!>";
}
ADLMIDI_EXPORT const char *adl_chipEmulatorName(struct ADL_MIDIPlayer *device)
{
if(device)
{
#ifndef ADLMIDI_HW_OPL
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
if(play && !play->opl.cardsOP2.empty())
return play->opl.cardsOP2[0]->emulatorName();
#else
return "Hardware OPL3 chip on 0x330";
#endif
}
return "Unknown";
}
ADLMIDI_EXPORT int adl_switchEmulator(struct ADL_MIDIPlayer *device, int emulator)
{
if(device)
{
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
if(play && (emulator >= 0) && (emulator < ADLMIDI_EMU_end))
{
play->m_setup.emulator = emulator;
adl_reset(device);
return 0;
}
play->setErrorString("OPL3 MIDI: Unknown emulation core!");
}
return -1;
}
ADLMIDI_EXPORT int adl_setRunAtPcmRate(ADL_MIDIPlayer *device, int enabled)
{
if(device)
{
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
if(play)
{
play->m_setup.runAtPcmRate = (enabled != 0);
adl_reset(device);
return 0;
}
}
return -1;
}
ADLMIDI_EXPORT const char *adl_linkedLibraryVersion()
{
#if !defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
return ADLMIDI_VERSION;
#else
return ADLMIDI_VERSION " (HQ)";
#endif
}
ADLMIDI_EXPORT const ADL_Version *adl_linkedVersion()
@ -373,9 +591,10 @@ ADLMIDI_EXPORT void adl_reset(struct ADL_MIDIPlayer *device)
return;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
play->m_setup.tick_skip_samples_delay = 0;
play->opl.Reset(play->m_setup.PCM_RATE);
play->opl.runAtPcmRate = play->m_setup.runAtPcmRate;
play->opl.Reset(play->m_setup.emulator, play->m_setup.PCM_RATE, play);
play->ch.clear();
play->ch.resize(play->opl.NumChannels);
play->ch.resize((size_t)play->opl.NumChannels);
}
ADLMIDI_EXPORT double adl_totalTimeLength(struct ADL_MIDIPlayer *device)
@ -568,31 +787,156 @@ ADLMIDI_EXPORT void adl_setDebugMessageHook(struct ADL_MIDIPlayer *device, ADL_D
play->hooks.onDebugMessage_userData = userData;
}
inline static void SendStereoAudio(int &samples_requested,
ssize_t &in_size,
short *_in,
ssize_t out_pos,
short *_out)
#ifndef ADLMIDI_HW_OPL
template <class Dst>
static void CopySamplesRaw(ADL_UInt8 *dstLeft, ADL_UInt8 *dstRight, const int32_t *src,
size_t frameCount, unsigned sampleOffset)
{
if(!in_size)
return;
size_t offset = static_cast<size_t>(out_pos);
size_t inSamples = static_cast<size_t>(in_size * 2);
size_t maxSamples = static_cast<size_t>(samples_requested) - offset;
size_t toCopy = std::min(maxSamples, inSamples);
std::memcpy(_out + out_pos, _in, toCopy * sizeof(short));
for(size_t i = 0; i < frameCount; ++i) {
*(Dst *)(dstLeft + (i * sampleOffset)) = src[2 * i];
*(Dst *)(dstRight + (i * sampleOffset)) = src[(2 * i) + 1];
}
}
template <class Dst, class Ret>
static void CopySamplesTransformed(ADL_UInt8 *dstLeft, ADL_UInt8 *dstRight, const int32_t *src,
size_t frameCount, unsigned sampleOffset,
Ret(&transform)(int32_t))
{
for(size_t i = 0; i < frameCount; ++i) {
*(Dst *)(dstLeft + (i * sampleOffset)) = (Dst)transform(src[2 * i]);
*(Dst *)(dstRight + (i * sampleOffset)) = (Dst)transform(src[(2 * i) + 1]);
}
}
ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out)
static int SendStereoAudio(int samples_requested,
ssize_t in_size,
int32_t *_in,
ssize_t out_pos,
ADL_UInt8 *left,
ADL_UInt8 *right,
const ADLMIDI_AudioFormat *format)
{
if(!in_size)
return 0;
size_t outputOffset = static_cast<size_t>(out_pos);
size_t inSamples = static_cast<size_t>(in_size * 2);
size_t maxSamples = static_cast<size_t>(samples_requested) - outputOffset;
size_t toCopy = std::min(maxSamples, inSamples);
ADLMIDI_SampleType sampleType = format->type;
const unsigned containerSize = format->containerSize;
const unsigned sampleOffset = format->sampleOffset;
left += (outputOffset / 2) * sampleOffset;
right += (outputOffset / 2) * sampleOffset;
typedef int32_t(&pfnConvert)(int32_t);
typedef float(&ffnConvert)(int32_t);
typedef double(&dfnConvert)(int32_t);
switch(sampleType) {
case ADLMIDI_SampleType_S8:
case ADLMIDI_SampleType_U8:
{
pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S8) ? adl_cvtS8 : adl_cvtU8;
switch(containerSize) {
case sizeof(int8_t):
CopySamplesTransformed<int8_t>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
case sizeof(int16_t):
CopySamplesTransformed<int16_t>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
case sizeof(int32_t):
CopySamplesTransformed<int32_t>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
default:
return -1;
}
break;
}
case ADLMIDI_SampleType_S16:
case ADLMIDI_SampleType_U16:
{
pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S16) ? adl_cvtS16 : adl_cvtU16;
switch(containerSize) {
case sizeof(int16_t):
CopySamplesTransformed<int16_t>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
case sizeof(int32_t):
CopySamplesRaw<int32_t>(left, right, _in, toCopy / 2, sampleOffset);
break;
default:
return -1;
}
break;
}
case ADLMIDI_SampleType_S24:
case ADLMIDI_SampleType_U24:
{
pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S24) ? adl_cvtS24 : adl_cvtU24;
switch(containerSize) {
case sizeof(int32_t):
CopySamplesTransformed<int32_t>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
default:
return -1;
}
break;
}
case ADLMIDI_SampleType_S32:
case ADLMIDI_SampleType_U32:
{
pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S32) ? adl_cvtS32 : adl_cvtU32;
switch(containerSize) {
case sizeof(int32_t):
CopySamplesTransformed<int32_t>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
default:
return -1;
}
break;
}
case ADLMIDI_SampleType_F32:
{
if(containerSize != sizeof(float))
return -1;
ffnConvert cvt = adl_cvtReal<float>;
CopySamplesTransformed<float>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
}
case ADLMIDI_SampleType_F64:
{
if(containerSize != sizeof(double))
return -1;
dfnConvert cvt = adl_cvtReal<double>;
CopySamplesTransformed<double>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
}
default:
return -1;
}
return 0;
}
#endif
ADLMIDI_EXPORT int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short *out)
{
return adl_playFormat(device, sampleCount, (ADL_UInt8 *)out, (ADL_UInt8 *)(out + 1), &adl_DefaultAudioFormat);
}
ADLMIDI_EXPORT int adl_playFormat(ADL_MIDIPlayer *device, int sampleCount,
ADL_UInt8 *out_left, ADL_UInt8 *out_right,
const ADLMIDI_AudioFormat *format)
{
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
#ifdef ADLMIDI_HW_OPL
(void)device;
(void)sampleCount;
(void)out;
(void)out_left;
(void)out_right;
(void)format;
return 0;
#else
sampleCount -= sampleCount % 2; //Avoid even sample requests
@ -646,33 +990,23 @@ ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out)
ssize_t in_generatedPhys = in_generatedStereo * 2;
//! Unsigned total sample count
//fill buffer with zeros
int16_t *out_buf = player->outBuf;
std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(int16_t));
int32_t *out_buf = player->outBuf;
std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(out_buf[0]));
unsigned int chips = player->opl.NumCards;
if(chips == 1)
{
#ifdef ADLMIDI_USE_DOSBOX_OPL
player->opl.cards[0].GenerateArr(out_buf, &in_generatedStereo);
in_generatedPhys = in_generatedStereo * 2;
#else
OPL3_GenerateStream(&player->opl.cards[0], out_buf, static_cast<Bit32u>(in_generatedStereo));
#endif
player->opl.cardsOP2[0]->generate32(out_buf, (size_t)in_generatedStereo);
}
else if(n_periodCountStereo > 0)
{
/* Generate data from every chip and mix result */
for(unsigned card = 0; card < chips; ++card)
{
#ifdef ADLMIDI_USE_DOSBOX_OPL
player->opl.cards[card].GenerateArrMix(out_buf, &in_generatedStereo);
in_generatedPhys = in_generatedStereo * 2;
#else
OPL3_GenerateStreamMix(&player->opl.cards[card], out_buf, static_cast<Bit32u>(in_generatedStereo));
#endif
}
for(size_t card = 0; card < chips; ++card)
player->opl.cardsOP2[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo);
}
/* Process it */
SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out);
if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1)
return 0;
left -= (int)in_generatedPhys;
gotten_len += (in_generatedPhys) /* - setup.stored_samples*/;
@ -698,11 +1032,20 @@ ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out)
ADLMIDI_EXPORT int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, short *out)
{
return adl_generateFormat(device, sampleCount, (ADL_UInt8 *)out, (ADL_UInt8 *)(out + 1), &adl_DefaultAudioFormat);
}
ADLMIDI_EXPORT int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleCount,
ADL_UInt8 *out_left, ADL_UInt8 *out_right,
const ADLMIDI_AudioFormat *format)
{
#ifdef ADLMIDI_HW_OPL
(void)device;
(void)sampleCount;
(void)out;
(void)out_left;
(void)out_right;
(void)format;
return 0;
#else
sampleCount -= sampleCount % 2; //Avoid even sample requests
@ -739,33 +1082,20 @@ ADLMIDI_EXPORT int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount,
ssize_t in_generatedPhys = in_generatedStereo * 2;
//! Unsigned total sample count
//fill buffer with zeros
int16_t *out_buf = player->outBuf;
std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(int16_t));
int32_t *out_buf = player->outBuf;
std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(out_buf[0]));
unsigned int chips = player->opl.NumCards;
if(chips == 1)
{
#ifdef ADLMIDI_USE_DOSBOX_OPL
player->opl.cards[0].GenerateArr(out_buf, &in_generatedStereo);
in_generatedPhys = in_generatedStereo * 2;
#else
OPL3_GenerateStream(&player->opl.cards[0], out_buf, static_cast<Bit32u>(in_generatedStereo));
#endif
}
player->opl.cardsOP2[0]->generate32(out_buf, (size_t)in_generatedStereo);
else if(n_periodCountStereo > 0)
{
/* Generate data from every chip and mix result */
for(unsigned card = 0; card < chips; ++card)
{
#ifdef ADLMIDI_USE_DOSBOX_OPL
player->opl.cards[card].GenerateArrMix(out_buf, &in_generatedStereo);
in_generatedPhys = in_generatedStereo * 2;
#else
OPL3_GenerateStreamMix(&player->opl.cards[card], out_buf, static_cast<Bit32u>(in_generatedStereo));
#endif
}
player->opl.cardsOP2[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo);
}
/* Process it */
SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out);
if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1)
return 0;
left -= (int)in_generatedPhys;
gotten_len += (in_generatedPhys) /* - setup.stored_samples*/;

View file

@ -30,13 +30,14 @@ extern "C" {
#define ADLMIDI_VERSION_MAJOR 1
#define ADLMIDI_VERSION_MINOR 3
#define ADLMIDI_VERSION_PATCHLEVEL 2
#define ADLMIDI_VERSION_PATCHLEVEL 3
#define ADLMIDI_TOSTR(s) #s
#define ADLMIDI_TOSTR_I(s) #s
#define ADLMIDI_TOSTR(s) ADLMIDI_TOSTR_I(s)
#define ADLMIDI_VERSION \
ADLMIDI_TOSTR(OPNMIDI_VERSION_MAJOR) "." \
ADLMIDI_TOSTR(OPNMIDI_VERSION_MINOR) "." \
ADLMIDI_TOSTR(OPNMIDI_VERSION_PATCHLEVEL)
ADLMIDI_TOSTR(ADLMIDI_VERSION_MAJOR) "." \
ADLMIDI_TOSTR(ADLMIDI_VERSION_MINOR) "." \
ADLMIDI_TOSTR(ADLMIDI_VERSION_PATCHLEVEL)
#include <stddef.h>
@ -57,11 +58,34 @@ typedef short ADL_SInt16;
enum ADLMIDI_VolumeModels
{
ADLMIDI_VolumeModel_AUTO = 0,
ADLMIDI_VolumeModel_Generic,
ADLMIDI_VolumeModel_CMF,
ADLMIDI_VolumeModel_DMX,
ADLMIDI_VolumeModel_APOGEE,
ADLMIDI_VolumeModel_9X
ADLMIDI_VolumeModel_Generic = 1,
ADLMIDI_VolumeModel_NativeOPL3 = 2,
ADLMIDI_VolumeModel_CMF = ADLMIDI_VolumeModel_NativeOPL3,
ADLMIDI_VolumeModel_DMX = 3,
ADLMIDI_VolumeModel_APOGEE = 4,
ADLMIDI_VolumeModel_9X = 5
};
enum ADLMIDI_SampleType
{
ADLMIDI_SampleType_S16 = 0, /* signed PCM 16-bit */
ADLMIDI_SampleType_S8, /* signed PCM 8-bit */
ADLMIDI_SampleType_F32, /* float 32-bit */
ADLMIDI_SampleType_F64, /* float 64-bit */
ADLMIDI_SampleType_S24, /* signed PCM 24-bit */
ADLMIDI_SampleType_S32, /* signed PCM 32-bit */
ADLMIDI_SampleType_U8, /* unsigned PCM 8-bit */
ADLMIDI_SampleType_U16, /* unsigned PCM 16-bit */
ADLMIDI_SampleType_U24, /* unsigned PCM 24-bit */
ADLMIDI_SampleType_U32, /* unsigned PCM 32-bit */
ADLMIDI_SampleType_Count,
};
struct ADLMIDI_AudioFormat
{
enum ADLMIDI_SampleType type; /* type of sample */
unsigned containerSize; /* size in bytes of the storage type */
unsigned sampleOffset; /* distance in bytes between consecutive samples */
};
struct ADL_MIDIPlayer
@ -87,6 +111,46 @@ extern int adl_getBanksCount();
/* Returns pointer to array of names of every bank */
extern const char *const *adl_getBankNames();
/* Reference to dynamic bank */
typedef struct ADL_Bank
{
void *pointer[3];
} ADL_Bank;
/* Identifier of dynamic bank */
typedef struct ADL_BankId
{
ADL_UInt8 percussive, msb, lsb;
} ADL_BankId;
/* Flags for dynamic bank access */
enum ADL_BankAccessFlags
{
ADLMIDI_Bank_Create = 1, /* create bank, allocating memory as needed */
ADLMIDI_Bank_CreateRt = 1|2, /* create bank, never allocating memory */
};
typedef struct ADL_Instrument ADL_Instrument;
#if defined(ADLMIDI_UNSTABLE_API)
/* Preallocates a minimum number of bank slots. Returns the actual capacity. */
extern int adl_reserveBanks(struct ADL_MIDIPlayer *device, unsigned banks);
/* Gets the bank designated by the identifier, optionally creating if it does not exist. */
extern int adl_getBank(struct ADL_MIDIPlayer *device, const ADL_BankId *id, int flags, ADL_Bank *bank);
/* Gets the identifier of a bank. */
extern int adl_getBankId(struct ADL_MIDIPlayer *device, const ADL_Bank *bank, ADL_BankId *id);
/* Removes a bank. */
extern int adl_removeBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank);
/* Gets the first bank. */
extern int adl_getFirstBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank);
/* Iterates to the next bank. */
extern int adl_getNextBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank);
/* Gets the nth intrument in the bank [0..127]. */
extern int adl_getInstrument(struct ADL_MIDIPlayer *device, const ADL_Bank *bank, unsigned index, ADL_Instrument *ins);
/* Sets the nth intrument in the bank [0..127]. */
extern int adl_setInstrument(struct ADL_MIDIPlayer *device, ADL_Bank *bank, unsigned index, const ADL_Instrument *ins);
#endif /* defined(ADLMIDI_UNSTABLE_API) */
/*Sets number of 4-operator channels between all chips.
By default, it is automatically re-calculating every bank change.
If you want to specify custom number of four operator channels,
@ -109,10 +173,16 @@ extern void adl_setHTremolo(struct ADL_MIDIPlayer *device, int htremo);
/*Override Enable(1) or Disable(0) scaling of modulator volumes. -1 - use bank default scaling of modulator volumes*/
extern void adl_setScaleModulators(struct ADL_MIDIPlayer *device, int smod);
/*Enable(1) or Disable(0) full-range brightness (MIDI CC74 used in XG music to filter result sounding) scaling.
By default, brightness affects sound between 0 and 64.
When this option is enabled, the range will use a full range from 0 up to 127.
*/
extern void adl_setFullRangeBrightness(struct ADL_MIDIPlayer *device, int fr_brightness);
/*Enable or disable built-in loop (built-in loop supports 'loopStart' and 'loopEnd' tags to loop specific part)*/
extern void adl_setLoopEnabled(struct ADL_MIDIPlayer *device, int loopEn);
/*Enable or disable Logariphmic volume changer */
/* !!!DEPRECATED!!! Enable or disable Logariphmic volume changer */
extern void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol);
/*Set different volume range model */
@ -125,15 +195,33 @@ extern int adl_openBankFile(struct ADL_MIDIPlayer *device, const char *filePath)
extern int adl_openBankData(struct ADL_MIDIPlayer *device, const void *mem, unsigned long size);
/*Returns name of currently used OPL3 emulator*/
/* DEPRECATED */
extern const char *adl_emulatorName();
/*Returns chip emulator name string*/
extern const char *adl_chipEmulatorName(struct ADL_MIDIPlayer *device);
enum ADL_Emulator
{
ADLMIDI_EMU_NUKED = 0,
ADLMIDI_EMU_NUKED_174,
ADLMIDI_EMU_DOSBOX,
ADLMIDI_EMU_end
};
/* Switch the emulation core */
extern int adl_switchEmulator(struct ADL_MIDIPlayer *device, int emulator);
typedef struct {
ADL_UInt16 major;
ADL_UInt16 minor;
ADL_UInt16 patch;
} ADL_Version;
/*Run emulator with PCM rate to reduce CPU usage on slow devices. May decrease sounding accuracy.*/
extern int adl_setRunAtPcmRate(struct ADL_MIDIPlayer *device, int enabled);
/*Returns string which contains a version number*/
extern const char *adl_linkedLibraryVersion();
@ -213,11 +301,17 @@ extern struct Adl_MarkerEntry adl_metaMarker(struct ADL_MIDIPlayer *device, size
/*Take a sample buffer and iterate MIDI timers */
extern int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short out[]);
extern int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short *out);
/*Take a sample buffer and iterate MIDI timers */
extern int adl_playFormat(struct ADL_MIDIPlayer *device, int sampleCount, ADL_UInt8 *left, ADL_UInt8 *right, const struct ADLMIDI_AudioFormat *format);
/*Generate audio output from chip emulators without iteration of MIDI timers.*/
extern int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, short *out);
/*Generate audio output from chip emulators without iteration of MIDI timers.*/
extern int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleCount, ADL_UInt8 *left, ADL_UInt8 *right, const struct ADLMIDI_AudioFormat *format);
/**
* @brief Periodic tick handler.
* @param device
@ -286,6 +380,70 @@ extern void adl_setNoteHook(struct ADL_MIDIPlayer *device, ADL_NoteHook noteHook
/* Set debug message hook */
extern void adl_setDebugMessageHook(struct ADL_MIDIPlayer *device, ADL_DebugMessageHook debugMessageHook, void *userData);
/**Instrument structures**/
enum
{
ADLMIDI_InstrumentVersion = 0,
};
typedef enum ADL_InstrumentFlags
{
/* Is two-operator single-voice instrument (no flags) */
ADLMIDI_Ins_2op = 0x00,
/* Is true four-operator instrument */
ADLMIDI_Ins_4op = 0x01,
/* Is pseudo four-operator (two 2-operator voices) instrument */
ADLMIDI_Ins_Pseudo4op = 0x02,
/* Is a blank instrument entry */
ADLMIDI_Ins_IsBlank = 0x04,
/* Mask of the flags range */
ADLMIDI_Ins_ALL_MASK = 0x07,
} ADL_InstrumentFlags;
typedef struct ADL_Operator
{
/* AM/Vib/Env/Ksr/FMult characteristics */
ADL_UInt8 avekf_20;
/* Key Scale Level / Total level register data */
ADL_UInt8 ksl_l_40;
/* Attack / Decay */
ADL_UInt8 atdec_60;
/* Systain and Release register data */
ADL_UInt8 susrel_80;
/* Wave form */
ADL_UInt8 waveform_E0;
} ADL_Operator;
typedef struct ADL_Instrument
{
/* Version of the instrument object */
int version;
/* MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */
ADL_SInt16 note_offset1;
/* MIDI note key (half-tone) offset for a second voice in pseudo-4-op mode */
ADL_SInt16 note_offset2;
/* MIDI note velocity offset (taken from Apogee TMB format) */
ADL_SInt8 midi_velocity_offset;
/* Second voice detune level (taken from DMX OP2) */
ADL_SInt8 second_voice_detune;
/* Percussion MIDI base tone number at which this drum will be played */
ADL_UInt8 percussion_key_number;
/* Enum ADL_InstrumentFlags */
ADL_UInt8 inst_flags;
/* Feedback&Connection register for first and second operators */
ADL_UInt8 fb_conn1_C0;
/* Feedback&Connection register for third and fourth operators */
ADL_UInt8 fb_conn2_C0;
/* Operators register data */
ADL_Operator operators[4];
/* Millisecond delay of sounding while key is on */
ADL_UInt16 delay_on_ms;
/* Millisecond delay of sounding after key off */
ADL_UInt16 delay_off_ms;
} ADL_Instrument;
#ifdef __cplusplus
}
#endif

View file

@ -24,24 +24,15 @@
#ifndef ADLMIDI_HPP
#define ADLMIDI_HPP
#include "adlmidi.h"
#include <stdint.h>
#include <vector>
class OPL3;
class MIDIplay;
struct ADL_MIDIPlayer;
class AdlInstrumentTester
{
uint32_t cur_gm;
uint32_t ins_idx;
std::vector<uint32_t> adl_ins_list;
OPL3 *opl;
MIDIplay * play;
struct Impl;
Impl *P;
public:
AdlInstrumentTester(ADL_MIDIPlayer *device);
explicit AdlInstrumentTester(ADL_MIDIPlayer *device);
virtual ~AdlInstrumentTester();
// Find list of adlib instruments that supposedly implement this GM
@ -51,6 +42,10 @@ public:
void NextGM(int offset);
void NextAdl(int offset);
bool HandleInputChar(char ch);
private:
AdlInstrumentTester(const AdlInstrumentTester &);
AdlInstrumentTester &operator=(const AdlInstrumentTester &);
};
#endif //ADLMIDI_HPP

View file

@ -0,0 +1,127 @@
/*
* 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) 2015-2018 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_BANKMAP_H
#define ADLMIDI_BANKMAP_H
#include <list>
#include <utility>
#include <stdint.h>
#include <stddef.h>
#include "adlmidi_ptr.hpp"
/**
* A simple hash map which accepts bank numbers as keys, can be reserved to a
* fixed size, offers O(1) search and insertion, has a hash function to
* optimize for the worst case, and has some good cache locality properties.
*/
template <class T>
class BasicBankMap
{
public:
typedef uint16_t key_type; /* the bank identifier */
typedef T mapped_type;
typedef std::pair<key_type, T> value_type;
BasicBankMap();
void reserve(size_t capacity);
size_t size() const
{ return m_size; }
size_t capacity() const
{ return m_capacity; }
bool empty() const
{ return m_size == 0; }
class iterator;
iterator begin() const;
iterator end() const;
struct do_not_expand_t {};
iterator find(key_type key);
void erase(iterator it);
std::pair<iterator, bool> insert(const value_type &value);
std::pair<iterator, bool> insert(const value_type &value, do_not_expand_t);
void clear();
T &operator[](key_type key);
private:
struct Slot;
enum { minimum_allocation = 4 };
enum
{
hash_bits = 8, /* worst case # of collisions: 128^2/2^hash_bits */
hash_buckets = 1 << hash_bits,
};
public:
class iterator
{
public:
iterator();
value_type &operator*() const { return slot->value; }
value_type *operator->() const { return &slot->value; }
iterator &operator++();
bool operator==(const iterator &o) const;
bool operator!=(const iterator &o) const;
void to_ptrs(void *ptrs[3]);
static iterator from_ptrs(void *const ptrs[3]);
private:
Slot **buckets;
Slot *slot;
size_t index;
iterator(Slot **buckets, Slot *slot, size_t index);
#ifdef _MSC_VER
template<class _T>
friend class BasicBankMap;
#else
friend class BasicBankMap<T>;
#endif
};
private:
struct Slot {
Slot *next, *prev;
value_type value;
Slot() : next(NULL), prev(NULL) {}
};
AdlMIDI_SPtrArray<Slot *> m_buckets;
std::list< AdlMIDI_SPtrArray<Slot> > m_allocations;
Slot *m_freeslots;
size_t m_size;
size_t m_capacity;
static size_t hash(key_type key);
Slot *allocate_slot();
Slot *ensure_allocate_slot();
void free_slot(Slot *slot);
Slot *bucket_find(size_t index, key_type key);
void bucket_add(size_t index, Slot *slot);
void bucket_remove(size_t index, Slot *slot);
};
#include "adlmidi_bankmap.tcc"
#endif // ADLMIDI_BANKMAP_H

View file

@ -0,0 +1,283 @@
/*
* 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) 2015-2018 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/>.
*/
#include "adlmidi_bankmap.h"
#include <cassert>
template <class T>
inline BasicBankMap<T>::BasicBankMap()
: m_freeslots(NULL),
m_size(0),
m_capacity(0)
{
m_buckets.reset(new Slot *[hash_buckets]());
}
template <class T>
inline size_t BasicBankMap<T>::hash(key_type key)
{
// disregard the 0 high bit in LSB
key = (key & 127) | ((key >> 8) << 7);
// take low part as hash value
return key & (hash_buckets - 1);
}
template <class T>
void BasicBankMap<T>::reserve(size_t capacity)
{
if(m_capacity >= capacity)
return;
size_t need = capacity - m_capacity;
const size_t minalloc = static_cast<size_t>(minimum_allocation);
need = (need < minalloc) ? minalloc : need;
AdlMIDI_SPtrArray<Slot> slotz;
slotz.reset(new Slot[need]);
m_allocations.push_back(slotz);
m_capacity += need;
for(size_t i = need; i-- > 0;)
free_slot(&slotz[i]);
}
template <class T>
typename BasicBankMap<T>::iterator
BasicBankMap<T>::begin() const
{
iterator it(m_buckets.get(), NULL, 0);
while(it.index < hash_buckets && !(it.slot = m_buckets[it.index]))
++it.index;
return it;
}
template <class T>
typename BasicBankMap<T>::iterator
BasicBankMap<T>::end() const
{
iterator it(m_buckets.get(), NULL, hash_buckets);
return it;
}
template <class T>
typename BasicBankMap<T>::iterator BasicBankMap<T>::find(key_type key)
{
size_t index = hash(key);
Slot *slot = bucket_find(index, key);
if(!slot)
return end();
return iterator(m_buckets.get(), slot, index);
}
template <class T>
void BasicBankMap<T>::erase(iterator it)
{
bucket_remove(it.index, it.slot);
free_slot(it.slot);
--m_size;
}
template <class T>
inline BasicBankMap<T>::iterator::iterator()
: buckets(NULL), slot(NULL), index(0)
{
}
template <class T>
inline BasicBankMap<T>::iterator::iterator(Slot **buckets, Slot *slot, size_t index)
: buckets(buckets), slot(slot), index(index)
{
}
template <class T>
typename BasicBankMap<T>::iterator &
BasicBankMap<T>::iterator::operator++()
{
if(slot->next)
slot = slot->next;
else {
Slot *slot = NULL;
++index;
while(index < hash_buckets && !(slot = buckets[index]))
++index;
this->slot = slot;
}
return *this;
}
template <class T>
bool BasicBankMap<T>::iterator::operator==(const iterator &o) const
{
return buckets == o.buckets && slot == o.slot && index == o.index;
}
template <class T>
inline bool BasicBankMap<T>::iterator::operator!=(const iterator &o) const
{
return !operator==(o);
}
template <class T>
void BasicBankMap<T>::iterator::to_ptrs(void *ptrs[3])
{
ptrs[0] = buckets;
ptrs[1] = slot;
ptrs[2] = (void *)index;
}
template <class T>
typename BasicBankMap<T>::iterator
BasicBankMap<T>::iterator::from_ptrs(void *const ptrs[3])
{
iterator it;
it.buckets = (Slot **)ptrs[0];
it.slot = (Slot *)ptrs[1];
it.index = (size_t)ptrs[2];
return it;
}
template <class T>
std::pair<typename BasicBankMap<T>::iterator, bool>
BasicBankMap<T>::insert(const value_type &value)
{
size_t index = hash(value.first);
Slot *slot = bucket_find(index, value.first);
if(slot)
return std::make_pair(iterator(m_buckets.get(), slot, index), false);
slot = allocate_slot();
if(!slot) {
reserve(m_capacity + minimum_allocation);
slot = ensure_allocate_slot();
}
slot->value = value;
bucket_add(index, slot);
++m_size;
return std::make_pair(iterator(m_buckets.get(), slot, index), true);
}
template <class T>
std::pair<typename BasicBankMap<T>::iterator, bool>
BasicBankMap<T>::insert(const value_type &value, do_not_expand_t)
{
size_t index = hash(value.first);
Slot *slot = bucket_find(index, value.first);
if(slot)
return std::make_pair(iterator(m_buckets.get(), slot, index), false);
slot = allocate_slot();
if(!slot)
return std::make_pair(end(), false);
slot->value = value;
bucket_add(index, slot);
++m_size;
return std::make_pair(iterator(m_buckets.get(), slot, index), true);
}
template <class T>
void BasicBankMap<T>::clear()
{
for(size_t i = 0; i < hash_buckets; ++i) {
Slot *slot = m_buckets[i];
while (Slot *cur = slot) {
slot = slot->next;
free_slot(cur);
}
m_buckets[i] = NULL;
}
m_size = 0;
}
template <class T>
inline T &BasicBankMap<T>::operator[](key_type key)
{
return insert(value_type(key, T())).first->second;
}
template <class T>
typename BasicBankMap<T>::Slot *
BasicBankMap<T>::allocate_slot()
{
Slot *slot = m_freeslots;
if(!slot)
return NULL;
Slot *next = slot->next;
if(next)
next->prev = NULL;
m_freeslots = next;
return slot;
}
template <class T>
inline typename BasicBankMap<T>::Slot *
BasicBankMap<T>::ensure_allocate_slot()
{
Slot *slot = allocate_slot();
assert(slot);
return slot;
}
template <class T>
void BasicBankMap<T>::free_slot(Slot *slot)
{
Slot *next = m_freeslots;
if(next)
next->prev = slot;
slot->prev = NULL;
slot->next = next;
m_freeslots = slot;
m_freeslots->value.second = T();
}
template <class T>
typename BasicBankMap<T>::Slot *
BasicBankMap<T>::bucket_find(size_t index, key_type key)
{
Slot *slot = m_buckets[index];
while(slot && slot->value.first != key)
slot = slot->next;
return slot;
}
template <class T>
void BasicBankMap<T>::bucket_add(size_t index, Slot *slot)
{
assert(slot);
Slot *next = m_buckets[index];
if(next)
next->prev = slot;
slot->next = next;
m_buckets[index] = slot;
}
template <class T>
void BasicBankMap<T>::bucket_remove(size_t index, Slot *slot)
{
assert(slot);
Slot *prev = slot->prev;
Slot *next = slot->next;
if(!prev)
m_buckets[index] = next;
else
prev->next = next;
if(next)
next->prev = prev;
}

View file

@ -22,6 +22,7 @@
*/
#include "adlmidi_private.hpp"
#include "wopl/wopl_file.h"
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
# ifndef ADLMIDI_DISABLE_MUS_SUPPORT
@ -32,6 +33,7 @@
# endif//XMI
#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
uint64_t MIDIplay::ReadBEint(const void *buffer, size_t nbytes)
{
uint64_t result = 0;
@ -54,6 +56,8 @@ uint64_t MIDIplay::ReadLEint(const void *buffer, size_t nbytes)
return result;
}
#endif
bool MIDIplay::LoadBank(const std::string &filename)
{
fileReader file;
@ -68,282 +72,223 @@ bool MIDIplay::LoadBank(const void *data, size_t size)
return LoadBank(file);
}
/* WOPL-needed misc functions */
static uint16_t toUint16LE(const uint8_t *arr)
template <class WOPLI>
static void cvt_generic_to_FMIns(adlinsdata2 &ins, const WOPLI &in)
{
uint16_t num = arr[0];
num |= ((arr[1] << 8) & 0xFF00);
return num;
}
static uint16_t toUint16BE(const uint8_t *arr)
{
uint16_t num = arr[1];
num |= ((arr[0] << 8) & 0xFF00);
return num;
}
static int16_t toSint16BE(const uint8_t *arr)
{
int16_t num = *reinterpret_cast<const int8_t *>(&arr[0]);
num *= 1 << 8;
num |= arr[1];
return num;
}
static const char *wopl3_magic = "WOPL3-BANK\0";
static const uint16_t wopl_latest_version = 3;
#define WOPL_INST_SIZE_V2 62
#define WOPL_INST_SIZE_V3 66
enum WOPL_InstrumentFlags
{
WOPL_Flags_NONE = 0,
WOPL_Flag_Enable4OP = 0x01,
WOPL_Flag_Pseudo4OP = 0x02,
WOPL_Flag_NoSound = 0x04,
};
struct WOPL_Inst
{
bool fourOps;
char padding[7];
struct adlinsdata adlins;
struct adldata op[2];
uint16_t ms_sound_kon;
uint16_t ms_sound_koff;
};
static bool readInstrument(MIDIplay::fileReader &file, WOPL_Inst &ins, uint16_t &version, bool isPercussion = false)
{
uint8_t idata[WOPL_INST_SIZE_V3];
if(version >= 3)
{
if(file.read(idata, 1, WOPL_INST_SIZE_V3) != WOPL_INST_SIZE_V3)
return false;
}
else
{
if(file.read(idata, 1, WOPL_INST_SIZE_V2) != WOPL_INST_SIZE_V2)
return false;
}
//strncpy(ins.name, char_p(idata), 32);
ins.op[0].finetune = (int8_t)toSint16BE(idata + 32);
ins.op[1].finetune = (int8_t)toSint16BE(idata + 34);
//ins.velocity_offset = int8_t(idata[36]);
ins.adlins.voice2_fine_tune = 0.0;
int8_t voice2_fine_tune = int8_t(idata[37]);
ins.voice2_fine_tune = 0.0;
int8_t voice2_fine_tune = in.second_voice_detune;
if(voice2_fine_tune != 0)
{
if(voice2_fine_tune == 1)
ins.adlins.voice2_fine_tune = 0.000025;
ins.voice2_fine_tune = 0.000025;
else if(voice2_fine_tune == -1)
ins.adlins.voice2_fine_tune = -0.000025;
ins.voice2_fine_tune = -0.000025;
else
ins.adlins.voice2_fine_tune = ((voice2_fine_tune * 15.625) / 1000.0);
ins.voice2_fine_tune = voice2_fine_tune * (15.625 / 1000.0);
}
ins.adlins.tone = isPercussion ? idata[38] : 0;
ins.tone = in.percussion_key_number;
ins.flags = (in.inst_flags & WOPL_Ins_4op) && (in.inst_flags & WOPL_Ins_Pseudo4op) ? adlinsdata::Flag_Pseudo4op : 0;
ins.flags|= (in.inst_flags & WOPL_Ins_4op) && ((in.inst_flags & WOPL_Ins_Pseudo4op) == 0) ? adlinsdata::Flag_Real4op : 0;
ins.flags|= (in.inst_flags & WOPL_Ins_IsBlank) ? adlinsdata::Flag_NoSound : 0;
uint8_t flags = idata[39];
ins.adlins.flags = (flags & WOPL_Flag_Enable4OP) && (flags & WOPL_Flag_Pseudo4OP) ? adlinsdata::Flag_Pseudo4op : 0;
ins.adlins.flags|= (flags & WOPL_Flag_NoSound) ? adlinsdata::Flag_NoSound : 0;
ins.fourOps = (flags & WOPL_Flag_Enable4OP) || (flags & WOPL_Flag_Pseudo4OP);
ins.op[0].feedconn = (idata[40]);
ins.op[1].feedconn = (idata[41]);
for(size_t op = 0, slt = 0; op < 4; op++, slt++)
bool fourOps = (in.inst_flags & WOPL_Ins_4op) || (in.inst_flags & WOPL_Ins_Pseudo4op);
for(size_t op = 0, slt = 0; op < static_cast<size_t>(fourOps ? 4 : 2); op++, slt++)
{
size_t off = 42 + size_t(op) * 5;
// ins.setAVEKM(op, idata[off + 0]);//AVEKM
// ins.setAtDec(op, idata[off + 2]);//AtDec
// ins.setSusRel(op, idata[off + 3]);//SusRel
// ins.setWaveForm(op, idata[off + 4]);//WaveForm
// ins.setKSLL(op, idata[off + 1]);//KSLL
ins.op[slt].carrier_E862 =
((static_cast<uint32_t>(idata[off + 4]) << 24) & 0xFF000000) //WaveForm
| ((static_cast<uint32_t>(idata[off + 3]) << 16) & 0x00FF0000) //SusRel
| ((static_cast<uint32_t>(idata[off + 2]) << 8) & 0x0000FF00) //AtDec
| ((static_cast<uint32_t>(idata[off + 0]) << 0) & 0x000000FF); //AVEKM
ins.op[slt].carrier_40 = idata[off + 1];//KSLL
ins.adl[slt].carrier_E862 =
((static_cast<uint32_t>(in.operators[op].waveform_E0) << 24) & 0xFF000000) //WaveForm
| ((static_cast<uint32_t>(in.operators[op].susrel_80) << 16) & 0x00FF0000) //SusRel
| ((static_cast<uint32_t>(in.operators[op].atdec_60) << 8) & 0x0000FF00) //AtDec
| ((static_cast<uint32_t>(in.operators[op].avekf_20) << 0) & 0x000000FF); //AVEKM
ins.adl[slt].carrier_40 = in.operators[op].ksl_l_40;//KSLL
op++;
off = 42 + size_t(op) * 5;
ins.op[slt].modulator_E862 =
((static_cast<uint32_t>(idata[off + 4]) << 24) & 0xFF000000) //WaveForm
| ((static_cast<uint32_t>(idata[off + 3]) << 16) & 0x00FF0000) //SusRel
| ((static_cast<uint32_t>(idata[off + 2]) << 8) & 0x0000FF00) //AtDec
| ((static_cast<uint32_t>(idata[off + 0]) << 0) & 0x000000FF); //AVEKM
ins.op[slt].modulator_40 = idata[off + 1];//KSLL
ins.adl[slt].modulator_E862 =
((static_cast<uint32_t>(in.operators[op].waveform_E0) << 24) & 0xFF000000) //WaveForm
| ((static_cast<uint32_t>(in.operators[op].susrel_80) << 16) & 0x00FF0000) //SusRel
| ((static_cast<uint32_t>(in.operators[op].atdec_60) << 8) & 0x0000FF00) //AtDec
| ((static_cast<uint32_t>(in.operators[op].avekf_20) << 0) & 0x000000FF); //AVEKM
ins.adl[slt].modulator_40 = in.operators[op].ksl_l_40;//KSLL
}
if(version >= 3)
ins.adl[0].finetune = static_cast<int8_t>(in.note_offset1);
ins.adl[0].feedconn = in.fb_conn1_C0;
if(!fourOps)
ins.adl[1] = ins.adl[0];
else
{
ins.ms_sound_kon = toUint16BE(idata + 62);
ins.ms_sound_koff = toUint16BE(idata + 64);
ins.adl[1].finetune = static_cast<int8_t>(in.note_offset2);
ins.adl[1].feedconn = in.fb_conn2_C0;
}
ins.ms_sound_kon = in.delay_on_ms;
ins.ms_sound_koff = in.delay_off_ms;
}
template <class WOPLI>
static void cvt_FMIns_to_generic(WOPLI &ins, const adlinsdata2 &in)
{
ins.second_voice_detune = 0;
double voice2_fine_tune = in.voice2_fine_tune;
if(voice2_fine_tune != 0)
{
if(voice2_fine_tune > 0 && voice2_fine_tune <= 0.000025)
ins.second_voice_detune = 1;
else if(voice2_fine_tune < 0 && voice2_fine_tune >= -0.000025)
ins.second_voice_detune = -1;
else
{
long value = lround(voice2_fine_tune * (1000.0 / 15.625));
value = (value < -128) ? -128 : value;
value = (value > +127) ? +127 : value;
ins.second_voice_detune = static_cast<int8_t>(value);
}
}
ins.percussion_key_number = in.tone;
bool fourOps = (in.flags & adlinsdata::Flag_Pseudo4op) || in.adl[0] != in.adl[1];
ins.inst_flags = fourOps ? WOPL_Ins_4op : 0;
ins.inst_flags|= (in.flags & adlinsdata::Flag_Pseudo4op) ? WOPL_Ins_Pseudo4op : 0;
ins.inst_flags|= (in.flags & adlinsdata::Flag_NoSound) ? WOPL_Ins_IsBlank : 0;
for(size_t op = 0, slt = 0; op < static_cast<size_t>(fourOps ? 4 : 2); op++, slt++)
{
ins.operators[op].waveform_E0 = static_cast<uint8_t>(in.adl[slt].carrier_E862 >> 24);
ins.operators[op].susrel_80 = static_cast<uint8_t>(in.adl[slt].carrier_E862 >> 16);
ins.operators[op].atdec_60 = static_cast<uint8_t>(in.adl[slt].carrier_E862 >> 8);
ins.operators[op].avekf_20 = static_cast<uint8_t>(in.adl[slt].carrier_E862 >> 0);
ins.operators[op].ksl_l_40 = in.adl[slt].carrier_40;
op++;
ins.operators[op].waveform_E0 = static_cast<uint8_t>(in.adl[slt].carrier_E862 >> 24);
ins.operators[op].susrel_80 = static_cast<uint8_t>(in.adl[slt].carrier_E862 >> 16);
ins.operators[op].atdec_60 = static_cast<uint8_t>(in.adl[slt].carrier_E862 >> 8);
ins.operators[op].avekf_20 = static_cast<uint8_t>(in.adl[slt].carrier_E862 >> 0);
ins.operators[op].ksl_l_40 = in.adl[slt].carrier_40;
}
ins.note_offset1 = in.adl[0].finetune;
ins.fb_conn1_C0 = in.adl[0].feedconn;
if(!fourOps)
{
ins.operators[2] = ins.operators[0];
ins.operators[3] = ins.operators[1];
}
else
{
ins.ms_sound_kon = 1000;
ins.ms_sound_koff = 500;
ins.note_offset2 = in.adl[1].finetune;
ins.fb_conn2_C0 = in.adl[1].feedconn;
}
return true;
ins.delay_on_ms = in.ms_sound_kon;
ins.delay_off_ms = in.ms_sound_koff;
}
void cvt_ADLI_to_FMIns(adlinsdata2 &ins, const ADL_Instrument &in)
{
return cvt_generic_to_FMIns(ins, in);
}
void cvt_FMIns_to_ADLI(ADL_Instrument &ins, const adlinsdata2 &in)
{
cvt_FMIns_to_generic(ins, in);
}
bool MIDIplay::LoadBank(MIDIplay::fileReader &fr)
{
int err = 0;
WOPLFile *wopl = NULL;
char *raw_file_data = NULL;
size_t fsize;
ADL_UNUSED(fsize);
if(!fr.isValid())
{
errorStringOut = "Custom bank: Invalid data stream!";
return false;
}
char magic[32];
std::memset(magic, 0, 32);
uint16_t version = 0;
uint16_t count_melodic_banks = 1;
uint16_t count_percusive_banks = 1;
if(fr.read(magic, 1, 11) != 11)
// Read complete bank file into the memory
fr.seek(0, SEEK_END);
fsize = fr.tell();
fr.seek(0, SEEK_SET);
// Allocate necessary memory block
raw_file_data = (char*)malloc(fsize);
if(!raw_file_data)
{
errorStringOut = "Custom bank: Can't read magic number!";
errorStringOut = "Custom bank: Out of memory before of read!";
return false;
}
fr.read(raw_file_data, 1, fsize);
if(std::strncmp(magic, wopl3_magic, 11) != 0)
// Parse bank file from the memory
wopl = WOPL_LoadBankFromMem((void*)raw_file_data, fsize, &err);
//Free the buffer no more needed
free(raw_file_data);
// Check for any erros
if(!wopl)
{
errorStringOut = "Custom bank: Invalid magic number!";
return false;
switch(err)
{
case WOPL_ERR_BAD_MAGIC:
errorStringOut = "Custom bank: Invalid magic!";
return false;
case WOPL_ERR_UNEXPECTED_ENDING:
errorStringOut = "Custom bank: Unexpected ending!";
return false;
case WOPL_ERR_INVALID_BANKS_COUNT:
errorStringOut = "Custom bank: Invalid banks count!";
return false;
case WOPL_ERR_NEWER_VERSION:
errorStringOut = "Custom bank: Version is newer than supported by this library!";
return false;
case WOPL_ERR_OUT_OF_MEMORY:
errorStringOut = "Custom bank: Out of memory!";
return false;
default:
errorStringOut = "Custom bank: Unknown error!";
return false;
}
}
uint8_t version_raw[2];
if(fr.read(version_raw, 1, 2) != 2)
{
errorStringOut = "Custom bank: Can't read version!";
return false;
}
version = toUint16LE(version_raw);
if(version > wopl_latest_version)
{
errorStringOut = "Custom bank: Unsupported WOPL version!";
return false;
}
uint8_t head[6];
std::memset(head, 0, 6);
if(fr.read(head, 1, 6) != 6)
{
errorStringOut = "Custom bank: Can't read header!";
return false;
}
count_melodic_banks = toUint16BE(head);
count_percusive_banks = toUint16BE(head + 2);
if((count_melodic_banks < 1) || (count_percusive_banks < 1))
{
errorStringOut = "Custom bank: Too few banks in this file!";
return false;
}
/*UNUSED YET*/
bool default_deep_vibrato = ((head[4]>>0) & 0x01);
bool default_deep_tremolo = ((head[4]>>1) & 0x01);
//5'th byte reserved for Deep-Tremolo and Deep-Vibrato flags
m_setup.HighTremoloMode = default_deep_tremolo;
m_setup.HighVibratoMode = default_deep_vibrato;
//6'th byte reserved for ADLMIDI's default volume model
m_setup.VolumeModel = (int)head[5];
opl.dynamic_melodic_banks.clear();
opl.dynamic_percussion_banks.clear();
opl.dynamic_bank_setup.adLibPercussions = false;
opl.dynamic_bank_setup.scaleModulators = false;
opl.dynamic_bank_setup.deepTremolo = (wopl->opl_flags & WOPL_FLAG_DEEP_TREMOLO) != 0;
opl.dynamic_bank_setup.deepVibrato = (wopl->opl_flags & WOPL_FLAG_DEEP_VIBRATO) != 0;
opl.dynamic_bank_setup.volumeModel = wopl->volume_model;
m_setup.HighTremoloMode = -1;
m_setup.HighVibratoMode = -1;
m_setup.VolumeModel = ADLMIDI_VolumeModel_AUTO;
opl.setEmbeddedBank(m_setup.AdlBank);
if(version >= 2)//Read bank meta-entries
uint16_t slots_counts[2] = {wopl->banks_count_melodic, wopl->banks_count_percussion};
WOPLBank *slots_src_ins[2] = { wopl->banks_melodic, wopl->banks_percussive };
for(unsigned ss = 0; ss < 2; ss++)
{
for(uint16_t i = 0; i < count_melodic_banks; i++)
for(unsigned i = 0; i < slots_counts[ss]; i++)
{
uint8_t bank_meta[34];
if(fr.read(bank_meta, 1, 34) != 34)
unsigned bankno =
(slots_src_ins[ss][i].bank_midi_msb * 256) +
slots_src_ins[ss][i].bank_midi_lsb +
(ss ? OPL3::PercussionTag : 0);
OPL3::Bank &bank = opl.dynamic_banks[bankno];
for(int j = 0; j < 128; j++)
{
errorStringOut = "Custom bank: Fail to read melodic bank meta-data!";
return false;
adlinsdata2 &ins = bank.ins[j];
std::memset(&ins, 0, sizeof(adlinsdata2));
WOPLInstrument &inIns = slots_src_ins[ss][i].ins[j];
cvt_generic_to_FMIns(ins, inIns);
}
uint16_t bank = uint16_t(bank_meta[33]) * 256 + uint16_t(bank_meta[32]);
size_t offset = opl.dynamic_melodic_banks.size();
opl.dynamic_melodic_banks[bank] = offset;
//strncpy(bankMeta.name, char_p(bank_meta), 32);
}
for(uint16_t i = 0; i < count_percusive_banks; i++)
{
uint8_t bank_meta[34];
if(fr.read(bank_meta, 1, 34) != 34)
{
errorStringOut = "Custom bank: Fail to read percussion bank meta-data!";
return false;
}
uint16_t bank = uint16_t(bank_meta[33]) * 256 + uint16_t(bank_meta[32]);
size_t offset = opl.dynamic_percussion_banks.size();
opl.dynamic_percussion_banks[bank] = offset;
//strncpy(bankMeta.name, char_p(bank_meta), 32);
}
}
uint16_t total = 128 * count_melodic_banks;
bool readPercussion = false;
tryAgain:
for(uint16_t i = 0; i < total; i++)
{
WOPL_Inst ins;
std::memset(&ins, 0, sizeof(WOPL_Inst));
if(!readInstrument(fr, ins, version, readPercussion))
{
opl.setEmbeddedBank(m_setup.AdlBank);
errorStringOut = "Custom bank: Fail to read instrument!";
return false;
}
ins.adlins.ms_sound_kon = ins.ms_sound_kon;
ins.adlins.ms_sound_koff = ins.ms_sound_koff;
ins.adlins.adlno1 = static_cast<uint16_t>(opl.dynamic_instruments.size() | opl.DynamicInstrumentTag);
opl.dynamic_instruments.push_back(ins.op[0]);
ins.adlins.adlno2 = ins.adlins.adlno1;
if(ins.fourOps)
{
ins.adlins.adlno2 = static_cast<uint16_t>(opl.dynamic_instruments.size() | opl.DynamicInstrumentTag);
opl.dynamic_instruments.push_back(ins.op[1]);
}
opl.dynamic_metainstruments.push_back(ins.adlins);
}
if(!readPercussion)
{
total = 128 * count_percusive_banks;
readPercussion = true;
goto tryAgain;
}
opl.AdlBank = ~0u; // Use dynamic banks!
//Percussion offset is count of instruments multipled to count of melodic banks
opl.dynamic_percussion_offset = 128 * count_melodic_banks;
applySetup();
WOPL_Free(wopl);
return true;
}
@ -373,7 +318,7 @@ bool MIDIplay::LoadMIDI(MIDIplay::fileReader &fr)
errorString.clear();
#ifdef DISABLE_EMBEDDED_BANKS
if((opl.AdlBank != ~0u) || (opl.dynamic_metainstruments.size() < 256))
if((opl.AdlBank != ~0u) || opl.dynamic_banks.empty())
{
errorStringOut = "Bank is not set! Please load any instruments bank by using of adl_openBankFile() or adl_openBankData() functions!";
return false;
@ -497,8 +442,7 @@ riffskip:
#endif //ADLMIDI_DISABLE_XMI_SUPPORT
else if(std::memcmp(HeaderBuf, "CTMF", 4) == 0)
{
opl.dynamic_instruments.clear();
opl.dynamic_metainstruments.clear();
opl.dynamic_banks.clear();
// Creative Music Format (CMF).
// When playing CTMF files, use the following commandline:
// adlmidi song8.ctmf -p -v 1 1 0
@ -520,13 +464,19 @@ riffskip:
//std::printf("%u instruments\n", ins_count);
for(unsigned i = 0; i < ins_count; ++i)
{
unsigned bank = i / 256;
bank = (bank & 127) + ((bank >> 7) << 8);
if(bank > 127 + (127 << 8))
break;
bank += (i % 256 < 128) ? 0 : OPL3::PercussionTag;
unsigned char InsData[16];
fr.read(InsData, 1, 16);
/*std::printf("Ins %3u: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
i, InsData[0],InsData[1],InsData[2],InsData[3], InsData[4],InsData[5],InsData[6],InsData[7],
InsData[8],InsData[9],InsData[10],InsData[11], InsData[12],InsData[13],InsData[14],InsData[15]);*/
struct adldata adl;
struct adlinsdata adlins;
adlinsdata2 &adlins = opl.dynamic_banks[bank].ins[i % 128];
adldata adl;
adl.modulator_E862 =
((static_cast<uint32_t>(InsData[8] & 0x07) << 24) & 0xFF000000) //WaveForm
| ((static_cast<uint32_t>(InsData[6]) << 16) & 0x00FF0000) //Sustain/Release
@ -541,15 +491,13 @@ riffskip:
adl.carrier_40 = InsData[3];
adl.feedconn = InsData[10] & 0x0F;
adl.finetune = 0;
adlins.adlno1 = static_cast<uint16_t>(opl.dynamic_instruments.size() | opl.DynamicInstrumentTag);
adlins.adlno2 = adlins.adlno1;
adlins.adl[0] = adl;
adlins.adl[1] = adl;
adlins.ms_sound_kon = 1000;
adlins.ms_sound_koff = 500;
adlins.tone = 0;
adlins.flags = 0;
adlins.voice2_fine_tune = 0.0;
opl.dynamic_metainstruments.push_back(adlins);
opl.dynamic_instruments.push_back(adl);
}
fr.seeku(mus_start, SEEK_SET);
@ -557,10 +505,9 @@ riffskip:
DeltaTicks = (size_t)ticks;
opl.AdlBank = ~0u; // Ignore AdlBank number, use dynamic banks instead
//std::printf("CMF deltas %u ticks %u, basictempo = %u\n", deltas, ticks, basictempo);
opl.LogarithmicVolumes = true;
opl.AdlPercussionMode = true;
opl.m_musicMode = OPL3::MODE_CMF;
opl.m_volumeScale = OPL3::VOLUME_CMF;
opl.m_volumeScale = OPL3::VOLUME_NATIVE;
}
else
{
@ -575,10 +522,9 @@ riffskip:
fr.seek(0x7D, SEEK_SET);
TrackCount = 1;
DeltaTicks = 60;
opl.LogarithmicVolumes = true;
//opl.CartoonersVolumes = true;
opl.m_musicMode = OPL3::MODE_RSXX;
opl.m_volumeScale = OPL3::VOLUME_CMF;
opl.m_volumeScale = OPL3::VOLUME_NATIVE;
}
}
@ -636,11 +582,11 @@ riffskip:
TrackData.clear();
TrackData.resize(TrackCount, std::vector<uint8_t>());
//CurrentPosition.track.clear();
//CurrentPosition.track.resize(TrackCount);
InvDeltaTicks = fraction<uint64_t>(1, 1000000l * static_cast<uint64_t>(DeltaTicks));
//Tempo = 1000000l * InvDeltaTicks;
Tempo = fraction<uint64_t>(1, static_cast<uint64_t>(DeltaTicks));
if(is_CMF || is_RSXX)
Tempo = fraction<uint64_t>(1, static_cast<uint64_t>(DeltaTicks));
else
Tempo = fraction<uint64_t>(1, static_cast<uint64_t>(DeltaTicks) * 2);
static const unsigned char EndTag[4] = {0xFF, 0x2F, 0x00, 0x00};
size_t totalGotten = 0;
@ -761,14 +707,14 @@ riffskip:
return false;
}
//Build new MIDI events table (ALPHA!!!)
//Build new MIDI events table
if(!buildTrackData())
{
errorStringOut = fr._fileName + ": MIDI data parsing error has occouped!\n" + errorString;
return false;
}
opl.Reset(m_setup.PCM_RATE); // Reset AdLib
opl.Reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPL3 chip
//opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously)
ch.clear();
ch.resize(opl.NumChannels);

File diff suppressed because it is too large Load diff

View file

@ -25,6 +25,21 @@
#ifdef ADLMIDI_HW_OPL
static const unsigned OPLBase = 0x388;
#else
# if defined(ADLMIDI_DISABLE_NUKED_EMULATOR) && defined(ADLMIDI_DISABLE_DOSBOX_EMULATOR)
# error "No emulators enabled. You must enable at least one emulator to use this library!"
# endif
// Nuked OPL3 emulator, Most accurate, but requires the powerful CPU
# ifndef ADLMIDI_DISABLE_NUKED_EMULATOR
# include "chips/nuked_opl3.h"
# include "chips/nuked_opl3_v174.h"
# endif
// DosBox 0.74 OPL3 emulator, Well-accurate and fast
# ifndef ADLMIDI_DISABLE_DOSBOX_EMULATOR
# include "chips/dosbox_opl3.h"
# endif
#endif
#ifdef DISABLE_EMBEDDED_BANKS
@ -114,87 +129,56 @@ static const unsigned short Channels[23] =
Ports: ???
*/
const adlinsdata &OPL3::GetAdlMetaIns(size_t n)
{
return (n & DynamicMetaInstrumentTag) ?
dynamic_metainstruments[n & ~DynamicMetaInstrumentTag]
: adlins[n];
}
size_t OPL3::GetAdlMetaNumber(size_t midiins)
{
return (AdlBank == ~0u) ?
(midiins | DynamicMetaInstrumentTag)
: banks[AdlBank][midiins];
}
const adldata &OPL3::GetAdlIns(size_t insno)
{
return (insno & DynamicInstrumentTag)
? dynamic_instruments[insno & ~DynamicInstrumentTag]
: adl[insno];
}
void OPL3::setEmbeddedBank(unsigned int bank)
{
AdlBank = bank;
//Embedded banks are supports 128:128 GM set only
dynamic_percussion_offset = 128;
dynamic_melodic_banks.clear();
dynamic_percussion_banks.clear();
dynamic_banks.clear();
if(bank >= static_cast<unsigned int>(maxAdlBanks()))
return;
Bank *bank_pair[2] =
{
&dynamic_banks[0],
&dynamic_banks[PercussionTag]
};
for(unsigned i = 0; i < 256; ++i)
{
size_t meta = banks[bank][i];
adlinsdata2 &ins = bank_pair[i / 128]->ins[i % 128];
ins = adlinsdata2(adlins[meta]);
}
}
static adlinsdata2 makeEmptyInstrument()
{
adlinsdata2 ins;
memset(&ins, 0, sizeof(adlinsdata2));
ins.flags = adlinsdata::Flag_NoSound;
return ins;
}
const adlinsdata2 OPL3::emptyInstrument = makeEmptyInstrument();
OPL3::OPL3() :
dynamic_percussion_offset(128),
DynamicInstrumentTag(0x8000u),
DynamicMetaInstrumentTag(0x4000000u),
NumCards(1),
AdlBank(0),
NumFourOps(0),
HighTremoloMode(false),
HighVibratoMode(false),
AdlPercussionMode(false),
LogarithmicVolumes(false),
//CartoonersVolumes(false),
m_musicMode(MODE_MIDI),
m_volumeScale(VOLUME_Generic)
{}
void OPL3::Poke(size_t card, uint32_t index, uint32_t value)
{
#ifdef ADLMIDI_HW_OPL
(void)card;
unsigned o = index >> 8;
unsigned port = OPLBase + o * 2;
#ifdef __DJGPP__
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);
#endif//__DJGPP__
#ifdef __WATCOMC__
outp(port, index);
for(uint16_t c = 0; c < 6; ++c) inp(port);
outp(port + 1, value);
for(uint16_t c = 0; c < 35; ++c) inp(port);
#endif//__WATCOMC__
#else//ADLMIDI_HW_OPL
#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//ADLMIDI_HW_OPL
#ifdef DISABLE_EMBEDDED_BANKS
AdlBank = ~0u;
#else
setEmbeddedBank(0);
#endif
}
void OPL3::PokeN(size_t card, uint16_t index, uint8_t value)
void OPL3::Poke(size_t card, uint16_t index, uint8_t value)
{
#ifdef ADLMIDI_HW_OPL
(void)card;
@ -216,11 +200,7 @@ void OPL3::PokeN(size_t card, uint16_t index, uint8_t value)
#endif//__WATCOMC__
#else
#ifdef ADLMIDI_USE_DOSBOX_OPL
cards[card].WriteReg(static_cast<Bit32u>(index), value);
#else
OPL3_WriteReg(&cards[card], index, value);
#endif
cardsOP2[card]->writeReg(index, value);
#endif
}
@ -277,10 +257,9 @@ void OPL3::Touch_Real(unsigned c, unsigned volume, uint8_t brightness)
volume = 63;
size_t card = c / 23, cc = c % 23;
size_t i = ins[c];
const adldata &adli = ins[c];
uint16_t o1 = Operators[cc * 2 + 0];
uint16_t o2 = Operators[cc * 2 + 1];
const adldata &adli = GetAdlIns(i);
uint8_t x = adli.modulator_40, y = adli.carrier_40;
uint16_t mode = 1; // 2-op AM
@ -290,22 +269,22 @@ void OPL3::Touch_Real(unsigned c, unsigned volume, uint8_t brightness)
}
else if(four_op_category[c] == 1 || four_op_category[c] == 2)
{
size_t i0, i1;
const adldata *i0, *i1;
if(four_op_category[c] == 1)
{
i0 = i;
i1 = ins[c + 3];
i0 = &adli;
i1 = &ins[c + 3];
mode = 2; // 4-op xx-xx ops 1&2
}
else
{
i0 = ins[c - 3];
i1 = i;
i0 = &ins[c - 3];
i1 = &adli;
mode = 6; // 4-op xx-xx ops 3&4
}
mode += (GetAdlIns(i0).feedconn & 1) + (GetAdlIns(i1).feedconn & 1) * 2;
mode += (i0->feedconn & 1) + (i1->feedconn & 1) * 2;
}
static const bool do_ops[10][2] =
@ -372,14 +351,13 @@ void OPL3::Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127
}
}*/
void OPL3::Patch(uint16_t c, size_t i)
void OPL3::Patch(uint16_t c, const adldata &adli)
{
uint16_t card = c / 23, cc = c % 23;
static const uint8_t data[4] = {0x20, 0x60, 0x80, 0xE0};
ins[c] = i;
ins[c] = adli;
uint16_t o1 = Operators[cc * 2 + 0];
uint16_t o2 = Operators[cc * 2 + 1];
const adldata &adli = GetAdlIns(i);
unsigned x = adli.modulator_E862, y = adli.carrier_E862;
for(unsigned a = 0; a < 4; ++a, x >>= 8, y >>= 8)
@ -395,7 +373,7 @@ void OPL3::Pan(unsigned c, unsigned value)
unsigned card = c / 23, cc = c % 23;
if(Channels[cc] != 0xFFF)
Poke(card, 0xC0 + Channels[cc], GetAdlIns(ins[c]).feedconn | value);
Poke(card, 0xC0 + Channels[cc], ins[c].feedconn | value);
}
void OPL3::Silence() // Silence all OPL channels.
@ -479,9 +457,8 @@ void OPL3::ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel)
m_volumeScale = OPL3::VOLUME_Generic;
break;
case ADLMIDI_VolumeModel_CMF:
LogarithmicVolumes = true;
m_volumeScale = OPL3::VOLUME_CMF;
case ADLMIDI_VolumeModel_NativeOPL3:
m_volumeScale = OPL3::VOLUME_NATIVE;
break;
case ADLMIDI_VolumeModel_DMX:
@ -498,26 +475,36 @@ void OPL3::ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel)
}
}
void OPL3::Reset(unsigned long PCM_RATE)
#ifndef ADLMIDI_HW_OPL
void OPL3::ClearChips()
{
#ifndef ADLMIDI_HW_OPL
#ifdef ADLMIDI_USE_DOSBOX_OPL
DBOPL::Handler emptyChip; //Constructors inside are will initialize necessary fields
#else
_opl3_chip emptyChip;
std::memset(&emptyChip, 0, sizeof(_opl3_chip));
#endif
cards.clear();
#endif
for(size_t i = 0; i < cardsOP2.size(); i++)
cardsOP2[i].reset(NULL);
cardsOP2.clear();
}
#endif
void OPL3::Reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler)
{
#ifndef ADLMIDI_HW_OPL
ClearChips();
#else
(void)emulator;
(void)PCM_RATE;
#endif
#if !defined(ADLMIDI_AUDIO_TICK_HANDLER)
(void)audioTickHandler;
#endif
ins.clear();
pit.clear();
regBD.clear();
#ifndef ADLMIDI_HW_OPL
cards.resize(NumCards, emptyChip);
#endif
#ifndef ADLMIDI_HW_OPL
cardsOP2.resize(NumCards, AdlMIDI_SPtr<OPLChipBase>());
#endif
NumChannels = NumCards * 23;
ins.resize(NumChannels, 189);
ins.resize(NumChannels, adl[adlDefaultNumber]);
pit.resize(NumChannels, 0);
regBD.resize(NumCards, 0);
four_op_category.resize(NumChannels, 0);
@ -536,24 +523,45 @@ void OPL3::Reset(unsigned long PCM_RATE)
};
unsigned fours = NumFourOps;
for(unsigned card = 0; card < NumCards; ++card)
for(size_t i = 0; i < NumCards; ++i)
{
#ifndef ADLMIDI_HW_OPL
# ifdef ADLMIDI_USE_DOSBOX_OPL
cards[card].Init(PCM_RATE);
# else
OPL3_Reset(&cards[card], static_cast<Bit32u>(PCM_RATE));
# endif
#endif
#ifndef ADLMIDI_HW_OPL
OPLChipBase *chip;
switch(emulator)
{
default:
#ifndef ADLMIDI_DISABLE_NUKED_EMULATOR
case ADLMIDI_EMU_NUKED: /* Latest Nuked OPL3 */
chip = new NukedOPL3;
break;
case ADLMIDI_EMU_NUKED_174: /* Old Nuked OPL3 1.4.7 modified and optimized */
chip = new NukedOPL3v174;
break;
#endif
#ifndef ADLMIDI_DISABLE_DOSBOX_EMULATOR
case ADLMIDI_EMU_DOSBOX:
chip = new DosBoxOPL3;
break;
#endif
}
cardsOP2[i].reset(chip);
chip->setChipId(i);
chip->setRate((uint32_t)PCM_RATE);
if(runAtPcmRate)
chip->setRunningAtPcmRate(true);
# if defined(ADLMIDI_AUDIO_TICK_HANDLER)
chip->setAudioTickHandlerInstance(audioTickHandler);
# endif
#endif // ADLMIDI_HW_OPL
for(unsigned a = 0; a < 18; ++a) Poke(card, 0xB0 + Channels[a], 0x00);
for(unsigned a = 0; a < 18; ++a) Poke(i, 0xB0 + Channels[a], 0x00);
for(unsigned a = 0; a < sizeof(data) / sizeof(*data); a += 2)
PokeN(card, data[a], static_cast<uint8_t>(data[a + 1]));
Poke(card, 0x0BD, regBD[card] = (HighTremoloMode * 0x80
+ HighVibratoMode * 0x40
+ AdlPercussionMode * 0x20));
Poke(i, data[a], static_cast<uint8_t>(data[a + 1]));
Poke(i, 0x0BD, regBD[i] = (HighTremoloMode * 0x80
+ HighVibratoMode * 0x40
+ AdlPercussionMode * 0x20));
unsigned fours_this_card = std::min(fours, 6u);
Poke(card, 0x104, (1 << fours_this_card) - 1);
Poke(i, 0x104, (1 << fours_this_card) - 1);
//fprintf(stderr, "Card %u: %u four-ops.\n", card, fours_this_card);
fours -= fours_this_card;
}

View file

@ -25,6 +25,13 @@
std::string ADLMIDI_ErrorString;
// Generator callback on audio rate ticks
void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate)
{
reinterpret_cast<MIDIplay *>(instance)->AudioTick(chipId, rate);
}
int adlRefreshNumCards(ADL_MIDIPlayer *device)
{
unsigned n_fourop[2] = {0, 0}, n_total[2] = {0, 0};
@ -34,19 +41,22 @@ int adlRefreshNumCards(ADL_MIDIPlayer *device)
if(play->opl.AdlBank == ~0u)
{
//For custom bank
for(size_t a = 0; a < play->opl.dynamic_metainstruments.size(); ++a)
OPL3::BankMap::iterator it = play->opl.dynamic_banks.begin();
OPL3::BankMap::iterator end = play->opl.dynamic_banks.end();
for(; it != end; ++it)
{
size_t div = (a >= play->opl.dynamic_percussion_offset) ? 1 : 0;
++n_total[div];
adlinsdata &ins = play->opl.dynamic_metainstruments[a];
if((ins.adlno1 != ins.adlno2) && ((ins.flags & adlinsdata::Flag_Pseudo4op) == 0))
++n_fourop[div];
uint16_t bank = it->first;
unsigned div = (bank & OPL3::PercussionTag) ? 1 : 0;
for(unsigned i = 0; i < 128; ++i)
{
adlinsdata2 &ins = it->second.ins[i];
if(ins.flags & adlinsdata::Flag_NoSound)
continue;
if((ins.adl[0] != ins.adl[1]) && ((ins.flags & adlinsdata::Flag_Pseudo4op) == 0))
++n_fourop[div];
++n_total[div];
}
}
play->m_setup.NumFourOps =
(n_fourop[0] >= 128 * 7 / 8) ? play->m_setup.NumCards * 6
: (n_fourop[0] < 128 * 1 / 8) ? (n_fourop[1] > 0 ? 4 : 0)
: (play->m_setup.NumCards == 1 ? 1 : play->m_setup.NumCards * 4);
}
else
{
@ -57,27 +67,34 @@ int adlRefreshNumCards(ADL_MIDIPlayer *device)
if(insno == 198)
continue;
++n_total[a / 128];
const adlinsdata &ins = adlins[insno];
if((ins.adlno1 != ins.adlno2) && ((ins.flags & adlinsdata::Flag_Pseudo4op) == 0))
adlinsdata2 ins(adlins[insno]);
if(ins.flags & adlinsdata::Flag_Real4op)
++n_fourop[a / 128];
}
play->m_setup.NumFourOps =
(n_fourop[0] >= (n_total[0] % 128) * 7 / 8) ? play->m_setup.NumCards * 6
: (n_fourop[0] < (n_total[0] % 128) * 1 / 8) ? 0
: (play->m_setup.NumCards == 1 ? 1 : play->m_setup.NumCards * 4);
}
play->opl.NumFourOps = play->m_setup.NumFourOps;
unsigned numFourOps = 0;
if(n_fourop[0] >= n_total[0] * 15 / 16 && play->m_setup.NumFourOps == 0)
{
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");
return -1;
}
// All 2ops (no 4ops)
if((n_fourop[0] == 0) && (n_fourop[1] == 0))
numFourOps = 0;
// All 2op melodics and Some (or All) 4op drums
else if((n_fourop[0] == 0) && (n_fourop[1] > 0))
numFourOps = 2;
// Many 4op melodics
else if((n_fourop[0] >= (n_total[0] * 7) / 8))
numFourOps = 6;
// Few 4op melodics
else if(n_fourop[0] > 0)
numFourOps = 4;
/* //Old formula
unsigned NumFourOps = ((n_fourop[0] == 0) && (n_fourop[1] == 0)) ? 0
: (n_fourop[0] >= (n_total[0] * 7) / 8) ? play->m_setup.NumCards * 6
: (play->m_setup.NumCards == 1 ? 1 : play->m_setup.NumCards * 4);
*/
play->opl.NumFourOps = play->m_setup.NumFourOps = (numFourOps * play->m_setup.NumCards);
return 0;
}

View file

@ -35,6 +35,9 @@
# endif
#endif
// Require declarations of unstable API for extern "C"
#define ADLMIDI_UNSTABLE_API
#ifdef _WIN32
#define NOMINMAX
#endif
@ -87,12 +90,14 @@ typedef int32_t ssize_t;
#include <cmath>
#include <cstdarg>
#include <cstdio>
#include <cassert>
#include <vector> // vector
#include <deque> // deque
#include <cmath> // exp, log, ceil
#if defined(__WATCOMC__)
#include <math.h> // round, sqrt
#endif
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits> // numeric_limit
@ -104,22 +109,31 @@ typedef int32_t ssize_t;
#include <deque>
#include <algorithm>
#ifdef _MSC_VER
#pragma warning(disable:4319)
#pragma warning(disable:4267)
#pragma warning(disable:4244)
#pragma warning(disable:4146)
#pragma warning(disable:4800)
/*
* Workaround for some compilers are has no those macros in their headers!
*/
#ifndef INT8_MIN
#define INT8_MIN (-0x7f - 1)
#endif
#ifndef INT16_MIN
#define INT16_MIN (-0x7fff - 1)
#endif
#ifndef INT32_MIN
#define INT32_MIN (-0x7fffffff - 1)
#endif
#ifndef INT8_MAX
#define INT8_MAX 0x7f
#endif
#ifndef INT16_MAX
#define INT16_MAX 0x7fff
#endif
#ifndef INT32_MAX
#define INT32_MAX 0x7fffffff
#endif
#include "fraction.hpp"
#ifndef ADLMIDI_HW_OPL
#ifdef ADLMIDI_USE_DOSBOX_OPL
#include "dbopl.h"
#else
#include "nukedopl3.h"
#endif
#include "chips/opl_chip_base.h"
#endif
#include "adldata.hh"
@ -127,46 +141,63 @@ typedef int32_t ssize_t;
#ifndef ADLMIDI_DISABLE_CPP_EXTRAS
#include "adlmidi.hpp" //Extra C++ API
#endif
#include "adlmidi_ptr.hpp"
#include "adlmidi_bankmap.h"
#define ADL_UNUSED(x) (void)x
#define OPL_PANNING_LEFT 0x10
#define OPL_PANNING_RIGHT 0x20
#define OPL_PANNING_BOTH 0x30
extern std::string ADLMIDI_ErrorString;
/*
Smart pointer for C heaps, created with malloc() call.
FAQ: Why not std::shared_ptr? Because of Android NDK now doesn't supports it
Sample conversions to various formats
*/
template<class PTR>
class AdlMIDI_CPtr
template <class Real>
inline Real adl_cvtReal(int32_t x)
{
PTR *m_p;
public:
AdlMIDI_CPtr() : m_p(NULL) {}
~AdlMIDI_CPtr()
{
reset(NULL);
}
return x * ((Real)1 / INT16_MAX);
}
void reset(PTR *p = NULL)
{
if(m_p)
free(m_p);
m_p = p;
}
inline int32_t adl_cvtS16(int32_t x)
{
x = (x < INT16_MIN) ? (INT16_MIN) : x;
x = (x > INT16_MAX) ? (INT16_MAX) : x;
return x;
}
PTR *get()
{
return m_p;
}
PTR &operator*()
{
return *m_p;
}
PTR *operator->()
{
return m_p;
}
};
inline int32_t adl_cvtS8(int32_t x)
{
return adl_cvtS16(x) / 256;
}
inline int32_t adl_cvtS24(int32_t x)
{
return adl_cvtS16(x) * 256;
}
inline int32_t adl_cvtS32(int32_t x)
{
return adl_cvtS16(x) * 65536;
}
inline int32_t adl_cvtU16(int32_t x)
{
return adl_cvtS16(x) - INT16_MIN;
}
inline int32_t adl_cvtU8(int32_t x)
{
return (adl_cvtS16(x) / 256) - INT8_MIN;
}
inline int32_t adl_cvtU24(int32_t x)
{
enum { int24_min = -(1 << 23) };
return adl_cvtS24(x) - int24_min;
}
inline int32_t adl_cvtU32(int32_t x)
{
// unsigned operation because overflow on signed integers is undefined
return (uint32_t)adl_cvtS32(x) - (uint32_t)INT32_MIN;
}
class MIDIplay;
struct ADL_MIDIPlayer;
@ -179,34 +210,26 @@ public:
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
std::vector<AdlMIDI_SPtr<OPLChipBase > > cardsOP2;
#endif
private:
std::vector<size_t> ins; // index to adl[], cached, needed by Touch()
std::vector<adldata> ins; // patch data, cached, needed by Touch()
std::vector<uint8_t> pit; // value poked to B0, cached, needed by NoteOff)(
std::vector<uint8_t> regBD;
friend int adlRefreshNumCards(ADL_MIDIPlayer *device);
//! Meta information about every instrument
std::vector<adlinsdata> dynamic_metainstruments; // Replaces adlins[] when CMF file
//! Raw instrument data ready to be sent to the chip
std::vector<adldata> dynamic_instruments; // Replaces adl[] when CMF file
size_t dynamic_percussion_offset;
typedef std::map<uint16_t, size_t> BankMap;
BankMap dynamic_melodic_banks;
BankMap dynamic_percussion_banks;
const unsigned DynamicInstrumentTag /* = 0x8000u*/,
DynamicMetaInstrumentTag /* = 0x4000000u*/;
const adlinsdata &GetAdlMetaIns(size_t n);
size_t GetAdlMetaNumber(size_t midiins);
const adldata &GetAdlIns(size_t insno);
public:
struct Bank
{
adlinsdata2 ins[128];
};
typedef BasicBankMap<Bank> BankMap;
BankMap dynamic_banks;
AdlBankSetup dynamic_bank_setup;
public:
void setEmbeddedBank(unsigned int bank);
static const adlinsdata2 emptyInstrument;
enum { PercussionTag = 1 << 15 };
//! Total number of running concurrent emulated chips
unsigned int NumCards;
@ -222,8 +245,10 @@ public:
bool AdlPercussionMode;
//! Carriers-only are scaled by default by volume level. This flag will tell to scale modulators too.
bool ScaleModulators;
//! Required to play CMF files. Can be turned on by using of "CMF" volume model
bool LogarithmicVolumes;
//! Run emulator at PCM rate if that possible. Reduces sounding accuracy, but decreases CPU usage on lower rates.
bool runAtPcmRate;
// ! Required to play CMF files. Can be turned on by using of "CMF" volume model
//bool LogarithmicVolumes; //[REPLACED WITH "m_volumeScale == VOLUME_NATIVE", DEPRECATED!!!]
// ! Required to play EA-MUS files [REPLACED WITH "m_musicMode", DEPRECATED!!!]
//bool CartoonersVolumes;
enum MusicMode
@ -239,7 +264,7 @@ public:
enum VolumesScale
{
VOLUME_Generic,
VOLUME_CMF,
VOLUME_NATIVE,
VOLUME_DMX,
VOLUME_APOGEE,
VOLUME_9X
@ -255,21 +280,23 @@ public:
// 7 = percussion Hihat
// 8 = percussion slave
void Poke(size_t card, uint32_t index, uint32_t value);
void PokeN(size_t card, uint16_t index, uint8_t value);
void Poke(size_t card, uint16_t index, uint8_t value);
void NoteOff(size_t c);
void NoteOn(unsigned c, double hertz);
void Touch_Real(unsigned c, unsigned volume, uint8_t brightness = 127);
//void Touch(unsigned c, unsigned volume)
void Patch(uint16_t c, size_t i);
void Patch(uint16_t c, const adldata &adli);
void Pan(unsigned c, unsigned value);
void Silence();
void updateFlags();
void updateDeepFlags();
void ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel);
void Reset(unsigned long PCM_RATE);
#ifndef ADLMIDI_HW_OPL
void ClearChips();
#endif
void Reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler);
};
@ -455,7 +482,7 @@ public:
bool eof()
{
if(fp)
return std::feof(fp);
return (std::feof(fp) != 0);
else
return mp_tell >= mp_size;
}
@ -473,9 +500,15 @@ public:
uint8_t bank_lsb, bank_msb;
uint8_t patch;
uint8_t volume, expression;
uint8_t panning, vibrato, sustain;
uint8_t panning, vibrato, aftertouch, sustain;
//! Per note Aftertouch values
uint8_t noteAftertouch[128];
//! Is note aftertouch has any non-zero value
bool noteAfterTouchInUse;
char ____padding[6];
double bend, bendsense;
int bend;
double bendsense;
int bendsense_lsb, bendsense_msb;
double vibpos, vibspeed, vibdepth;
int64_t vibdelay;
uint8_t lastlrpn, lastmrpn;
@ -484,66 +517,220 @@ public:
bool is_xg_percussion;
struct NoteInfo
{
uint8_t note;
bool active;
// Current pressure
uint8_t vol;
char ____padding[1];
// Note vibrato (a part of Note Aftertouch feature)
uint8_t vibrato;
// Tone selected on noteon:
int16_t tone;
char ____padding2[4];
// Patch selected on noteon; index to banks[AdlBank][]
// Patch selected on noteon; index to bank.ins[]
size_t midiins;
// Index to physical adlib data structure, adlins[]
size_t insmeta;
// Patch selected
const adlinsdata2 *ains;
enum
{
MaxNumPhysChans = 2,
MaxNumPhysItemCount = MaxNumPhysChans,
};
struct Phys
{
//! Destination chip channel
uint16_t chip_chan;
//! ins, inde to adl[]
size_t insId;
adldata ains;
//! Is this voice must be detunable?
bool pseudo4op;
void assign(const Phys &oth)
{
ains = oth.ains;
pseudo4op = oth.pseudo4op;
}
bool operator==(const Phys &oth) const
{
return (insId == oth.insId) && (pseudo4op == oth.pseudo4op);
return (ains == oth.ains) && (pseudo4op == oth.pseudo4op);
}
bool operator!=(const Phys &oth) const
{
return !operator==(oth);
}
};
typedef std::map<uint16_t, Phys> PhysMap;
// List of OPL3 channels it is currently occupying.
std::map<uint16_t /*adlchn*/, Phys> phys;
//! List of OPL3 channels it is currently occupying.
Phys chip_channels[MaxNumPhysItemCount];
//! Count of used channels.
unsigned chip_channels_count;
//
Phys *phys_find(unsigned chip_chan)
{
Phys *ph = NULL;
for(unsigned i = 0; i < chip_channels_count && !ph; ++i)
if(chip_channels[i].chip_chan == chip_chan)
ph = &chip_channels[i];
return ph;
}
Phys *phys_find_or_create(uint16_t chip_chan)
{
Phys *ph = phys_find(chip_chan);
if(!ph) {
if(chip_channels_count < MaxNumPhysItemCount) {
ph = &chip_channels[chip_channels_count++];
ph->chip_chan = chip_chan;
}
}
return ph;
}
Phys *phys_ensure_find_or_create(uint16_t chip_chan)
{
Phys *ph = phys_find_or_create(chip_chan);
assert(ph);
return ph;
}
void phys_erase_at(const Phys *ph)
{
intptr_t pos = ph - chip_channels;
assert(pos < static_cast<intptr_t>(chip_channels_count));
for(intptr_t i = pos + 1; i < static_cast<intptr_t>(chip_channels_count); ++i)
chip_channels[i - 1] = chip_channels[i];
--chip_channels_count;
}
void phys_erase(unsigned chip_chan)
{
Phys *ph = phys_find(chip_chan);
if(ph)
phys_erase_at(ph);
}
};
typedef std::map<uint8_t, NoteInfo> activenotemap_t;
typedef activenotemap_t::iterator activenoteiterator;
char ____padding2[5];
activenotemap_t activenotes;
NoteInfo activenotes[128];
struct activenoteiterator
{
explicit activenoteiterator(NoteInfo *info = 0)
: ptr(info) {}
activenoteiterator &operator++()
{
if(ptr->note == 127)
ptr = 0;
else
for(++ptr; ptr && !ptr->active;)
ptr = (ptr->note == 127) ? 0 : (ptr + 1);
return *this;
}
activenoteiterator operator++(int)
{
activenoteiterator pos = *this;
++*this;
return pos;
}
NoteInfo &operator*() const
{ return *ptr; }
NoteInfo *operator->() const
{ return ptr; }
bool operator==(activenoteiterator other) const
{ return ptr == other.ptr; }
bool operator!=(activenoteiterator other) const
{ return ptr != other.ptr; }
operator NoteInfo *() const
{ return ptr; }
private:
NoteInfo *ptr;
};
activenoteiterator activenotes_begin()
{
activenoteiterator it(activenotes);
return (it->active) ? it : ++it;
}
activenoteiterator activenotes_find(uint8_t note)
{
assert(note < 128);
return activenoteiterator(
activenotes[note].active ? &activenotes[note] : 0);
}
activenoteiterator activenotes_ensure_find(uint8_t note)
{
activenoteiterator it = activenotes_find(note);
assert(it);
return it;
}
std::pair<activenoteiterator, bool> activenotes_insert(uint8_t note)
{
assert(note < 128);
NoteInfo &info = activenotes[note];
bool inserted = !info.active;
if(inserted) info.active = true;
return std::pair<activenoteiterator, bool>(activenoteiterator(&info), inserted);
}
void activenotes_erase(activenoteiterator pos)
{
if(pos)
pos->active = false;
}
bool activenotes_empty()
{
return !activenotes_begin();
}
void activenotes_clear()
{
for(uint8_t i = 0; i < 128; ++i) {
activenotes[i].note = i;
activenotes[i].active = false;
}
}
void reset()
{
portamento = 0;
resetAllControllers();
patch = 0;
vibpos = 0;
bank_lsb = 0;
bank_msb = 0;
patch = 0;
volume = 100;
expression = 127;
panning = 0x30;
vibrato = 0;
sustain = 0;
bend = 0.0;
bendsense = 2 / 8192.0;
vibpos = 0;
vibspeed = 2 * 3.141592653 * 5.0;
vibdepth = 0.5 / 127;
vibdelay = 0;
lastlrpn = 0;
lastmrpn = 0;
nrpn = false;
brightness = 127;
is_xg_percussion = false;
}
MIDIchannel()
: activenotes()
void resetAllControllers()
{
bend = 0;
bendsense_msb = 2;
bendsense_lsb = 0;
updateBendSensitivity();
volume = 100;
expression = 127;
sustain = 0;
vibrato = 0;
aftertouch = 0;
std::memset(noteAftertouch, 0, 128);
noteAfterTouchInUse = false;
vibspeed = 2 * 3.141592653 * 5.0;
vibdepth = 0.5 / 127;
vibdelay = 0;
panning = OPL_PANNING_BOTH;
portamento = 0;
brightness = 127;
}
bool hasVibrato()
{
return (vibrato > 0) || (aftertouch > 0) || noteAfterTouchInUse;
}
void updateBendSensitivity()
{
int cent = bendsense_msb * 128 + bendsense_lsb;
bendsense = cent * (1.0 / (128 * 8192));
}
MIDIchannel()
{
activenotes_clear();
reset();
}
};
@ -551,36 +738,70 @@ public:
// Additional information about OPL3 channels
struct AdlChannel
{
// For collisions
struct Location
{
uint16_t MidCh;
uint8_t note;
bool operator==(const Location &b) const
{
return MidCh == b.MidCh && note == b.note;
}
bool operator< (const Location &b) const
{
return MidCh < b.MidCh || (MidCh == b.MidCh && note < b.note);
}
char ____padding[1];
bool operator==(const Location &l) const
{ return MidCh == l.MidCh && note == l.note; }
bool operator!=(const Location &l) const
{ return !operator==(l); }
};
struct LocationData
{
LocationData *prev, *next;
Location loc;
bool sustained;
char ____padding[7];
MIDIchannel::NoteInfo::Phys ins; // a copy of that in phys[]
//! Has fixed sustain, don't iterate "on" timeout
bool fixed_sustain;
//! Timeout until note will be allowed to be killed by channel manager while it is on
int64_t kon_time_until_neglible;
int64_t vibdelay;
};
typedef std::map<Location, LocationData> users_t;
users_t users;
// If the channel is keyoff'd
int64_t koff_time_until_neglible;
enum { users_max = 128 };
LocationData *users_first, *users_free_cells;
LocationData users_cells[users_max];
unsigned users_size;
bool users_empty() const;
LocationData *users_find(Location loc);
LocationData *users_allocate();
LocationData *users_find_or_create(Location loc);
LocationData *users_insert(const LocationData &x);
void users_erase(LocationData *user);
void users_clear();
void users_assign(const LocationData *users, size_t count);
// For channel allocation:
AdlChannel(): users(), koff_time_until_neglible(0) { }
AdlChannel(): koff_time_until_neglible(0)
{
users_clear();
}
AdlChannel(const AdlChannel &oth): koff_time_until_neglible(oth.koff_time_until_neglible)
{
if(oth.users_first)
{
users_first = NULL;
users_assign(oth.users_first, oth.users_size);
}
else
users_clear();
}
AdlChannel &operator=(const AdlChannel &oth)
{
koff_time_until_neglible = oth.koff_time_until_neglible;
users_assign(oth.users_first, oth.users_size);
return *this;
}
void AddAge(int64_t ms);
};
@ -713,6 +934,8 @@ public:
struct Setup
{
int emulator;
bool runAtPcmRate;
unsigned int AdlBank;
unsigned int NumFourOps;
unsigned int NumCards;
@ -724,6 +947,7 @@ public:
//unsigned int SkipForward;
bool loopingIsEnabled;
int ScaleModulators;
bool fullRangeBrightnessCC74;
double delay;
double carry;
@ -760,6 +984,11 @@ private:
char ____padding[7];
std::vector<AdlChannel> ch;
//! Counter of arpeggio processing
size_t m_arpeggioCounter;
//! Audio tick counter
uint32_t m_audioTickCounter;
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
std::vector<std::vector<uint8_t> > TrackData;
@ -832,11 +1061,25 @@ public:
#endif
OPL3 opl;
int16_t outBuf[1024];
int32_t outBuf[1024];
Setup m_setup;
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
/**
* @brief Utility function to read Big-Endian integer from raw binary data
* @param buffer Pointer to raw binary buffer
* @param nbytes Count of bytes to parse integer
* @return Extracted unsigned integer
*/
static uint64_t ReadBEint(const void *buffer, size_t nbytes);
/**
* @brief Utility function to read Little-Endian integer from raw binary data
* @param buffer Pointer to raw binary buffer
* @param nbytes Count of bytes to parse integer
* @return Extracted unsigned integer
*/
static uint64_t ReadLEint(const void *buffer, size_t nbytes);
/**
@ -853,6 +1096,7 @@ public:
* @return Unsigned integer that conains parsed variable-length value
*/
uint64_t ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok);
#endif
bool LoadBank(const std::string &filename);
bool LoadBank(const void *data, size_t size);
@ -943,6 +1187,9 @@ public:
void realTime_panic();
// Audio rate tick handler
void AudioTick(uint32_t chipId, uint32_t rate);
private:
enum
{
@ -951,7 +1198,9 @@ private:
Upd_Volume = 0x4,
Upd_Pitch = 0x8,
Upd_All = Upd_Pan + Upd_Volume + Upd_Pitch,
Upd_Off = 0x20
Upd_Off = 0x20,
Upd_Mute = 0x40,
Upt_OffMute = Upd_Off + Upd_Mute
};
void NoteUpdate(uint16_t MidCh,
@ -966,7 +1215,7 @@ private:
// Determine how good a candidate this adlchannel
// would be for playing a note from this instrument.
int64_t CalculateAdlChannelGoodness(unsigned c, const MIDIchannel::NoteInfo::Phys &ins, uint16_t /*MidCh*/) const;
int64_t CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins, uint16_t /*MidCh*/) const;
// A new note will be played on this channel using this instrument.
// Kill existing notes on this channel (or don't, if we do arpeggio)
@ -974,7 +1223,7 @@ private:
void KillOrEvacuate(
size_t from_channel,
AdlChannel::users_t::iterator j,
AdlChannel::LocationData *j,
MIDIchannel::activenoteiterator i);
void Panic();
void KillSustainingNotes(int32_t MidCh = -1, int32_t this_adlchn = -1);
@ -1009,6 +1258,7 @@ struct FourChars
};
*/
extern void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate);
extern int adlRefreshNumCards(ADL_MIDIPlayer *device);

View file

@ -0,0 +1,217 @@
/*
* 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) 2015-2018 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_PTR_HPP_THING
#define ADLMIDI_PTR_HPP_THING
#include <algorithm> // swap
#include <stddef.h>
#include <stdlib.h>
/*
Generic deleters for smart pointers
*/
template <class T>
struct ADLMIDI_DefaultDelete
{
void operator()(T *x) { delete x; }
};
template <class T>
struct ADLMIDI_DefaultArrayDelete
{
void operator()(T *x) { delete[] x; }
};
struct ADLMIDI_CDelete
{
void operator()(void *x) { free(x); }
};
/*
Safe unique pointer for C++98, non-copyable but swappable.
*/
template< class T, class Deleter = ADLMIDI_DefaultDelete<T> >
class AdlMIDI_UPtr
{
T *m_p;
public:
explicit AdlMIDI_UPtr(T *p)
: m_p(p) {}
~AdlMIDI_UPtr()
{
reset();
}
void reset(T *p = NULL)
{
if(p != m_p) {
if(m_p) {
Deleter del;
del(m_p);
}
m_p = p;
}
}
void swap(AdlMIDI_UPtr &other)
{
std::swap(m_p, other.m_p);
}
T *get() const
{
return m_p;
}
T &operator*() const
{
return *m_p;
}
T *operator->() const
{
return m_p;
}
T &operator[](size_t index) const
{
return m_p[index];
}
private:
AdlMIDI_UPtr(const AdlMIDI_UPtr &);
AdlMIDI_UPtr &operator=(const AdlMIDI_UPtr &);
};
template <class T>
void swap(AdlMIDI_UPtr<T> &a, AdlMIDI_UPtr<T> &b)
{
a.swap(b);
}
/**
Unique pointer for arrays.
*/
template<class T>
class AdlMIDI_UPtrArray :
public AdlMIDI_UPtr< T, ADLMIDI_DefaultArrayDelete<T> >
{
public:
explicit AdlMIDI_UPtrArray(T *p = NULL)
: AdlMIDI_UPtr< T, ADLMIDI_DefaultArrayDelete<T> >(p) {}
};
/**
Unique pointer for C memory.
*/
template<class T>
class AdlMIDI_CPtr :
public AdlMIDI_UPtr< T, ADLMIDI_CDelete >
{
public:
explicit AdlMIDI_CPtr(T *p = NULL)
: AdlMIDI_UPtr< T, ADLMIDI_CDelete >(p) {}
};
/*
Shared pointer with non-atomic counter
FAQ: Why not std::shared_ptr? Because of Android NDK now doesn't supports it
*/
template< class T, class Deleter = ADLMIDI_DefaultDelete<T> >
class AdlMIDI_SPtr
{
T *m_p;
size_t *m_counter;
public:
explicit AdlMIDI_SPtr(T *p = NULL)
: m_p(p), m_counter(p ? new size_t(1) : NULL) {}
~AdlMIDI_SPtr()
{
reset(NULL);
}
AdlMIDI_SPtr(const AdlMIDI_SPtr &other)
: m_p(other.m_p), m_counter(other.m_counter)
{
if(m_counter)
++*m_counter;
}
AdlMIDI_SPtr &operator=(const AdlMIDI_SPtr &other)
{
if(this == &other)
return *this;
reset();
m_p = other.m_p;
m_counter = other.m_counter;
if(m_counter)
++*m_counter;
return *this;
}
void reset(T *p = NULL)
{
if(p != m_p) {
if(m_p && --*m_counter == 0) {
Deleter del;
del(m_p);
if(!p) {
delete m_counter;
m_counter = NULL;
}
}
m_p = p;
if(p) {
if(!m_counter)
m_counter = new size_t;
*m_counter = 1;
}
}
}
T *get() const
{
return m_p;
}
T &operator*() const
{
return *m_p;
}
T *operator->() const
{
return m_p;
}
T &operator[](size_t index) const
{
return m_p[index];
}
};
/**
Shared pointer for arrays.
*/
template<class T>
class AdlMIDI_SPtrArray :
public AdlMIDI_SPtr< T, ADLMIDI_DefaultArrayDelete<T> >
{
public:
explicit AdlMIDI_SPtrArray(T *p = NULL)
: AdlMIDI_SPtr< T, ADLMIDI_DefaultArrayDelete<T> >(p) {}
};
#endif //ADLMIDI_PTR_HPP_THING

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,284 @@
/*
* Copyright (C) 2002-2018 The DOSBox Team
*
* 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 2 of the License, or
* (at your option) 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <inttypes.h>
#include <stdint.h>
#include <sys/types.h>
#if defined(__GNUC__) && defined(__i386__)
#define DB_FASTCALL __attribute__((fastcall))
#elif defined(_MSC_VER)
#define DB_FASTCALL __fastcall
#else
#define DB_FASTCALL
#endif
typedef uintptr_t Bitu;
typedef intptr_t Bits;
typedef uint64_t Bit64u;
typedef int64_t Bit64s;
typedef uint32_t Bit32u;
typedef int32_t Bit32s;
typedef uint16_t Bit16u;
typedef int16_t Bit16s;
typedef uint8_t Bit8u;
typedef int8_t Bit8s;
//Use 8 handlers based on a small logatirmic wavetabe and an exponential table for volume
#define WAVE_HANDLER 10
//Use a logarithmic wavetable with an exponential table for volume
#define WAVE_TABLELOG 11
//Use a linear wavetable with a multiply table for volume
#define WAVE_TABLEMUL 12
//Select the type of wave generator routine
#define DBOPL_WAVE WAVE_TABLEMUL
namespace DBOPL {
struct Chip;
struct Operator;
struct Channel;
#if (DBOPL_WAVE == WAVE_HANDLER)
typedef Bits ( DB_FASTCALL *WaveHandler) ( Bitu i, Bitu volume );
#endif
typedef Bits ( DBOPL::Operator::*VolumeHandler) ( );
typedef Channel* ( DBOPL::Channel::*SynthHandler) ( Chip* chip, Bit32u samples, Bit32s* output );
//Different synth modes that can generate blocks of data
typedef enum {
sm2AM,
sm2FM,
sm3AM,
sm3FM,
sm4Start,
sm3FMFM,
sm3AMFM,
sm3FMAM,
sm3AMAM,
sm6Start,
sm2Percussion,
sm3Percussion,
} SynthMode;
//Shifts for the values contained in chandata variable
enum {
SHIFT_KSLBASE = 16,
SHIFT_KEYCODE = 24,
};
struct Operator {
public:
//Masks for operator 20 values
enum {
MASK_KSR = 0x10,
MASK_SUSTAIN = 0x20,
MASK_VIBRATO = 0x40,
MASK_TREMOLO = 0x80,
};
typedef enum {
OFF,
RELEASE,
SUSTAIN,
DECAY,
ATTACK,
} State;
VolumeHandler volHandler;
#if (DBOPL_WAVE == WAVE_HANDLER)
WaveHandler waveHandler; //Routine that generate a wave
#else
Bit16s* waveBase;
Bit32u waveMask;
Bit32u waveStart;
#endif
Bit32u waveIndex; //WAVE_BITS shifted counter of the frequency index
Bit32u waveAdd; //The base frequency without vibrato
Bit32u waveCurrent; //waveAdd + vibratao
Bit32u chanData; //Frequency/octave and derived data coming from whatever channel controls this
Bit32u freqMul; //Scale channel frequency with this, TODO maybe remove?
Bit32u vibrato; //Scaled up vibrato strength
Bit32s sustainLevel; //When stopping at sustain level stop here
Bit32s totalLevel; //totalLevel is added to every generated volume
Bit32u currentLevel; //totalLevel + tremolo
Bit32s volume; //The currently active volume
Bit32u attackAdd; //Timers for the different states of the envelope
Bit32u decayAdd;
Bit32u releaseAdd;
Bit32u rateIndex; //Current position of the evenlope
Bit8u rateZero; //Bits for the different states of the envelope having no changes
Bit8u keyOn; //Bitmask of different values that can generate keyon
//Registers, also used to check for changes
Bit8u reg20, reg40, reg60, reg80, regE0;
//Active part of the envelope we're in
Bit8u state;
//0xff when tremolo is enabled
Bit8u tremoloMask;
//Strength of the vibrato
Bit8u vibStrength;
//Keep track of the calculated KSR so we can check for changes
Bit8u ksr;
private:
void SetState( Bit8u s );
void UpdateAttack( const Chip* chip );
void UpdateRelease( const Chip* chip );
void UpdateDecay( const Chip* chip );
public:
void UpdateAttenuation();
void UpdateRates( const Chip* chip );
void UpdateFrequency( );
void Write20( const Chip* chip, Bit8u val );
void Write40( const Chip* chip, Bit8u val );
void Write60( const Chip* chip, Bit8u val );
void Write80( const Chip* chip, Bit8u val );
void WriteE0( const Chip* chip, Bit8u val );
bool Silent() const;
void Prepare( const Chip* chip );
void KeyOn( Bit8u mask);
void KeyOff( Bit8u mask);
template< State state>
Bits TemplateVolume( );
Bit32s RateForward( Bit32u add );
Bitu ForwardWave();
Bitu ForwardVolume();
Bits GetSample( Bits modulation );
Bits GetWave( Bitu index, Bitu vol );
public:
Operator();
};
struct Channel {
Operator op[2];
inline Operator* Op( Bitu index ) {
return &( ( this + (index >> 1) )->op[ index & 1 ]);
}
SynthHandler synthHandler;
Bit32u chanData; //Frequency/octave and derived values
Bit32s old[2]; //Old data for feedback
Bit8u feedback; //Feedback shift
Bit8u regB0; //Register values to check for changes
Bit8u regC0;
//This should correspond with reg104, bit 6 indicates a Percussion channel, bit 7 indicates a silent channel
Bit8u fourMask;
Bit8s maskLeft; //Sign extended values for both channel's panning
Bit8s maskRight;
//Forward the channel data to the operators of the channel
void SetChanData( const Chip* chip, Bit32u data );
//Change in the chandata, check for new values and if we have to forward to operators
void UpdateFrequency( const Chip* chip, Bit8u fourOp );
void UpdateSynth(const Chip* chip);
void WriteA0( const Chip* chip, Bit8u val );
void WriteB0( const Chip* chip, Bit8u val );
void WriteC0( const Chip* chip, Bit8u val );
//call this for the first channel
template< bool opl3Mode >
void GeneratePercussion( Chip* chip, Bit32s* output );
//Generate blocks of data in specific modes
template<SynthMode mode>
Channel* BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output );
Channel();
};
struct Chip {
//This is used as the base counter for vibrato and tremolo
Bit32u lfoCounter;
Bit32u lfoAdd;
Bit32u noiseCounter;
Bit32u noiseAdd;
Bit32u noiseValue;
//Frequency scales for the different multiplications
Bit32u freqMul[16];
//Rates for decay and release for rate of this chip
Bit32u linearRates[76];
//Best match attack rates for the rate of this chip
Bit32u attackRates[76];
//18 channels with 2 operators each
Channel chan[18];
Bit8u reg104;
Bit8u reg08;
Bit8u reg04;
Bit8u regBD;
Bit8u vibratoIndex;
Bit8u tremoloIndex;
Bit8s vibratoSign;
Bit8u vibratoShift;
Bit8u tremoloValue;
Bit8u vibratoStrength;
Bit8u tremoloStrength;
//Mask for allowed wave forms
Bit8u waveFormMask;
//0 or -1 when enabled
Bit8s opl3Active;
//Return the maximum amount of samples before and LFO change
Bit32u ForwardLFO( Bit32u samples );
Bit32u ForwardNoise();
void WriteBD( Bit8u val );
void WriteReg(Bit32u reg, Bit8u val );
Bit32u WriteAddr( Bit32u port, Bit8u val );
void GenerateBlock2( Bitu samples, Bit32s* output );
void GenerateBlock2_Mix( Bitu samples, Bit32s* output );
void GenerateBlock3( Bitu samples, Bit32s* output );
void GenerateBlock3_Mix( Bitu samples, Bit32s* output );
//Update the synth handlers in all channels
void UpdateSynths();
void Generate( Bit32u samples );
void Setup( Bit32u r );
Chip();
};
struct Handler {
DBOPL::Chip chip;
Bit32u WriteAddr( Bit32u port, Bit8u val );
void WriteReg( Bit32u addr, Bit8u val );
void GenerateArr(Bit32s *out, Bitu *samples);
void GenerateArr(Bit16s *out, Bitu *samples);
void GenerateArrMix(Bit32s *out, Bitu *samples);
void GenerateArrMix(Bit16s *out, Bitu *samples);
void Init( Bitu rate );
};
} //Namespace

View file

@ -0,0 +1,54 @@
#include "dosbox_opl3.h"
#include "dosbox/dbopl.h"
#include <new>
#include <cstdlib>
#include <assert.h>
DosBoxOPL3::DosBoxOPL3() :
OPLChipBaseBufferedT(),
m_chip(new DBOPL::Handler)
{
reset();
}
DosBoxOPL3::~DosBoxOPL3()
{
DBOPL::Handler *chip_r = reinterpret_cast<DBOPL::Handler*>(m_chip);
delete chip_r;
}
void DosBoxOPL3::setRate(uint32_t rate)
{
OPLChipBaseBufferedT::setRate(rate);
DBOPL::Handler *chip_r = reinterpret_cast<DBOPL::Handler*>(m_chip);
chip_r->~Handler();
new(chip_r) DBOPL::Handler;
chip_r->Init(effectiveRate());
}
void DosBoxOPL3::reset()
{
OPLChipBaseBufferedT::reset();
DBOPL::Handler *chip_r = reinterpret_cast<DBOPL::Handler*>(m_chip);
chip_r->~Handler();
new(chip_r) DBOPL::Handler;
chip_r->Init(effectiveRate());
}
void DosBoxOPL3::writeReg(uint16_t addr, uint8_t data)
{
DBOPL::Handler *chip_r = reinterpret_cast<DBOPL::Handler*>(m_chip);
chip_r->WriteReg(static_cast<Bit32u>(addr), data);
}
void DosBoxOPL3::nativeGenerateN(int16_t *output, size_t frames)
{
DBOPL::Handler *chip_r = reinterpret_cast<DBOPL::Handler*>(m_chip);
Bitu frames_i = frames;
chip_r->GenerateArr(output, &frames_i);
}
const char *DosBoxOPL3::emulatorName()
{
return "DosBox 0.74-r4111 OPL3";
}

View file

@ -0,0 +1,23 @@
#ifndef DOSBOX_OPL3_H
#define DOSBOX_OPL3_H
#include "opl_chip_base.h"
class DosBoxOPL3 final : public OPLChipBaseBufferedT<DosBoxOPL3>
{
void *m_chip;
public:
DosBoxOPL3();
~DosBoxOPL3() override;
bool canRunAtPcmRate() const override { return true; }
void setRate(uint32_t rate) override;
void reset() override;
void writeReg(uint16_t addr, uint8_t data) override;
void nativePreGenerate() override {}
void nativePostGenerate() override {}
void nativeGenerateN(int16_t *output, size_t frames) override;
const char *emulatorName() override;
};
#endif // DOSBOX_OPL3_H

File diff suppressed because it is too large Load diff

View file

@ -1,19 +1,16 @@
/*
* Copyright (C) 2013-2016 Alexey Khokholov (Nuke.YKT)
* Copyright (C) 2013-2018 Alexey Khokholov (Nuke.YKT)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* 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 2
* of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* 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
* Lesser General Public License for more details.
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Nuked OPL3 emulator.
* Thanks:
@ -23,21 +20,21 @@
* Tremolo and phase generator calculation information.
* OPLx decapsulated(Matthew Gambrell, Olli Niemitalo):
* OPL2 ROMs.
* siliconpr0n.org(John McMaster, digshadow):
* YMF262 and VRC VII decaps and die shots.
*
* version: 1.7.4
* version: 1.8
*/
#ifndef OPL_OPL3_H
#define OPL_OPL3_H
#include <inttypes.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define OPL_WRITEBUF_SIZE 1024
#define OPL_WRITEBUF_DELAY 2
@ -82,16 +79,14 @@ struct _opl3_slot {
Bit8u reg_rr;
Bit8u reg_wf;
Bit8u key;
Bit32u pg_reset;
Bit32u pg_phase;
Bit32u timer;
Bit16u maskzero;
Bit8u signpos;
Bit8u phaseshift;
Bit16u pg_phase_out;
Bit8u slot_num;
};
struct _opl3_channel {
opl3_slot *slots[2];
opl3_slot *slotz[2];/*Don't use "slots" keyword to avoid conflict with Qt applications*/
opl3_channel *pair;
opl3_chip *chip;
Bit16s *out[4];
@ -103,6 +98,7 @@ struct _opl3_channel {
Bit8u alg;
Bit8u ksv;
Bit16u cha, chb;
Bit8u ch_num;
};
typedef struct _opl3_writebuf {
@ -113,8 +109,12 @@ typedef struct _opl3_writebuf {
struct _opl3_chip {
opl3_channel channel[18];
opl3_slot chipslot[36];
opl3_slot slot[36];
Bit16u timer;
Bit64u eg_timer;
Bit8u eg_timerrem;
Bit8u eg_state;
Bit8u eg_add;
Bit8u newm;
Bit8u nts;
Bit8u rhy;
@ -126,6 +126,12 @@ struct _opl3_chip {
Bit32u noise;
Bit16s zeromod;
Bit32s mixbuff[2];
Bit8u rm_hh_bit2;
Bit8u rm_hh_bit3;
Bit8u rm_hh_bit7;
Bit8u rm_hh_bit8;
Bit8u rm_tc_bit3;
Bit8u rm_tc_bit5;
/* OPL3L */
Bit32s rateratio;
Bit32s samplecnt;

View file

@ -30,7 +30,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nukedopl3.h"
#include "nukedopl3_174.h"
#define RSM_FRAC 10
@ -638,18 +638,18 @@ static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, Bit8u data)
channel6 = &chip->channel[6];
channel7 = &chip->channel[7];
channel8 = &chip->channel[8];
channel6->out[0] = &channel6->slots[1]->out;
channel6->out[1] = &channel6->slots[1]->out;
channel6->out[0] = &channel6->slotz[1]->out;
channel6->out[1] = &channel6->slotz[1]->out;
channel6->out[2] = &chip->zeromod;
channel6->out[3] = &chip->zeromod;
channel7->out[0] = &channel7->slots[0]->out;
channel7->out[1] = &channel7->slots[0]->out;
channel7->out[2] = &channel7->slots[1]->out;
channel7->out[3] = &channel7->slots[1]->out;
channel8->out[0] = &channel8->slots[0]->out;
channel8->out[1] = &channel8->slots[0]->out;
channel8->out[2] = &channel8->slots[1]->out;
channel8->out[3] = &channel8->slots[1]->out;
channel7->out[0] = &channel7->slotz[0]->out;
channel7->out[1] = &channel7->slotz[0]->out;
channel7->out[2] = &channel7->slotz[1]->out;
channel7->out[3] = &channel7->slotz[1]->out;
channel8->out[0] = &channel8->slotz[0]->out;
channel8->out[1] = &channel8->slotz[0]->out;
channel8->out[2] = &channel8->slotz[1]->out;
channel8->out[3] = &channel8->slotz[1]->out;
for (chnum = 6; chnum < 9; chnum++)
{
chip->channel[chnum].chtype = ch_drum;
@ -658,49 +658,49 @@ static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, Bit8u data)
/*hh*/
if (chip->rhy & 0x01)
{
OPL3_EnvelopeKeyOn(channel7->slots[0], egk_drum);
OPL3_EnvelopeKeyOn(channel7->slotz[0], egk_drum);
}
else
{
OPL3_EnvelopeKeyOff(channel7->slots[0], egk_drum);
OPL3_EnvelopeKeyOff(channel7->slotz[0], egk_drum);
}
/*tc*/
if (chip->rhy & 0x02)
{
OPL3_EnvelopeKeyOn(channel8->slots[1], egk_drum);
OPL3_EnvelopeKeyOn(channel8->slotz[1], egk_drum);
}
else
{
OPL3_EnvelopeKeyOff(channel8->slots[1], egk_drum);
OPL3_EnvelopeKeyOff(channel8->slotz[1], egk_drum);
}
/*tom*/
if (chip->rhy & 0x04)
{
OPL3_EnvelopeKeyOn(channel8->slots[0], egk_drum);
OPL3_EnvelopeKeyOn(channel8->slotz[0], egk_drum);
}
else
{
OPL3_EnvelopeKeyOff(channel8->slots[0], egk_drum);
OPL3_EnvelopeKeyOff(channel8->slotz[0], egk_drum);
}
/*sd*/
if (chip->rhy & 0x08)
{
OPL3_EnvelopeKeyOn(channel7->slots[1], egk_drum);
OPL3_EnvelopeKeyOn(channel7->slotz[1], egk_drum);
}
else
{
OPL3_EnvelopeKeyOff(channel7->slots[1], egk_drum);
OPL3_EnvelopeKeyOff(channel7->slotz[1], egk_drum);
}
/*bd*/
if (chip->rhy & 0x10)
{
OPL3_EnvelopeKeyOn(channel6->slots[0], egk_drum);
OPL3_EnvelopeKeyOn(channel6->slots[1], egk_drum);
OPL3_EnvelopeKeyOn(channel6->slotz[0], egk_drum);
OPL3_EnvelopeKeyOn(channel6->slotz[1], egk_drum);
}
else
{
OPL3_EnvelopeKeyOff(channel6->slots[0], egk_drum);
OPL3_EnvelopeKeyOff(channel6->slots[1], egk_drum);
OPL3_EnvelopeKeyOff(channel6->slotz[0], egk_drum);
OPL3_EnvelopeKeyOff(channel6->slotz[1], egk_drum);
}
}
else
@ -709,8 +709,8 @@ static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, Bit8u data)
{
chip->channel[chnum].chtype = ch_2op;
OPL3_ChannelSetupAlg(&chip->channel[chnum]);
OPL3_EnvelopeKeyOff(chip->channel[chnum].slots[0], egk_drum);
OPL3_EnvelopeKeyOff(chip->channel[chnum].slots[1], egk_drum);
OPL3_EnvelopeKeyOff(chip->channel[chnum].slotz[0], egk_drum);
OPL3_EnvelopeKeyOff(chip->channel[chnum].slotz[1], egk_drum);
}
}
}
@ -724,18 +724,18 @@ static void OPL3_ChannelWriteA0(opl3_channel *channel, Bit8u data)
channel->f_num = (channel->f_num & 0x300) | data;
channel->ksv = (channel->block << 1)
| ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01);
OPL3_EnvelopeUpdateKSL(channel->slots[0]);
OPL3_EnvelopeUpdateKSL(channel->slots[1]);
OPL3_EnvelopeUpdateRate(channel->slots[0]);
OPL3_EnvelopeUpdateRate(channel->slots[1]);
OPL3_EnvelopeUpdateKSL(channel->slotz[0]);
OPL3_EnvelopeUpdateKSL(channel->slotz[1]);
OPL3_EnvelopeUpdateRate(channel->slotz[0]);
OPL3_EnvelopeUpdateRate(channel->slotz[1]);
if (channel->chip->newm && channel->chtype == ch_4op)
{
channel->pair->f_num = channel->f_num;
channel->pair->ksv = channel->ksv;
OPL3_EnvelopeUpdateKSL(channel->pair->slots[0]);
OPL3_EnvelopeUpdateKSL(channel->pair->slots[1]);
OPL3_EnvelopeUpdateRate(channel->pair->slots[0]);
OPL3_EnvelopeUpdateRate(channel->pair->slots[1]);
OPL3_EnvelopeUpdateKSL(channel->pair->slotz[0]);
OPL3_EnvelopeUpdateKSL(channel->pair->slotz[1]);
OPL3_EnvelopeUpdateRate(channel->pair->slotz[0]);
OPL3_EnvelopeUpdateRate(channel->pair->slotz[1]);
}
}
@ -749,19 +749,19 @@ static void OPL3_ChannelWriteB0(opl3_channel *channel, Bit8u data)
channel->block = (data >> 2) & 0x07;
channel->ksv = (channel->block << 1)
| ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01);
OPL3_EnvelopeUpdateKSL(channel->slots[0]);
OPL3_EnvelopeUpdateKSL(channel->slots[1]);
OPL3_EnvelopeUpdateRate(channel->slots[0]);
OPL3_EnvelopeUpdateRate(channel->slots[1]);
OPL3_EnvelopeUpdateKSL(channel->slotz[0]);
OPL3_EnvelopeUpdateKSL(channel->slotz[1]);
OPL3_EnvelopeUpdateRate(channel->slotz[0]);
OPL3_EnvelopeUpdateRate(channel->slotz[1]);
if (channel->chip->newm && channel->chtype == ch_4op)
{
channel->pair->f_num = channel->f_num;
channel->pair->block = channel->block;
channel->pair->ksv = channel->ksv;
OPL3_EnvelopeUpdateKSL(channel->pair->slots[0]);
OPL3_EnvelopeUpdateKSL(channel->pair->slots[1]);
OPL3_EnvelopeUpdateRate(channel->pair->slots[0]);
OPL3_EnvelopeUpdateRate(channel->pair->slots[1]);
OPL3_EnvelopeUpdateKSL(channel->pair->slotz[0]);
OPL3_EnvelopeUpdateKSL(channel->pair->slotz[1]);
OPL3_EnvelopeUpdateRate(channel->pair->slotz[0]);
OPL3_EnvelopeUpdateRate(channel->pair->slotz[1]);
}
}
@ -772,12 +772,12 @@ static void OPL3_ChannelSetupAlg(opl3_channel *channel)
switch (channel->alg & 0x01)
{
case 0x00:
channel->slots[0]->mod = &channel->slots[0]->fbmod;
channel->slots[1]->mod = &channel->slots[0]->out;
channel->slotz[0]->mod = &channel->slotz[0]->fbmod;
channel->slotz[1]->mod = &channel->slotz[0]->out;
break;
case 0x01:
channel->slots[0]->mod = &channel->slots[0]->fbmod;
channel->slots[1]->mod = &channel->chip->zeromod;
channel->slotz[0]->mod = &channel->slotz[0]->fbmod;
channel->slotz[1]->mod = &channel->chip->zeromod;
break;
}
return;
@ -795,43 +795,43 @@ static void OPL3_ChannelSetupAlg(opl3_channel *channel)
switch (channel->alg & 0x03)
{
case 0x00:
channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod;
channel->pair->slots[1]->mod = &channel->pair->slots[0]->out;
channel->slots[0]->mod = &channel->pair->slots[1]->out;
channel->slots[1]->mod = &channel->slots[0]->out;
channel->out[0] = &channel->slots[1]->out;
channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod;
channel->pair->slotz[1]->mod = &channel->pair->slotz[0]->out;
channel->slotz[0]->mod = &channel->pair->slotz[1]->out;
channel->slotz[1]->mod = &channel->slotz[0]->out;
channel->out[0] = &channel->slotz[1]->out;
channel->out[1] = &channel->chip->zeromod;
channel->out[2] = &channel->chip->zeromod;
channel->out[3] = &channel->chip->zeromod;
break;
case 0x01:
channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod;
channel->pair->slots[1]->mod = &channel->pair->slots[0]->out;
channel->slots[0]->mod = &channel->chip->zeromod;
channel->slots[1]->mod = &channel->slots[0]->out;
channel->out[0] = &channel->pair->slots[1]->out;
channel->out[1] = &channel->slots[1]->out;
channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod;
channel->pair->slotz[1]->mod = &channel->pair->slotz[0]->out;
channel->slotz[0]->mod = &channel->chip->zeromod;
channel->slotz[1]->mod = &channel->slotz[0]->out;
channel->out[0] = &channel->pair->slotz[1]->out;
channel->out[1] = &channel->slotz[1]->out;
channel->out[2] = &channel->chip->zeromod;
channel->out[3] = &channel->chip->zeromod;
break;
case 0x02:
channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod;
channel->pair->slots[1]->mod = &channel->chip->zeromod;
channel->slots[0]->mod = &channel->pair->slots[1]->out;
channel->slots[1]->mod = &channel->slots[0]->out;
channel->out[0] = &channel->pair->slots[0]->out;
channel->out[1] = &channel->slots[1]->out;
channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod;
channel->pair->slotz[1]->mod = &channel->chip->zeromod;
channel->slotz[0]->mod = &channel->pair->slotz[1]->out;
channel->slotz[1]->mod = &channel->slotz[0]->out;
channel->out[0] = &channel->pair->slotz[0]->out;
channel->out[1] = &channel->slotz[1]->out;
channel->out[2] = &channel->chip->zeromod;
channel->out[3] = &channel->chip->zeromod;
break;
case 0x03:
channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod;
channel->pair->slots[1]->mod = &channel->chip->zeromod;
channel->slots[0]->mod = &channel->pair->slots[1]->out;
channel->slots[1]->mod = &channel->chip->zeromod;
channel->out[0] = &channel->pair->slots[0]->out;
channel->out[1] = &channel->slots[0]->out;
channel->out[2] = &channel->slots[1]->out;
channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod;
channel->pair->slotz[1]->mod = &channel->chip->zeromod;
channel->slotz[0]->mod = &channel->pair->slotz[1]->out;
channel->slotz[1]->mod = &channel->chip->zeromod;
channel->out[0] = &channel->pair->slotz[0]->out;
channel->out[1] = &channel->slotz[0]->out;
channel->out[2] = &channel->slotz[1]->out;
channel->out[3] = &channel->chip->zeromod;
break;
}
@ -841,18 +841,18 @@ static void OPL3_ChannelSetupAlg(opl3_channel *channel)
switch (channel->alg & 0x01)
{
case 0x00:
channel->slots[0]->mod = &channel->slots[0]->fbmod;
channel->slots[1]->mod = &channel->slots[0]->out;
channel->out[0] = &channel->slots[1]->out;
channel->slotz[0]->mod = &channel->slotz[0]->fbmod;
channel->slotz[1]->mod = &channel->slotz[0]->out;
channel->out[0] = &channel->slotz[1]->out;
channel->out[1] = &channel->chip->zeromod;
channel->out[2] = &channel->chip->zeromod;
channel->out[3] = &channel->chip->zeromod;
break;
case 0x01:
channel->slots[0]->mod = &channel->slots[0]->fbmod;
channel->slots[1]->mod = &channel->chip->zeromod;
channel->out[0] = &channel->slots[0]->out;
channel->out[1] = &channel->slots[1]->out;
channel->slotz[0]->mod = &channel->slotz[0]->fbmod;
channel->slotz[1]->mod = &channel->chip->zeromod;
channel->out[0] = &channel->slotz[0]->out;
channel->out[1] = &channel->slotz[1]->out;
channel->out[2] = &channel->chip->zeromod;
channel->out[3] = &channel->chip->zeromod;
break;
@ -905,21 +905,21 @@ static void OPL3_ChannelKeyOn(opl3_channel *channel)
{
if (channel->chtype == ch_4op)
{
OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm);
OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm);
OPL3_EnvelopeKeyOn(channel->pair->slots[0], egk_norm);
OPL3_EnvelopeKeyOn(channel->pair->slots[1], egk_norm);
OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm);
OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm);
OPL3_EnvelopeKeyOn(channel->pair->slotz[0], egk_norm);
OPL3_EnvelopeKeyOn(channel->pair->slotz[1], egk_norm);
}
else if (channel->chtype == ch_2op || channel->chtype == ch_drum)
{
OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm);
OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm);
OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm);
OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm);
}
}
else
{
OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm);
OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm);
OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm);
OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm);
}
}
@ -929,21 +929,21 @@ static void OPL3_ChannelKeyOff(opl3_channel *channel)
{
if (channel->chtype == ch_4op)
{
OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm);
OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm);
OPL3_EnvelopeKeyOff(channel->pair->slots[0], egk_norm);
OPL3_EnvelopeKeyOff(channel->pair->slots[1], egk_norm);
OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm);
OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm);
OPL3_EnvelopeKeyOff(channel->pair->slotz[0], egk_norm);
OPL3_EnvelopeKeyOff(channel->pair->slotz[1], egk_norm);
}
else if (channel->chtype == ch_2op || channel->chtype == ch_drum)
{
OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm);
OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm);
OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm);
OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm);
}
}
else
{
OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm);
OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm);
OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm);
OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm);
}
}
@ -997,9 +997,9 @@ static void OPL3_GenerateRhythm1(opl3_chip *chip)
channel6 = &chip->channel[6];
channel7 = &chip->channel[7];
channel8 = &chip->channel[8];
OPL3_SlotGenerate(channel6->slots[0]);
phase14 = (channel7->slots[0]->pg_phase >> 9) & 0x3ff;
phase17 = (channel8->slots[1]->pg_phase >> 9) & 0x3ff;
OPL3_SlotGenerate(channel6->slotz[0]);
phase14 = (channel7->slotz[0]->pg_phase >> 9) & 0x3ff;
phase17 = (channel8->slotz[1]->pg_phase >> 9) & 0x3ff;
phase = 0x00;
/*hh tc phase bit*/
phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04)
@ -1007,9 +1007,9 @@ static void OPL3_GenerateRhythm1(opl3_chip *chip)
/*hh*/
phase = (phasebit << 9)
| (0x34 << ((phasebit ^ (chip->noise & 0x01)) << 1));
OPL3_SlotGeneratePhase(channel7->slots[0], phase);
OPL3_SlotGeneratePhase(channel7->slotz[0], phase);
/*tt*/
OPL3_SlotGenerateZM(channel8->slots[0]);
OPL3_SlotGenerateZM(channel8->slotz[0]);
}
static void OPL3_GenerateRhythm2(opl3_chip *chip)
@ -1025,22 +1025,22 @@ static void OPL3_GenerateRhythm2(opl3_chip *chip)
channel6 = &chip->channel[6];
channel7 = &chip->channel[7];
channel8 = &chip->channel[8];
OPL3_SlotGenerate(channel6->slots[1]);
phase14 = (channel7->slots[0]->pg_phase >> 9) & 0x3ff;
phase17 = (channel8->slots[1]->pg_phase >> 9) & 0x3ff;
OPL3_SlotGenerate(channel6->slotz[1]);
phase14 = (channel7->slotz[0]->pg_phase >> 9) & 0x3ff;
phase17 = (channel8->slotz[1]->pg_phase >> 9) & 0x3ff;
phase = 0x00;
/*hh tc phase bit*/
phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04)
| (((phase17 >> 2) ^ phase17) & 0x08)) ? 0x01 : 0x00;
/*sd*/
phase = (0x100 << ((phase14 >> 8) & 0x01)) ^ ((chip->noise & 0x01) << 8);
OPL3_SlotGeneratePhase(channel7->slots[1], phase);
OPL3_SlotGeneratePhase(channel7->slotz[1], phase);
/*tc*/
phase = 0x100 | (phasebit << 9);
OPL3_SlotGeneratePhase(channel8->slots[1], phase);
OPL3_SlotGeneratePhase(channel8->slotz[1], phase);
}
void OPL3_Generate(opl3_chip *chip, Bit16s *buf)
void OPL3v17_Generate(opl3_chip *chip, Bit16s *buf)
{
Bit8u ii;
Bit8u jj;
@ -1161,20 +1161,20 @@ void OPL3_Generate(opl3_chip *chip, Bit16s *buf)
break;
}
chip->writebuf[chip->writebuf_cur].reg &= 0x1ff;
OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_cur].reg,
OPL3v17_WriteReg(chip, chip->writebuf[chip->writebuf_cur].reg,
chip->writebuf[chip->writebuf_cur].data);
chip->writebuf_cur = (chip->writebuf_cur + 1) % OPL_WRITEBUF_SIZE;
}
chip->writebuf_samplecnt++;
}
void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf)
void OPL3v17_GenerateResampled(opl3_chip *chip, Bit16s *buf)
{
while (chip->samplecnt >= chip->rateratio)
{
chip->oldsamples[0] = chip->samples[0];
chip->oldsamples[1] = chip->samples[1];
OPL3_Generate(chip, chip->samples);
OPL3v17_Generate(chip, chip->samples);
chip->samplecnt -= chip->rateratio;
}
buf[0] = (Bit16s)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt)
@ -1184,7 +1184,7 @@ void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf)
chip->samplecnt += 1 << RSM_FRAC;
}
void OPL3_Reset(opl3_chip *chip, Bit32u samplerate)
void OPL3v17_Reset(opl3_chip *chip, Bit32u samplerate)
{
Bit8u slotnum;
Bit8u channum;
@ -1202,8 +1202,8 @@ void OPL3_Reset(opl3_chip *chip, Bit32u samplerate)
}
for (channum = 0; channum < 18; channum++)
{
chip->channel[channum].slots[0] = &chip->chipslot[ch_slot[channum]];
chip->channel[channum].slots[1] = &chip->chipslot[ch_slot[channum] + 3];
chip->channel[channum].slotz[0] = &chip->chipslot[ch_slot[channum]];
chip->channel[channum].slotz[1] = &chip->chipslot[ch_slot[channum] + 3];
chip->chipslot[ch_slot[channum]].channel = &chip->channel[channum];
chip->chipslot[ch_slot[channum] + 3].channel = &chip->channel[channum];
if ((channum % 9) < 3)
@ -1230,7 +1230,7 @@ void OPL3_Reset(opl3_chip *chip, Bit32u samplerate)
chip->vibshift = 1;
}
void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v)
void OPL3v17_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v)
{
Bit8u high = (reg >> 8) & 0x01;
Bit8u regm = reg & 0xff;
@ -1329,13 +1329,13 @@ void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v)
}
}
void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v)
void OPL3v17_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v)
{
Bit64u time1, time2;
if (chip->writebuf[chip->writebuf_last].reg & 0x200)
{
OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_last].reg & 0x1ff,
OPL3v17_WriteReg(chip, chip->writebuf[chip->writebuf_last].reg & 0x1ff,
chip->writebuf[chip->writebuf_last].data);
chip->writebuf_cur = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE;
@ -1357,13 +1357,13 @@ void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v)
chip->writebuf_last = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE;
}
void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples)
void OPL3v17_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples)
{
Bit32u i;
for(i = 0; i < numsamples; i++)
{
OPL3_GenerateResampled(chip, sndptr);
OPL3v17_GenerateResampled(chip, sndptr);
sndptr += 2;
}
}
@ -1372,7 +1372,7 @@ void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples)
#define OPL3_MAX(A, B) (((A) < (B)) ? (B) : (A))
#define OPL3_CLAMP(V, MIN, MAX) OPL3_MAX(OPL3_MIN(V, MAX), MIN)
void OPL3_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples)
void OPL3v17_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples)
{
Bit32u i;
Bit16s sample[2];
@ -1380,7 +1380,7 @@ void OPL3_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples)
for(i = 0; i < numsamples; i++)
{
OPL3_GenerateResampled(chip, sample);
OPL3v17_GenerateResampled(chip, sample);
mix[0] = sndptr[0] + sample[0];
mix[1] = sndptr[1] + sample[1];
sndptr[0] = OPL3_CLAMP(mix[0], INT16_MIN, INT16_MAX);

View file

@ -0,0 +1,154 @@
/*
* Copyright (C) 2013-2016 Alexey Khokholov (Nuke.YKT)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Nuked OPL3 emulator.
* Thanks:
* MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh):
* Feedback and Rhythm part calculation information.
* forums.submarine.org.uk(carbon14, opl3):
* Tremolo and phase generator calculation information.
* OPLx decapsulated(Matthew Gambrell, Olli Niemitalo):
* OPL2 ROMs.
*
* version: 1.7.4
*/
#ifndef OPL_OPL3_H
#define OPL_OPL3_H
#include <inttypes.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define OPL_WRITEBUF_SIZE 1024
#define OPL_WRITEBUF_DELAY 2
typedef uintptr_t Bitu;
typedef intptr_t Bits;
typedef uint64_t Bit64u;
typedef int64_t Bit64s;
typedef uint32_t Bit32u;
typedef int32_t Bit32s;
typedef uint16_t Bit16u;
typedef int16_t Bit16s;
typedef uint8_t Bit8u;
typedef int8_t Bit8s;
typedef struct _opl3_slot opl3_slot;
typedef struct _opl3_channel opl3_channel;
typedef struct _opl3_chip opl3_chip;
struct _opl3_slot {
opl3_channel *channel;
opl3_chip *chip;
Bit16s out;
Bit16s fbmod;
Bit16s *mod;
Bit16s prout;
Bit16s eg_rout;
Bit16s eg_out;
Bit8u eg_inc;
Bit8u eg_gen;
Bit8u eg_rate;
Bit8u eg_ksl;
Bit8u *trem;
Bit8u reg_vib;
Bit8u reg_type;
Bit8u reg_ksr;
Bit8u reg_mult;
Bit8u reg_ksl;
Bit8u reg_tl;
Bit8u reg_ar;
Bit8u reg_dr;
Bit8u reg_sl;
Bit8u reg_rr;
Bit8u reg_wf;
Bit8u key;
Bit32u pg_phase;
Bit32u timer;
Bit16u maskzero;
Bit8u signpos;
Bit8u phaseshift;
};
struct _opl3_channel {
opl3_slot *slotz[2];/*Don't use "slots" keyword to avoid conflict with Qt applications*/
opl3_channel *pair;
opl3_chip *chip;
Bit16s *out[4];
Bit8u chtype;
Bit16u f_num;
Bit8u block;
Bit8u fb;
Bit8u con;
Bit8u alg;
Bit8u ksv;
Bit16u cha, chb;
};
typedef struct _opl3_writebuf {
Bit64u time;
Bit16u reg;
Bit8u data;
} opl3_writebuf;
struct _opl3_chip {
opl3_channel channel[18];
opl3_slot chipslot[36];
Bit16u timer;
Bit8u newm;
Bit8u nts;
Bit8u rhy;
Bit8u vibpos;
Bit8u vibshift;
Bit8u tremolo;
Bit8u tremolopos;
Bit8u tremoloshift;
Bit32u noise;
Bit16s zeromod;
Bit32s mixbuff[2];
/* OPL3L */
Bit32s rateratio;
Bit32s samplecnt;
Bit16s oldsamples[2];
Bit16s samples[2];
Bit64u writebuf_samplecnt;
Bit32u writebuf_cur;
Bit32u writebuf_last;
Bit64u writebuf_lasttime;
opl3_writebuf writebuf[OPL_WRITEBUF_SIZE];
};
void OPL3v17_Generate(opl3_chip *chip, Bit16s *buf);
void OPL3v17_GenerateResampled(opl3_chip *chip, Bit16s *buf);
void OPL3v17_Reset(opl3_chip *chip, Bit32u samplerate);
void OPL3v17_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v);
void OPL3v17_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v);
void OPL3v17_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples);
void OPL3v17_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,49 @@
#include "nuked_opl3.h"
#include "nuked/nukedopl3.h"
#include <cstring>
NukedOPL3::NukedOPL3() :
OPLChipBaseT()
{
m_chip = new opl3_chip;
setRate(m_rate);
}
NukedOPL3::~NukedOPL3()
{
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
delete chip_r;
}
void NukedOPL3::setRate(uint32_t rate)
{
OPLChipBaseT::setRate(rate);
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
std::memset(chip_r, 0, sizeof(opl3_chip));
OPL3_Reset(chip_r, rate);
}
void NukedOPL3::reset()
{
OPLChipBaseT::reset();
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
std::memset(chip_r, 0, sizeof(opl3_chip));
OPL3_Reset(chip_r, m_rate);
}
void NukedOPL3::writeReg(uint16_t addr, uint8_t data)
{
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
OPL3_WriteRegBuffered(chip_r, addr, data);
}
void NukedOPL3::nativeGenerate(int16_t *frame)
{
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
OPL3_Generate(chip_r, frame);
}
const char *NukedOPL3::emulatorName()
{
return "Nuked OPL3 (v 1.8)";
}

View file

@ -0,0 +1,23 @@
#ifndef NUKED_OPL3_H
#define NUKED_OPL3_H
#include "opl_chip_base.h"
class NukedOPL3 final : public OPLChipBaseT<NukedOPL3>
{
void *m_chip;
public:
NukedOPL3();
~NukedOPL3() override;
bool canRunAtPcmRate() const override { return false; }
void setRate(uint32_t rate) override;
void reset() override;
void writeReg(uint16_t addr, uint8_t data) override;
void nativePreGenerate() override {}
void nativePostGenerate() override {}
void nativeGenerate(int16_t *frame) override;
const char *emulatorName() override;
};
#endif // NUKED_OPL3_H

View file

@ -0,0 +1,49 @@
#include "nuked_opl3_v174.h"
#include "nuked/nukedopl3_174.h"
#include <cstring>
NukedOPL3v174::NukedOPL3v174() :
OPLChipBaseT()
{
m_chip = new opl3_chip;
setRate(m_rate);
}
NukedOPL3v174::~NukedOPL3v174()
{
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
delete chip_r;
}
void NukedOPL3v174::setRate(uint32_t rate)
{
OPLChipBaseT::setRate(rate);
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
std::memset(chip_r, 0, sizeof(opl3_chip));
OPL3v17_Reset(chip_r, rate);
}
void NukedOPL3v174::reset()
{
OPLChipBaseT::reset();
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
std::memset(chip_r, 0, sizeof(opl3_chip));
OPL3v17_Reset(chip_r, m_rate);
}
void NukedOPL3v174::writeReg(uint16_t addr, uint8_t data)
{
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
OPL3v17_WriteReg(chip_r, addr, data);
}
void NukedOPL3v174::nativeGenerate(int16_t *frame)
{
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
OPL3v17_Generate(chip_r, frame);
}
const char *NukedOPL3v174::emulatorName()
{
return "Nuked OPL3 (v 1.7.4)";
}

View file

@ -0,0 +1,23 @@
#ifndef NUKED_OPL3174_H
#define NUKED_OPL3174_H
#include "opl_chip_base.h"
class NukedOPL3v174 final : public OPLChipBaseT<NukedOPL3v174>
{
void *m_chip;
public:
NukedOPL3v174();
~NukedOPL3v174() override;
bool canRunAtPcmRate() const override { return false; }
void setRate(uint32_t rate) override;
void reset() override;
void writeReg(uint16_t addr, uint8_t data) override;
void nativePreGenerate() override {}
void nativePostGenerate() override {}
void nativeGenerate(int16_t *frame) override;
const char *emulatorName() override;
};
#endif // NUKED_OPL3174_H

View file

@ -0,0 +1,129 @@
#ifndef ONP_CHIP_BASE_H
#define ONP_CHIP_BASE_H
#include <stdint.h>
#include <stddef.h>
#if !defined(_MSC_VER) && (__cplusplus <= 199711L)
#define final
#define override
#endif
#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
class VResampler;
#endif
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
extern void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate);
#endif
class OPLChipBase
{
public:
enum { nativeRate = 49716 };
protected:
uint32_t m_id;
uint32_t m_rate;
public:
OPLChipBase();
virtual ~OPLChipBase();
uint32_t chipId() const { return m_id; }
void setChipId(uint32_t id) { m_id = id; }
virtual bool canRunAtPcmRate() const = 0;
virtual bool isRunningAtPcmRate() const = 0;
virtual bool setRunningAtPcmRate(bool r) = 0;
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
virtual void setAudioTickHandlerInstance(void *instance) = 0;
#endif
virtual void setRate(uint32_t rate) = 0;
virtual uint32_t effectiveRate() const = 0;
virtual void reset() = 0;
virtual void writeReg(uint16_t addr, uint8_t data) = 0;
virtual void nativePreGenerate() = 0;
virtual void nativePostGenerate() = 0;
virtual void nativeGenerate(int16_t *frame) = 0;
virtual void generate(int16_t *output, size_t frames) = 0;
virtual void generateAndMix(int16_t *output, size_t frames) = 0;
virtual void generate32(int32_t *output, size_t frames) = 0;
virtual void generateAndMix32(int32_t *output, size_t frames) = 0;
virtual const char* emulatorName() = 0;
private:
OPLChipBase(const OPLChipBase &c);
OPLChipBase &operator=(const OPLChipBase &c);
};
// A base class providing F-bounded generic and efficient implementations,
// supporting resampling of chip outputs
template <class T>
class OPLChipBaseT : public OPLChipBase
{
public:
OPLChipBaseT();
virtual ~OPLChipBaseT();
bool isRunningAtPcmRate() const override;
bool setRunningAtPcmRate(bool r) override;
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
void setAudioTickHandlerInstance(void *instance);
#endif
virtual void setRate(uint32_t rate) override;
uint32_t effectiveRate() const override;
virtual void reset() override;
void generate(int16_t *output, size_t frames) override;
void generateAndMix(int16_t *output, size_t frames) override;
void generate32(int32_t *output, size_t frames) override;
void generateAndMix32(int32_t *output, size_t frames) override;
private:
bool m_runningAtPcmRate;
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
void *m_audioTickHandlerInstance;
#endif
void nativeTick(int16_t *frame);
void setupResampler(uint32_t rate);
void resetResampler();
void resampledGenerate(int32_t *output);
#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
VResampler *m_resampler;
#else
int32_t m_oldsamples[2];
int32_t m_samples[2];
int32_t m_samplecnt;
int32_t m_rateratio;
enum { rsm_frac = 10 };
#endif
// amplitude scale factors in and out of resampler, varying for chips;
// values are OK to "redefine", the static polymorphism will accept it.
enum { resamplerPreAmplify = 1, resamplerPostAttenuate = 1 };
};
// A base class which provides frame-by-frame interfaces on emulations which
// don't have a routine for it. It produces outputs in fixed size buffers.
// Fast register updates will suffer some latency because of buffering.
template <class T, unsigned Buffer = 256>
class OPLChipBaseBufferedT : public OPLChipBaseT<T>
{
public:
OPLChipBaseBufferedT()
: OPLChipBaseT<T>(), m_bufferIndex(0) {}
virtual ~OPLChipBaseBufferedT()
{}
public:
void reset() override;
void nativeGenerate(int16_t *frame) override;
protected:
virtual void nativeGenerateN(int16_t *output, size_t frames) = 0;
private:
unsigned m_bufferIndex;
int16_t m_buffer[2 * Buffer];
};
#include "opl_chip_base.tcc"
#endif // ONP_CHIP_BASE_H

View file

@ -0,0 +1,294 @@
#include "opl_chip_base.h"
#include <cmath>
#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
#include <zita-resampler/vresampler.h>
#endif
#if !defined(LIKELY) && defined(__GNUC__)
#define LIKELY(x) __builtin_expect((x), 1)
#elif !defined(LIKELY)
#define LIKELY(x) (x)
#endif
#if !defined(UNLIKELY) && defined(__GNUC__)
#define UNLIKELY(x) __builtin_expect((x), 0)
#elif !defined(UNLIKELY)
#define UNLIKELY(x) (x)
#endif
/* OPLChipBase */
inline OPLChipBase::OPLChipBase() :
m_id(0),
m_rate(44100)
{
}
inline OPLChipBase::~OPLChipBase()
{
}
/* OPLChipBaseT */
template <class T>
OPLChipBaseT<T>::OPLChipBaseT()
: OPLChipBase(),
m_runningAtPcmRate(false)
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
,
m_audioTickHandlerInstance(NULL)
#endif
{
#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
m_resampler = new VResampler;
#endif
setupResampler(m_rate);
}
template <class T>
OPLChipBaseT<T>::~OPLChipBaseT()
{
#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
delete m_resampler;
#endif
}
template <class T>
bool OPLChipBaseT<T>::isRunningAtPcmRate() const
{
return m_runningAtPcmRate;
}
template <class T>
bool OPLChipBaseT<T>::setRunningAtPcmRate(bool r)
{
if(r != m_runningAtPcmRate)
{
if(r && !static_cast<T *>(this)->canRunAtPcmRate())
return false;
m_runningAtPcmRate = r;
static_cast<T *>(this)->setRate(m_rate);
}
return true;
}
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
template <class T>
void OPLChipBaseT<T>::setAudioTickHandlerInstance(void *instance)
{
m_audioTickHandlerInstance = instance;
}
#endif
template <class T>
void OPLChipBaseT<T>::setRate(uint32_t rate)
{
uint32_t oldRate = m_rate;
m_rate = rate;
if(rate != oldRate)
setupResampler(rate);
else
resetResampler();
}
template <class T>
uint32_t OPLChipBaseT<T>::effectiveRate() const
{
return m_runningAtPcmRate ? m_rate : (uint32_t)nativeRate;
}
template <class T>
void OPLChipBaseT<T>::reset()
{
resetResampler();
}
template <class T>
void OPLChipBaseT<T>::generate(int16_t *output, size_t frames)
{
static_cast<T *>(this)->nativePreGenerate();
for(size_t i = 0; i < frames; ++i)
{
int32_t frame[2];
static_cast<T *>(this)->resampledGenerate(frame);
for (unsigned c = 0; c < 2; ++c) {
int32_t temp = frame[c];
temp = (temp > -32768) ? temp : -32768;
temp = (temp < 32767) ? temp : 32767;
output[c] = (int16_t)temp;
}
output += 2;
}
static_cast<T *>(this)->nativePostGenerate();
}
template <class T>
void OPLChipBaseT<T>::generateAndMix(int16_t *output, size_t frames)
{
static_cast<T *>(this)->nativePreGenerate();
for(size_t i = 0; i < frames; ++i)
{
int32_t frame[2];
static_cast<T *>(this)->resampledGenerate(frame);
for (unsigned c = 0; c < 2; ++c) {
int32_t temp = (int32_t)output[c] + frame[c];
temp = (temp > -32768) ? temp : -32768;
temp = (temp < 32767) ? temp : 32767;
output[c] = (int16_t)temp;
}
output += 2;
}
static_cast<T *>(this)->nativePostGenerate();
}
template <class T>
void OPLChipBaseT<T>::generate32(int32_t *output, size_t frames)
{
static_cast<T *>(this)->nativePreGenerate();
for(size_t i = 0; i < frames; ++i)
{
static_cast<T *>(this)->resampledGenerate(output);
output += 2;
}
static_cast<T *>(this)->nativePostGenerate();
}
template <class T>
void OPLChipBaseT<T>::generateAndMix32(int32_t *output, size_t frames)
{
static_cast<T *>(this)->nativePreGenerate();
for(size_t i = 0; i < frames; ++i)
{
int32_t frame[2];
static_cast<T *>(this)->resampledGenerate(frame);
output[0] += frame[0];
output[1] += frame[1];
output += 2;
}
static_cast<T *>(this)->nativePostGenerate();
}
template <class T>
void OPLChipBaseT<T>::nativeTick(int16_t *frame)
{
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
adl_audioTickHandler(m_audioTickHandlerInstance, m_id, effectiveRate());
#endif
static_cast<T *>(this)->nativeGenerate(frame);
}
template <class T>
void OPLChipBaseT<T>::setupResampler(uint32_t rate)
{
#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
m_resampler->setup(rate * (1.0 / 49716), 2, 48);
#else
m_oldsamples[0] = m_oldsamples[1] = 0;
m_samples[0] = m_samples[1] = 0;
m_samplecnt = 0;
m_rateratio = (int32_t)((rate << rsm_frac) / 49716);
#endif
}
template <class T>
void OPLChipBaseT<T>::resetResampler()
{
#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
m_resampler->reset();
#else
m_oldsamples[0] = m_oldsamples[1] = 0;
m_samples[0] = m_samples[1] = 0;
m_samplecnt = 0;
#endif
}
#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
template <class T>
void OPLChipBaseT<T>::resampledGenerate(int32_t *output)
{
if(UNLIKELY(m_runningAtPcmRate))
{
int16_t in[2];
static_cast<T *>(this)->nativeTick(in);
output[0] = (int32_t)in[0] * T::resamplerPreAmplify / T::resamplerPostAttenuate;
output[1] = (int32_t)in[1] * T::resamplerPreAmplify / T::resamplerPostAttenuate;
return;
}
VResampler *rsm = m_resampler;
float scale = (float)T::resamplerPreAmplify /
(float)T::resamplerPostAttenuate;
float f_in[2];
float f_out[2];
rsm->inp_count = 0;
rsm->inp_data = f_in;
rsm->out_count = 1;
rsm->out_data = f_out;
while(rsm->process(), rsm->out_count != 0)
{
int16_t in[2];
static_cast<T *>(this)->nativeTick(in);
f_in[0] = scale * (float)in[0];
f_in[1] = scale * (float)in[1];
rsm->inp_count = 1;
rsm->inp_data = f_in;
rsm->out_count = 1;
rsm->out_data = f_out;
}
output[0] = std::lround(f_out[0]);
output[1] = std::lround(f_out[1]);
}
#else
template <class T>
void OPLChipBaseT<T>::resampledGenerate(int32_t *output)
{
if(UNLIKELY(m_runningAtPcmRate))
{
int16_t in[2];
static_cast<T *>(this)->nativeTick(in);
output[0] = (int32_t)in[0] * T::resamplerPreAmplify / T::resamplerPostAttenuate;
output[1] = (int32_t)in[1] * T::resamplerPreAmplify / T::resamplerPostAttenuate;
return;
}
int32_t samplecnt = m_samplecnt;
const int32_t rateratio = m_rateratio;
while(samplecnt >= rateratio)
{
m_oldsamples[0] = m_samples[0];
m_oldsamples[1] = m_samples[1];
int16_t buffer[2];
static_cast<T *>(this)->nativeTick(buffer);
m_samples[0] = buffer[0] * T::resamplerPreAmplify;
m_samples[1] = buffer[1] * T::resamplerPreAmplify;
samplecnt -= rateratio;
}
output[0] = (int32_t)(((m_oldsamples[0] * (rateratio - samplecnt)
+ m_samples[0] * samplecnt) / rateratio)/T::resamplerPostAttenuate);
output[1] = (int32_t)(((m_oldsamples[1] * (rateratio - samplecnt)
+ m_samples[1] * samplecnt) / rateratio)/T::resamplerPostAttenuate);
m_samplecnt = samplecnt + (1 << rsm_frac);
}
#endif
/* OPLChipBaseBufferedT */
template <class T, unsigned Buffer>
void OPLChipBaseBufferedT<T, Buffer>::reset()
{
OPLChipBaseT<T>::reset();
m_bufferIndex = 0;
}
template <class T, unsigned Buffer>
void OPLChipBaseBufferedT<T, Buffer>::nativeGenerate(int16_t *frame)
{
unsigned bufferIndex = m_bufferIndex;
if(bufferIndex == 0)
static_cast<T *>(this)->nativeGenerateN(m_buffer, Buffer);
frame[0] = m_buffer[2 * bufferIndex];
frame[1] = m_buffer[2 * bufferIndex + 1];
bufferIndex = (bufferIndex + 1 < Buffer) ? (bufferIndex + 1) : 0;
m_bufferIndex = bufferIndex;
}

File diff suppressed because it is too large Load diff

View file

@ -1,314 +0,0 @@
/*
* Copyright (C) 2002-2010 The DOSBox Team
*
* 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 2 of the License, or
* (at your option) 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* BEGIN MIDIPLAY GLUE */
#include <stdint.h>
#include <stdlib.h>
typedef unsigned long Bitu;
typedef signed long Bits;
typedef unsigned Bit32u;
typedef int Bit32s;
typedef unsigned short Bit16u;
typedef signed short Bit16s;
typedef unsigned char Bit8u;
typedef signed char Bit8s;
#define INLINE inline
#ifdef _MSC_VER
#define GCC_UNLIKELY(x) (!!(x) == 0)
#define GCC_LIKELY(x) (!!(x) == 1)
#else
#define GCC_UNLIKELY(x) __builtin_expect((x),0)
#define GCC_LIKELY(x) __builtin_expect((x),1)
#endif
/* END MIDIPLAY GLUE */
//Use 8 handlers based on a small logatirmic wavetabe and an exponential table for volume
#define WAVE_HANDLER 10
//Use a logarithmic wavetable with an exponential table for volume
#define WAVE_TABLELOG 11
//Use a linear wavetable with a multiply table for volume
#define WAVE_TABLEMUL 12
//Select the type of wave generator routine
#define DBOPL_WAVE WAVE_TABLEMUL
#ifdef _WIN32
# ifdef _MSC_VER
# ifdef _WIN64
typedef __int64 ssize_t;
# else
typedef __int32 ssize_t;
# endif
# else
# ifdef _WIN64
typedef int64_t ssize_t;
# else
typedef int32_t ssize_t;
# endif
# endif
#endif
namespace DBOPL
{
struct Chip;
struct Operator;
struct Channel;
#if (DBOPL_WAVE == WAVE_HANDLER)
typedef Bits(DB_FASTCALL *WaveHandler)(Bitu i, Bitu volume);
#endif
typedef Bits(DBOPL::Operator::*VolumeHandler)();
typedef Channel *(DBOPL::Channel::*SynthHandler)(Chip *chip, Bit32u samples, Bit32s *output);
//Different synth modes that can generate blocks of data
typedef enum
{
sm2AM,
sm2FM,
sm3AM,
sm3FM,
sm4Start,
sm3FMFM,
sm3AMFM,
sm3FMAM,
sm3AMAM,
sm6Start,
sm2Percussion,
sm3Percussion
} SynthMode;
//Shifts for the values contained in chandata variable
enum
{
SHIFT_KSLBASE = 16,
SHIFT_KEYCODE = 24
};
struct Operator
{
public:
//Masks for operator 20 values
enum
{
MASK_KSR = 0x10,
MASK_SUSTAIN = 0x20,
MASK_VIBRATO = 0x40,
MASK_TREMOLO = 0x80
};
typedef enum
{
OFF,
RELEASE,
SUSTAIN,
DECAY,
ATTACK
} State;
VolumeHandler volHandler;
#if (DBOPL_WAVE == WAVE_HANDLER)
WaveHandler waveHandler; //Routine that generate a wave
#else
Bit16s *waveBase;
Bit32u waveMask;
Bit32u waveStart;
#endif
Bit32u waveIndex; //WAVE_BITS shifted counter of the frequency index
Bit32u waveAdd; //The base frequency without vibrato
Bit32u waveCurrent; //waveAdd + vibratao
Bit32u chanData; //Frequency/octave and derived data coming from whatever channel controls this
Bit32u freqMul; //Scale channel frequency with this, TODO maybe remove?
Bit32u vibrato; //Scaled up vibrato strength
Bit32s sustainLevel; //When stopping at sustain level stop here
Bit32s totalLevel; //totalLevel is added to every generated volume
Bit32u currentLevel; //totalLevel + tremolo
Bit32s volume; //The currently active volume
Bit32u attackAdd; //Timers for the different states of the envelope
Bit32u decayAdd;
Bit32u releaseAdd;
Bit32u rateIndex; //Current position of the evenlope
Bit8u rateZero; //Bits for the different states of the envelope having no changes
Bit8u keyOn; //Bitmask of different values that can generate keyon
//Registers, also used to check for changes
Bit8u reg20, reg40, reg60, reg80, regE0;
//Active part of the envelope we're in
Bit8u state;
//0xff when tremolo is enabled
Bit8u tremoloMask;
//Strength of the vibrato
Bit8u vibStrength;
//Keep track of the calculated KSR so we can check for changes
Bit8u ksr;
private:
void SetState(Bit8u s);
void UpdateAttack(const Chip *chip);
void UpdateRelease(const Chip *chip);
void UpdateDecay(const Chip *chip);
public:
void UpdateAttenuation();
void UpdateRates(const Chip *chip);
void UpdateFrequency();
void Write20(const Chip *chip, Bit8u val);
void Write40(const Chip *chip, Bit8u val);
void Write60(const Chip *chip, Bit8u val);
void Write80(const Chip *chip, Bit8u val);
void WriteE0(const Chip *chip, Bit8u val);
bool Silent() const;
void Prepare(const Chip *chip);
void KeyOn(Bit8u mask);
void KeyOff(Bit8u mask);
template< State state>
Bits TemplateVolume();
Bit32s RateForward(Bit32u add);
Bitu ForwardWave();
Bitu ForwardVolume();
Bits GetSample(Bits modulation);
Bits GetWave(Bitu index, Bitu vol);
public:
Operator();
char ____padding[5];
};
struct Channel
{
Operator op[2];
inline Operator *Op(Bitu index)
{
return &((this + (index >> 1))->op[ index & 1 ]);
}
SynthHandler synthHandler;
Bit32u chanData; //Frequency/octave and derived values
Bit32s old[2]; //Old data for feedback
Bit8u feedback; //Feedback shift
Bit8u regB0; //Register values to check for changes
Bit8u regC0;
//This should correspond with reg104, bit 6 indicates a Percussion channel, bit 7 indicates a silent channel
Bit8u fourMask;
Bit8s maskLeft; //Sign extended values for both channel's panning
Bit8s maskRight;
//Forward the channel data to the operators of the channel
void SetChanData(const Chip *chip, Bit32u data);
//Change in the chandata, check for new values and if we have to forward to operators
void UpdateFrequency(const Chip *chip, Bit8u fourOp);
void WriteA0(const Chip *chip, Bit8u val);
void WriteB0(const Chip *chip, Bit8u val);
void WriteC0(const Chip *chip, Bit8u val);
void ResetC0(const Chip *chip);
//call this for the first channel
template< bool opl3Mode >
void GeneratePercussion(Chip *chip, Bit32s *output);
//Generate blocks of data in specific modes
template<SynthMode mode>
Channel *BlockTemplate(Chip *chip, Bit32u samples, Bit32s *output);
Channel();
char ____padding[6];
};
struct Chip
{
//This is used as the base counter for vibrato and tremolo
Bit32u lfoCounter;
Bit32u lfoAdd;
Bit32u noiseCounter;
Bit32u noiseAdd;
Bit32u noiseValue;
//Frequency scales for the different multiplications
Bit32u freqMul[16];
//Rates for decay and release for rate of this chip
Bit32u linearRates[76];
//Best match attack rates for the rate of this chip
Bit32u attackRates[76];
//18 channels with 2 operators each
Channel chan[18];
Bit8u reg104;
Bit8u reg08;
Bit8u reg04;
Bit8u regBD;
Bit8u vibratoIndex;
Bit8u tremoloIndex;
Bit8s vibratoSign;
Bit8u vibratoShift;
Bit8u tremoloValue;
Bit8u vibratoStrength;
Bit8u tremoloStrength;
//Mask for allowed wave forms
Bit8u waveFormMask;
//0 or -1 when enabled
Bit8s opl3Active;
//Return the maximum amount of samples before and LFO change
Bit32u ForwardLFO(Bit32u samples);
Bit32u ForwardNoise();
void WriteBD(Bit8u val);
void WriteReg(Bit32u reg, Bit8u val);
Bit32u WriteAddr(Bit32u port, Bit8u val);
void GenerateBlock2(Bitu samples, Bit32s *output);
void GenerateBlock3(Bitu samples, Bit32s *output);
void GenerateBlock2_Mix(Bitu samples, Bit32s *output);
void GenerateBlock3_Mix(Bitu samples, Bit32s *output);
void Generate(Bit32u samples);
void Setup(Bit32u r);
Chip();
};
struct Handler
{
DBOPL::Chip chip;
Bit32u WriteAddr(Bit32u port, Bit8u val);
void WriteReg(Bit32u addr, Bit8u val);
void Generate(void(*AddSamples_m32)(Bitu, Bit32s *),
void(*AddSamples_s32)(Bitu, Bit32s *),
Bitu samples);
void GenerateArr(Bit32s *out, Bitu *samples);
void GenerateArr(Bit32s *out, ssize_t *samples);
void GenerateArr(Bit16s *out, ssize_t *samples);
void GenerateArrMix(Bit32s *out, ssize_t *samples);
void GenerateArrMix(Bit16s *out, ssize_t *samples);
void Init(Bitu rate);
};
} //Namespace

View file

@ -4,6 +4,7 @@
#include <cmath>
#include <limits>
/* Fraction number handling.
* Copyright (C) 1992,2001 Bisqwit (http://iki.fi/bisqwit/)
*/
@ -82,6 +83,10 @@ public:
self &operator= (long double orig);
};
#ifdef _MSC_VER
#pragma warning(disable:4146)
#endif
template<typename inttype>
void fraction<inttype>::Optim()
{
@ -110,6 +115,10 @@ void fraction<inttype>::Optim()
//fprintf(stderr, "result: %d/%d\n\n", nom(), denom());
}
#ifdef _MSC_VER
#pragma warning(default:4146)
#endif
template<typename inttype>
inline const fraction<inttype> abs(const fraction<inttype> &f)
{

View file

@ -0,0 +1,584 @@
/*
* Wohlstand's OPL3 Bank File - a bank format to store OPL3 timbre data and setup
*
* Copyright (c) 2015-2018 Vitaly Novichkov <admin@wohlnet.ru>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "wopl_file.h"
#include <string.h>
#include <stdlib.h>
static const char *wopl3_magic = "WOPL3-BANK\0";
static const char *wopli_magic = "WOPL3-INST\0";
static const uint16_t wopl_latest_version = 3;
#define WOPL_INST_SIZE_V2 62
#define WOPL_INST_SIZE_V3 66
static uint16_t toUint16LE(const uint8_t *arr)
{
uint16_t num = arr[0];
num |= ((arr[1] << 8) & 0xFF00);
return num;
}
static uint16_t toUint16BE(const uint8_t *arr)
{
uint16_t num = arr[1];
num |= ((arr[0] << 8) & 0xFF00);
return num;
}
static int16_t toSint16BE(const uint8_t *arr)
{
int16_t num = *(const int8_t *)(&arr[0]);
num *= 1 << 8;
num |= arr[1];
return num;
}
static void fromUint16LE(uint16_t in, uint8_t *arr)
{
arr[0] = in & 0x00FF;
arr[1] = (in >> 8) & 0x00FF;
}
static void fromUint16BE(uint16_t in, uint8_t *arr)
{
arr[1] = in & 0x00FF;
arr[0] = (in >> 8) & 0x00FF;
}
static void fromSint16BE(int16_t in, uint8_t *arr)
{
arr[1] = in & 0x00FF;
arr[0] = ((uint16_t)in >> 8) & 0x00FF;
}
WOPLFile *WOPL_Init(uint16_t melodic_banks, uint16_t percussive_banks)
{
WOPLFile *file = NULL;
if(melodic_banks == 0)
return NULL;
if(percussive_banks == 0)
return NULL;
file = (WOPLFile*)calloc(1, sizeof(WOPLFile));
if(!file)
return NULL;
file->banks_count_melodic = melodic_banks;
file->banks_count_percussion = percussive_banks;
file->banks_melodic = (WOPLBank*)calloc(1, sizeof(WOPLBank) * melodic_banks );
file->banks_percussive = (WOPLBank*)calloc(1, sizeof(WOPLBank) * percussive_banks );
return file;
}
void WOPL_Free(WOPLFile *file)
{
if(file)
{
if(file->banks_melodic)
free(file->banks_melodic);
if(file->banks_percussive)
free(file->banks_percussive);
free(file);
}
}
int WOPL_BanksCmp(const WOPLFile *bank1, const WOPLFile *bank2)
{
int res = 1;
res &= (bank1->version == bank2->version);
res &= (bank1->opl_flags == bank2->opl_flags);
res &= (bank1->volume_model == bank2->volume_model);
res &= (bank1->banks_count_melodic == bank2->banks_count_melodic);
res &= (bank1->banks_count_percussion == bank2->banks_count_percussion);
if(res)
{
int i;
for(i = 0; i < bank1->banks_count_melodic; i++)
res &= (memcmp(&bank1->banks_melodic[i], &bank2->banks_melodic[i], sizeof(WOPLBank)) == 0);
if(res)
{
for(i = 0; i < bank1->banks_count_percussion; i++)
res &= (memcmp(&bank1->banks_percussive[i], &bank2->banks_percussive[i], sizeof(WOPLBank)) == 0);
}
}
return res;
}
static void WOPL_parseInstrument(WOPLInstrument *ins, uint8_t *cursor, uint16_t version, uint8_t has_sounding_delays)
{
int l;
strncpy(ins->inst_name, (const char*)cursor, 32);
ins->inst_name[32] = '\0';
ins->note_offset1 = toSint16BE(cursor + 32);
ins->note_offset2 = toSint16BE(cursor + 34);
ins->midi_velocity_offset = (int8_t)cursor[36];
ins->second_voice_detune = (int8_t)cursor[37];
ins->percussion_key_number = cursor[38];
ins->inst_flags = cursor[39];
ins->fb_conn1_C0 = cursor[40];
ins->fb_conn2_C0 = cursor[41];
for(l = 0; l < 4; l++)
{
size_t off = 42 + (size_t)(l) * 5;
ins->operators[l].avekf_20 = cursor[off + 0];
ins->operators[l].ksl_l_40 = cursor[off + 1];
ins->operators[l].atdec_60 = cursor[off + 2];
ins->operators[l].susrel_80 = cursor[off + 3];
ins->operators[l].waveform_E0 = cursor[off + 4];
}
if((version >= 3) && has_sounding_delays)
{
ins->delay_on_ms = toUint16BE(cursor + 62);
ins->delay_off_ms = toUint16BE(cursor + 64);
}
}
static void WOPL_writeInstrument(WOPLInstrument *ins, uint8_t *cursor, uint16_t version, uint8_t has_sounding_delays)
{
int l;
strncpy((char*)cursor, ins->inst_name, 32);
fromSint16BE(ins->note_offset1, cursor + 32);
fromSint16BE(ins->note_offset2, cursor + 34);
cursor[36] = (uint8_t)ins->midi_velocity_offset;
cursor[37] = (uint8_t)ins->second_voice_detune;
cursor[38] = ins->percussion_key_number;
cursor[39] = ins->inst_flags;
cursor[40] = ins->fb_conn1_C0;
cursor[41] = ins->fb_conn2_C0;
for(l = 0; l < 4; l++)
{
size_t off = 42 + (size_t)(l) * 5;
cursor[off + 0] = ins->operators[l].avekf_20;
cursor[off + 1] = ins->operators[l].ksl_l_40;
cursor[off + 2] = ins->operators[l].atdec_60;
cursor[off + 3] = ins->operators[l].susrel_80;
cursor[off + 4] = ins->operators[l].waveform_E0;
}
if((version >= 3) && has_sounding_delays)
{
fromUint16BE(ins->delay_on_ms, cursor + 62);
fromUint16BE(ins->delay_off_ms, cursor + 64);
}
}
WOPLFile *WOPL_LoadBankFromMem(void *mem, size_t length, int *error)
{
WOPLFile *outFile = NULL;
uint16_t i = 0, j = 0, k = 0;
uint16_t version = 0;
uint16_t count_melodic_banks = 1;
uint16_t count_percusive_banks = 1;
uint8_t *cursor = (uint8_t *)mem;
WOPLBank *bankslots[2];
uint16_t bankslots_sizes[2];
#define SET_ERROR(err) \
{\
WOPL_Free(outFile);\
if(error)\
{\
*error = err;\
}\
}
#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; }
if(!cursor)
{
SET_ERROR(WOPL_ERR_NULL_POINTER);
return NULL;
}
{/* Magic number */
if(length < 11)
{
SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING);
return NULL;
}
if(memcmp(cursor, wopl3_magic, 11) != 0)
{
SET_ERROR(WOPL_ERR_BAD_MAGIC);
return NULL;
}
GO_FORWARD(11);
}
{/* Version code */
if(length < 2)
{
SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING);
return NULL;
}
version = toUint16LE(cursor);
if(version > wopl_latest_version)
{
SET_ERROR(WOPL_ERR_NEWER_VERSION);
return NULL;
}
GO_FORWARD(2);
}
{/* Header of WOPL */
uint8_t head[6];
if(length < 6)
{
SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING);
return NULL;
}
memcpy(head, cursor, 6);
count_melodic_banks = toUint16BE(head);
count_percusive_banks = toUint16BE(head + 2);
GO_FORWARD(6);
outFile = WOPL_Init(count_melodic_banks, count_percusive_banks);
if(!outFile)
{
SET_ERROR(WOPL_ERR_OUT_OF_MEMORY);
return NULL;
}
outFile->version = version;
outFile->opl_flags = head[4];
outFile->volume_model = head[5];
}
bankslots_sizes[0] = count_melodic_banks;
bankslots[0] = outFile->banks_melodic;
bankslots_sizes[1] = count_percusive_banks;
bankslots[1] = outFile->banks_percussive;
if(version >= 2) /* Bank names and LSB/MSB titles */
{
for(i = 0; i < 2; i++)
{
for(j = 0; j < bankslots_sizes[i]; j++)
{
if(length < 34)
{
SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING);
return NULL;
}
strncpy(bankslots[i][j].bank_name, (const char*)cursor, 32);
bankslots[i][j].bank_name[32] = '\0';
bankslots[i][j].bank_midi_lsb = cursor[32];
bankslots[i][j].bank_midi_msb = cursor[33];
GO_FORWARD(34);
}
}
}
{/* Read instruments data */
uint16_t insSize = 0;
if(version > 2)
insSize = WOPL_INST_SIZE_V3;
else
insSize = WOPL_INST_SIZE_V2;
for(i = 0; i < 2; i++)
{
if(length < (insSize * 128) * (size_t)bankslots_sizes[i])
{
SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING);
return NULL;
}
for(j = 0; j < bankslots_sizes[i]; j++)
{
for(k = 0; k < 128; k++)
{
WOPLInstrument *ins = &bankslots[i][j].ins[k];
WOPL_parseInstrument(ins, cursor, version, 1);
GO_FORWARD(insSize);
}
}
}
}
#undef GO_FORWARD
#undef SET_ERROR
return outFile;
}
int WOPL_LoadInstFromMem(WOPIFile *file, void *mem, size_t length)
{
uint16_t version = 0;
uint8_t *cursor = (uint8_t *)mem;
uint16_t ins_size;
if(!cursor)
return WOPL_ERR_NULL_POINTER;
#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; }
{/* Magic number */
if(length < 11)
return WOPL_ERR_UNEXPECTED_ENDING;
if(memcmp(cursor, wopli_magic, 11) != 0)
return WOPL_ERR_BAD_MAGIC;
GO_FORWARD(11);
}
{/* Version code */
if(length < 2)
return WOPL_ERR_UNEXPECTED_ENDING;
version = toUint16LE(cursor);
if(version > wopl_latest_version)
return WOPL_ERR_NEWER_VERSION;
GO_FORWARD(2);
}
{/* is drum flag */
if(length < 1)
return WOPL_ERR_UNEXPECTED_ENDING;
file->is_drum = *cursor;
GO_FORWARD(1);
}
if(version > 2)
/* Skip sounding delays are not part of single-instrument file
* two sizes of uint16_t will be subtracted */
ins_size = WOPL_INST_SIZE_V3 - (sizeof(uint16_t) * 2);
else
ins_size = WOPL_INST_SIZE_V2;
if(length < ins_size)
return WOPL_ERR_UNEXPECTED_ENDING;
WOPL_parseInstrument(&file->inst, cursor, version, 0);
GO_FORWARD(ins_size);
return WOPL_ERR_OK;
#undef GO_FORWARD
}
size_t WOPL_CalculateBankFileSize(WOPLFile *file, uint16_t version)
{
size_t final_size = 0;
size_t ins_size = 0;
if(version == 0)
version = wopl_latest_version;
if(!file)
return 0;
final_size += 11 + 2 + 2 + 2 + 1 + 1;
/*
* Magic number,
* Version,
* Count of melodic banks,
* Count of percussive banks,
* Chip specific flags
* Volume Model
*/
if(version >= 2)
{
/* Melodic banks meta-data */
final_size += (32 + 1 + 1) * file->banks_count_melodic;
/* Percussive banks meta-data */
final_size += (32 + 1 + 1) * file->banks_count_percussion;
}
if(version >= 3)
ins_size = WOPL_INST_SIZE_V3;
else
ins_size = WOPL_INST_SIZE_V2;
/* Melodic instruments */
final_size += (ins_size * 128) * file->banks_count_melodic;
/* Percusive instruments */
final_size += (ins_size * 128) * file->banks_count_percussion;
return final_size;
}
size_t WOPL_CalculateInstFileSize(WOPIFile *file, uint16_t version)
{
size_t final_size = 0;
size_t ins_size = 0;
if(version == 0)
version = wopl_latest_version;
if(!file)
return 0;
final_size += 11 + 2 + 1;
/*
* Magic number,
* version,
* is percussive instrument
*/
if(version >= 3)
ins_size = WOPL_INST_SIZE_V3;
else
ins_size = WOPL_INST_SIZE_V2;
final_size += ins_size * 128;
return final_size;
}
int WOPL_SaveBankToMem(WOPLFile *file, void *dest_mem, size_t length, uint16_t version, uint16_t force_gm)
{
uint8_t *cursor = (uint8_t *)dest_mem;
uint16_t ins_size = 0;
uint16_t i, j, k;
uint16_t banks_melodic = force_gm ? 1 : file->banks_count_melodic;
uint16_t banks_percusive = force_gm ? 1 : file->banks_count_percussion;
WOPLBank *bankslots[2];
uint16_t bankslots_sizes[2];
if(version == 0)
version = wopl_latest_version;
#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; }
if(length < 11)
return WOPL_ERR_UNEXPECTED_ENDING;
memcpy(cursor, wopl3_magic, 11);
GO_FORWARD(11);
if(length < 2)
return WOPL_ERR_UNEXPECTED_ENDING;
fromUint16LE(version, cursor);
GO_FORWARD(2);
if(length < 2)
return WOPL_ERR_UNEXPECTED_ENDING;
fromUint16BE(banks_melodic, cursor);
GO_FORWARD(2);
if(length < 2)
return WOPL_ERR_UNEXPECTED_ENDING;
fromUint16BE(banks_percusive, cursor);
GO_FORWARD(2);
if(length < 2)
return WOPL_ERR_UNEXPECTED_ENDING;
cursor[0] = file->opl_flags;
cursor[1] = file->volume_model;
GO_FORWARD(2);
bankslots[0] = file->banks_melodic;
bankslots_sizes[0] = banks_melodic;
bankslots[1] = file->banks_percussive;
bankslots_sizes[1] = banks_percusive;
if(version >= 2)
{
for(i = 0; i < 2; i++)
{
for(j = 0; j < bankslots_sizes[i]; j++)
{
if(length < 34)
return WOPL_ERR_UNEXPECTED_ENDING;
strncpy((char*)cursor, bankslots[i][j].bank_name, 32);
cursor[32] = bankslots[i][j].bank_midi_lsb;
cursor[33] = bankslots[i][j].bank_midi_msb;
GO_FORWARD(34);
}
}
}
{/* Write instruments data */
if(version >= 3)
ins_size = WOPL_INST_SIZE_V3;
else
ins_size = WOPL_INST_SIZE_V2;
for(i = 0; i < 2; i++)
{
if(length < (ins_size * 128) * (size_t)bankslots_sizes[i])
return WOPL_ERR_UNEXPECTED_ENDING;
for(j = 0; j < bankslots_sizes[i]; j++)
{
for(k = 0; k < 128; k++)
{
WOPLInstrument *ins = &bankslots[i][j].ins[k];
WOPL_writeInstrument(ins, cursor, version, 1);
GO_FORWARD(ins_size);
}
}
}
}
return WOPL_ERR_OK;
#undef GO_FORWARD
}
int WOPL_SaveInstToMem(WOPIFile *file, void *dest_mem, size_t length, uint16_t version)
{
uint8_t *cursor = (uint8_t *)dest_mem;
uint16_t ins_size;
if(!cursor)
return WOPL_ERR_NULL_POINTER;
if(version == 0)
version = wopl_latest_version;
#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; }
{/* Magic number */
if(length < 11)
return WOPL_ERR_UNEXPECTED_ENDING;
memcpy(cursor, wopli_magic, 11);
GO_FORWARD(11);
}
{/* Version code */
if(length < 2)
return WOPL_ERR_UNEXPECTED_ENDING;
fromUint16LE(version, cursor);
GO_FORWARD(2);
}
{/* is drum flag */
if(length < 1)
return WOPL_ERR_UNEXPECTED_ENDING;
*cursor = file->is_drum;
GO_FORWARD(1);
}
if(version > 2)
/* Skip sounding delays are not part of single-instrument file
* two sizes of uint16_t will be subtracted */
ins_size = WOPL_INST_SIZE_V3 - (sizeof(uint16_t) * 2);
else
ins_size = WOPL_INST_SIZE_V2;
if(length < ins_size)
return WOPL_ERR_UNEXPECTED_ENDING;
WOPL_writeInstrument(&file->inst, cursor, version, 0);
GO_FORWARD(ins_size);
return WOPL_ERR_OK;
#undef GO_FORWARD
}

View file

@ -0,0 +1,290 @@
/*
* Wohlstand's OPL3 Bank File - a bank format to store OPL3 timbre data and setup
*
* Copyright (c) 2015-2018 Vitaly Novichkov <admin@wohlnet.ru>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef WOPL_FILE_H
#define WOPL_FILE_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#if !defined(__STDC_VERSION__) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ < 199901L)) \
|| defined(__STRICT_ANSI__) || !defined(__cplusplus)
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef signed short int int16_t;
typedef unsigned short int uint16_t;
#endif
/* Global OPL flags */
typedef enum WOPLFileFlags
{
/* Enable Deep-Tremolo flag */
WOPL_FLAG_DEEP_TREMOLO = 0x01,
/* Enable Deep-Vibrato flag */
WOPL_FLAG_DEEP_VIBRATO = 0x02
} WOPLFileFlags;
/* Volume scaling model implemented in the libADLMIDI */
typedef enum WOPL_VolumeModel
{
WOPL_VM_Generic = 0,
WOPL_VM_Native,
WOPL_VM_DMX,
WOPL_VM_Apogee,
WOPL_VM_Win9x
} WOPL_VolumeModel;
typedef enum WOPL_InstrumentFlags
{
/* Is two-operator single-voice instrument (no flags) */
WOPL_Ins_2op = 0x00,
/* Is true four-operator instrument */
WOPL_Ins_4op = 0x01,
/* Is pseudo four-operator (two 2-operator voices) instrument */
WOPL_Ins_Pseudo4op = 0x02,
/* Is a blank instrument entry */
WOPL_Ins_IsBlank = 0x04,
/* RythmMode flags mask */
WOPL_RythmModeMask = 0x38,
/* Mask of the flags range */
WOPL_Ins_ALL_MASK = 0x07
} WOPL_InstrumentFlags;
typedef enum WOPL_RythmMode
{
/* RythmMode: BassDrum */
WOPL_RM_BassDrum = 0x08,
/* RythmMode: Snare */
WOPL_RM_Snare = 0x10,
/* RythmMode: TomTom */
WOPL_RM_TomTom = 0x18,
/* RythmMode: Cymbell */
WOPL_RM_Cymball = 0x20,
/* RythmMode: HiHat */
WOPL_RM_HiHat = 0x28
} WOPL_RythmMode;
/* Error codes */
typedef enum WOPL_ErrorCodes
{
WOPL_ERR_OK = 0,
/* Magic number is not maching */
WOPL_ERR_BAD_MAGIC,
/* Too short file */
WOPL_ERR_UNEXPECTED_ENDING,
/* Zero banks count */
WOPL_ERR_INVALID_BANKS_COUNT,
/* Version of file is newer than supported by current version of library */
WOPL_ERR_NEWER_VERSION,
/* Out of memory */
WOPL_ERR_OUT_OF_MEMORY,
/* Given null pointer memory data */
WOPL_ERR_NULL_POINTER
} WOPL_ErrorCodes;
/* Operator indeces inside of Instrument Entry */
#define WOPL_OP_CARRIER1 0
#define WOPL_OP_MODULATOR1 1
#define WOPL_OP_CARRIER2 2
#define WOPL_OP_MODULATOR2 3
/* OPL3 Oerators data */
typedef struct WOPLOperator
{
/* AM/Vib/Env/Ksr/FMult characteristics */
uint8_t avekf_20;
/* Key Scale Level / Total level register data */
uint8_t ksl_l_40;
/* Attack / Decay */
uint8_t atdec_60;
/* Systain and Release register data */
uint8_t susrel_80;
/* Wave form */
uint8_t waveform_E0;
} WOPLOperator;
/* Instrument entry */
typedef struct WOPLInstrument
{
/* Title of the instrument */
char inst_name[34];
/* MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */
int16_t note_offset1;
/* MIDI note key (half-tone) offset for a second voice in pseudo-4-op mode */
int16_t note_offset2;
/* MIDI note velocity offset (taken from Apogee TMB format) */
int8_t midi_velocity_offset;
/* Second voice detune level (taken from DMX OP2) */
int8_t second_voice_detune;
/* Percussion MIDI base tone number at which this drum will be played */
uint8_t percussion_key_number;
/* Enum WOPL_InstrumentFlags */
uint8_t inst_flags;
/* Feedback&Connection register for first and second operators */
uint8_t fb_conn1_C0;
/* Feedback&Connection register for third and fourth operators */
uint8_t fb_conn2_C0;
/* Operators register data */
WOPLOperator operators[4];
/* Millisecond delay of sounding while key is on */
uint16_t delay_on_ms;
/* Millisecond delay of sounding after key off */
uint16_t delay_off_ms;
} WOPLInstrument;
/* Bank entry */
typedef struct WOPLBank
{
/* Name of bank */
char bank_name[33];
/* MIDI Bank LSB code */
uint8_t bank_midi_lsb;
/* MIDI Bank MSB code */
uint8_t bank_midi_msb;
/* Instruments data of this bank */
WOPLInstrument ins[128];
} WOPLBank;
/* Instrument data file */
typedef struct WOPIFile
{
/* Version of instrument file */
uint16_t version;
/* Is this a percussion instrument */
uint8_t is_drum;
/* Instrument data */
WOPLInstrument inst;
} WOPIFile;
/* Bank data file */
typedef struct WOPLFile
{
/* Version of bank file */
uint16_t version;
/* Count of melodic banks in this file */
uint16_t banks_count_melodic;
/* Count of percussion banks in this file */
uint16_t banks_count_percussion;
/* Enum WOPLFileFlags */
uint8_t opl_flags;
/* Enum WOPL_VolumeModel */
uint8_t volume_model;
/* dynamically allocated data Melodic banks array */
WOPLBank *banks_melodic;
/* dynamically allocated data Percussive banks array */
WOPLBank *banks_percussive;
} WOPLFile;
/**
* @brief Initialize blank WOPL data structure with allocated bank data
* @param melodic_banks Count of melodic banks
* @param percussive_banks Count of percussive banks
* @return pointer to heap-allocated WOPL data structure or NULL when out of memory or incorrectly given banks counts
*/
extern WOPLFile *WOPL_Init(uint16_t melodic_banks, uint16_t percussive_banks);
/**
* @brief Clean up WOPL data file (all allocated bank arrays will be fried too)
* @param file pointer to heap-allocated WOPL data structure
*/
extern void WOPL_Free(WOPLFile *file);
/**
* @brief Compare two bank entries
* @param bank1 First bank
* @param bank2 Second bank
* @return 1 if banks are equal or 0 if there are different
*/
extern int WOPL_BanksCmp(const WOPLFile *bank1, const WOPLFile *bank2);
/**
* @brief Load WOPL bank file from the memory.
* WOPL data structure will be allocated. (don't forget to clear it with WOPL_Free() after use!)
* @param mem Pointer to memory block contains raw WOPL bank file data
* @param length Length of given memory block
* @param error pointer to integer to return an error code. Pass NULL if you don't want to use error codes.
* @return Heap-allocated WOPL file data structure or NULL if any error has occouped
*/
extern WOPLFile *WOPL_LoadBankFromMem(void *mem, size_t length, int *error);
/**
* @brief Load WOPI instrument file from the memory.
* You must allocate WOPIFile structure by yourself and give the pointer to it.
* @param file Pointer to destinition WOPIFile structure to fill it with parsed data.
* @param mem Pointer to memory block contains raw WOPI instrument file data
* @param length Length of given memory block
* @return 0 if no errors occouped, or an error code of WOPL_ErrorCodes enumeration
*/
extern int WOPL_LoadInstFromMem(WOPIFile *file, void *mem, size_t length);
/**
* @brief Calculate the size of the output memory block
* @param file Heap-allocated WOPL file data structure
* @param version Destinition version of the file
* @return Size of the raw WOPL file data
*/
extern size_t WOPL_CalculateBankFileSize(WOPLFile *file, uint16_t version);
/**
* @brief Calculate the size of the output memory block
* @param file Pointer to WOPI file data structure
* @param version Destinition version of the file
* @return Size of the raw WOPI file data
*/
extern size_t WOPL_CalculateInstFileSize(WOPIFile *file, uint16_t version);
/**
* @brief Write raw WOPL into given memory block
* @param file Heap-allocated WOPL file data structure
* @param dest_mem Destinition memory block pointer
* @param length Length of destinition memory block
* @param version Wanted WOPL version
* @param force_gm Force GM set in saved bank file
* @return Error code or 0 on success
*/
extern int WOPL_SaveBankToMem(WOPLFile *file, void *dest_mem, size_t length, uint16_t version, uint16_t force_gm);
/**
* @brief Write raw WOPI into given memory block
* @param file Pointer to WOPI file data structure
* @param dest_mem Destinition memory block pointer
* @param length Length of destinition memory block
* @param version Wanted WOPI version
* @return Error code or 0 on success
*/
extern int WOPL_SaveInstToMem(WOPIFile *file, void *dest_mem, size_t length, uint16_t version);
#ifdef __cplusplus
}
#endif
#endif /* WOPL_FILE_H */

View file

@ -319,7 +319,6 @@ protected:
class ADLMIDIDevice : public SoftSynthMIDIDevice
{
struct ADL_MIDIPlayer *Renderer;
TArray<int16_t> shortbuffer;
public:
ADLMIDIDevice(const char *args);
~ADLMIDIDevice();
@ -338,7 +337,6 @@ protected:
class OPNMIDIDevice : public SoftSynthMIDIDevice
{
struct OPN2_MIDIPlayer *Renderer;
TArray<int16_t> shortbuffer;
public:
OPNMIDIDevice(const char *args);
~OPNMIDIDevice();

View file

@ -56,6 +56,22 @@ CUSTOM_CVAR(Int, adl_chips_count, 6, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
}
}
CUSTOM_CVAR(Int, adl_emulator_id, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (currSong != nullptr && currSong->GetDeviceType() == MDEV_ADL)
{
MIDIDeviceChanged(-1, true);
}
}
CUSTOM_CVAR(Bool, adl_run_at_pcm_rate, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (currSong != nullptr && currSong->GetDeviceType() == MDEV_ADL)
{
MIDIDeviceChanged(-1, true);
}
}
CUSTOM_CVAR(Int, adl_bank, 14, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (currSong != nullptr && currSong->GetDeviceType() == MDEV_ADL)
@ -64,6 +80,22 @@ CUSTOM_CVAR(Int, adl_bank, 14, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
}
}
CUSTOM_CVAR(Bool, adl_use_custom_bank, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (currSong != nullptr && currSong->GetDeviceType() == MDEV_ADL)
{
MIDIDeviceChanged(-1, true);
}
}
CUSTOM_CVAR(String, adl_custom_bank, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (adl_use_custom_bank && currSong != nullptr && currSong->GetDeviceType() == MDEV_ADL)
{
MIDIDeviceChanged(-1, true);
}
}
CUSTOM_CVAR(Int, adl_volume_model, ADLMIDI_VolumeModel_DMX, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (currSong != nullptr && currSong->GetDeviceType() == MDEV_ADL)
@ -84,7 +116,18 @@ ADLMIDIDevice::ADLMIDIDevice(const char *args)
Renderer = adl_init(44100); // todo: make it configurable
if (Renderer != nullptr)
{
adl_setBank(Renderer, (int)adl_bank);
adl_switchEmulator(Renderer, (int)adl_emulator_id);
adl_setRunAtPcmRate(Renderer, (int)adl_run_at_pcm_rate);
// todo: Implement handling of external or in-resources WOPL bank files and load
/*
if(adl_use_custom_bank)
{
adl_openBankFile(Renderer, (char*)adl_bank_file);
adl_openBankData(Renderer, (char*)adl_bank, (unsigned long)size);
}
else
*/
adl_setBank(Renderer, (int)adl_bank);
adl_setNumChips(Renderer, (int)adl_chips_count);
adl_setVolumeRangeModel(Renderer, (int)adl_volume_model);
}
@ -176,6 +219,13 @@ void ADLMIDIDevice::HandleLongEvent(const uint8_t *data, int len)
{
}
static const ADLMIDI_AudioFormat audio_output_format =
{
ADLMIDI_SampleType_F32,
sizeof(float),
2 * sizeof(float)
};
//==========================================================================
//
// ADLMIDIDevice :: ComputeOutput
@ -184,11 +234,12 @@ void ADLMIDIDevice::HandleLongEvent(const uint8_t *data, int len)
void ADLMIDIDevice::ComputeOutput(float *buffer, int len)
{
if ((int)shortbuffer.Size() < len*2) shortbuffer.Resize(len*2);
auto result = adl_generate(Renderer, len*2, &shortbuffer[0]);
for(int i=0; i<result; i++)
ADL_UInt8* left = reinterpret_cast<ADL_UInt8*>(buffer);
ADL_UInt8* right = reinterpret_cast<ADL_UInt8*>(buffer + 1);
auto result = adl_generateFormat(Renderer, len * 2, left, right, &audio_output_format);
for(int i=0; i < result; i++)
{
buffer[i] = shortbuffer[i] * (3.5f/32768.f);
buffer[i] *= 3.5f;
}
}

View file

@ -58,6 +58,38 @@ CUSTOM_CVAR(Int, opn_chips_count, 8, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
}
}
CUSTOM_CVAR(Int, opn_emulator_id, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN)
{
MIDIDeviceChanged(-1, true);
}
}
CUSTOM_CVAR(Bool, opn_run_at_pcm_rate, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN)
{
MIDIDeviceChanged(-1, true);
}
}
CUSTOM_CVAR(Bool, opn_use_custom_bank, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN)
{
MIDIDeviceChanged(-1, true);
}
}
CUSTOM_CVAR(String, opn_custom_bank, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (opn_use_custom_bank && currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN)
{
MIDIDeviceChanged(-1, true);
}
}
//==========================================================================
//
// OPNMIDIDevice Constructor
@ -70,6 +102,17 @@ OPNMIDIDevice::OPNMIDIDevice(const char *args)
Renderer = opn2_init(44100); // todo: make it configurable
if (Renderer != nullptr)
{
opn2_switchEmulator(Renderer, (int)opn_emulator_id);
opn2_setRunAtPcmRate(Renderer, (int)opn_run_at_pcm_rate);
// todo: Implement handling of external or in-resources WOPN bank files and load
/*
if(opn_use_custom_bank)
{
opn2_openBankFile(Renderer, (char*)opn_bank_file);
opn2_openBankData(Renderer, (char*)opn_bank, (long)size);
}
else
*/
int lump = Wads.CheckNumForFullName("xg.wopn");
if (lump < 0)
{
@ -167,6 +210,13 @@ void OPNMIDIDevice::HandleLongEvent(const uint8_t *data, int len)
{
}
static const OPNMIDI_AudioFormat audio_output_format =
{
OPNMIDI_SampleType_F32,
sizeof(float),
2 * sizeof(float)
};
//==========================================================================
//
// OPNMIDIDevice :: ComputeOutput
@ -175,11 +225,7 @@ void OPNMIDIDevice::HandleLongEvent(const uint8_t *data, int len)
void OPNMIDIDevice::ComputeOutput(float *buffer, int len)
{
if ((int)shortbuffer.Size() < len*2) shortbuffer.Resize(len*2);
auto result = opn2_generate(Renderer, len*2, &shortbuffer[0]);
for(int i=0; i<result; i++)
{
buffer[i] = shortbuffer[i] * (1.f/32768.f);
}
OPN2_UInt8* left = reinterpret_cast<OPN2_UInt8*>(buffer);
OPN2_UInt8* right = reinterpret_cast<OPN2_UInt8*>(buffer + 1);
opn2_generateFormat(Renderer, len * 2, left, right, &audio_output_format);
}

View file

@ -2,7 +2,7 @@
// Based on Gens 2.10 ym2612.c
#include "Ym2612_ChipEmu.h"
#include "Ym2612_Emu.h"
#include <assert.h>
#include <stdlib.h>
@ -110,7 +110,7 @@ struct state_t
int TimerBcnt; // timerB counter = valeur courante du Timer B
int Mode; // Mode actuel des voie 3 et 6 (normal / special)
int DAC; // DAC enabled flag
channel_t CHANNEL[OPNMIDI_Ym2612_Emu::channel_count]; // Les 6 voies du YM2612
channel_t CHANNEL[Ym2612_Emu::channel_count]; // Les 6 voies du YM2612
int REG[2][0x100]; // Sauvegardes des valeurs de tout les registres, c'est facultatif
// cela nous rend le debuggage plus facile
};
@ -255,9 +255,9 @@ static const unsigned char LFO_FMS_TAB [8] =
inline void YM2612_Special_Update() { }
struct OPNMIDI_Ym2612_Impl
struct Ym2612_Impl
{
enum { channel_count = OPNMIDI_Ym2612_Emu::channel_count };
enum { channel_count = Ym2612_Emu::channel_count };
state_t YM2612;
int mute_mask;
@ -274,10 +274,10 @@ struct OPNMIDI_Ym2612_Impl
void write0( int addr, int data );
void write1( int addr, int data );
void run_timer( int );
void run( int pair_count, OPNMIDI_Ym2612_Emu::sample_t* );
void run( int pair_count, Ym2612_Emu::sample_t* );
};
void OPNMIDI_Ym2612_Impl::KEY_ON( channel_t& ch, int nsl)
void Ym2612_Impl::KEY_ON( channel_t& ch, int nsl)
{
slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot
@ -300,7 +300,7 @@ void OPNMIDI_Ym2612_Impl::KEY_ON( channel_t& ch, int nsl)
}
void OPNMIDI_Ym2612_Impl::KEY_OFF(channel_t& ch, int nsl)
void Ym2612_Impl::KEY_OFF(channel_t& ch, int nsl)
{
slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot
@ -318,7 +318,7 @@ void OPNMIDI_Ym2612_Impl::KEY_OFF(channel_t& ch, int nsl)
}
int OPNMIDI_Ym2612_Impl::SLOT_SET( int Adr, int data )
int Ym2612_Impl::SLOT_SET( int Adr, int data )
{
int nch = Adr & 3;
if ( nch == 3 )
@ -420,7 +420,7 @@ int OPNMIDI_Ym2612_Impl::SLOT_SET( int Adr, int data )
}
int OPNMIDI_Ym2612_Impl::CHANNEL_SET( int Adr, int data )
int Ym2612_Impl::CHANNEL_SET( int Adr, int data )
{
int num = Adr & 3;
if ( num == 3 )
@ -522,7 +522,7 @@ int OPNMIDI_Ym2612_Impl::CHANNEL_SET( int Adr, int data )
}
int OPNMIDI_Ym2612_Impl::YM_SET(int Adr, int data)
int Ym2612_Impl::YM_SET(int Adr, int data)
{
switch ( Adr )
{
@ -628,7 +628,7 @@ int OPNMIDI_Ym2612_Impl::YM_SET(int Adr, int data)
return 0;
}
void OPNMIDI_Ym2612_Impl::set_rate( double sample_rate, double clock_rate )
void Ym2612_Impl::set_rate( double sample_rate, double clock_rate )
{
assert( sample_rate );
assert( clock_rate > sample_rate );
@ -824,11 +824,11 @@ void OPNMIDI_Ym2612_Impl::set_rate( double sample_rate, double clock_rate )
reset();
}
const char* OPNMIDI_Ym2612_Emu::set_rate( double sample_rate, double clock_rate )
const char* Ym2612_Emu::set_rate( double sample_rate, double clock_rate )
{
if ( !impl )
{
impl = (OPNMIDI_Ym2612_Impl*) malloc( sizeof *impl );
impl = (Ym2612_Impl*) malloc( sizeof *impl );
if ( !impl )
return "Out of memory";
impl->mute_mask = 0;
@ -840,12 +840,12 @@ const char* OPNMIDI_Ym2612_Emu::set_rate( double sample_rate, double clock_rate
return 0;
}
OPNMIDI_Ym2612_Emu::~OPNMIDI_Ym2612_Emu()
Ym2612_Emu::~Ym2612_Emu()
{
free( impl );
}
inline void OPNMIDI_Ym2612_Impl::write0( int opn_addr, int data )
inline void Ym2612_Impl::write0( int opn_addr, int data )
{
assert( (unsigned) data <= 0xFF );
@ -865,7 +865,7 @@ inline void OPNMIDI_Ym2612_Impl::write0( int opn_addr, int data )
}
}
inline void OPNMIDI_Ym2612_Impl::write1( int opn_addr, int data )
inline void Ym2612_Impl::write1( int opn_addr, int data )
{
assert( (unsigned) data <= 0xFF );
@ -880,12 +880,12 @@ inline void OPNMIDI_Ym2612_Impl::write1( int opn_addr, int data )
}
}
void OPNMIDI_Ym2612_Emu::reset()
void Ym2612_Emu::reset()
{
impl->reset();
}
void OPNMIDI_Ym2612_Impl::reset()
void Ym2612_Impl::reset()
{
g.LFOcnt = 0;
YM2612.TimerA = 0;
@ -949,17 +949,17 @@ void OPNMIDI_Ym2612_Impl::reset()
write0( 0x2A, 0x80 );
}
void OPNMIDI_Ym2612_Emu::write0( int addr, int data )
void Ym2612_Emu::write0( int addr, int data )
{
impl->write0( addr, data );
}
void OPNMIDI_Ym2612_Emu::write1( int addr, int data )
void Ym2612_Emu::write1( int addr, int data )
{
impl->write1( addr, data );
}
void OPNMIDI_Ym2612_Emu::mute_voices( int mask ) { impl->mute_mask = mask; }
void Ym2612_Emu::mute_voices( int mask ) { impl->mute_mask = mask; }
static void update_envelope_( slot_t* sl )
{
@ -1033,14 +1033,14 @@ inline void update_envelope( slot_t& sl )
template<int algo>
struct ym2612_update_chan {
static void func( tables_t&, channel_t&, OPNMIDI_Ym2612_Emu::sample_t*, int );
static void func( tables_t&, channel_t&, Ym2612_Emu::sample_t*, int );
};
typedef void (*ym2612_update_chan_t)( tables_t&, channel_t&, OPNMIDI_Ym2612_Emu::sample_t*, int );
typedef void (*ym2612_update_chan_t)( tables_t&, channel_t&, Ym2612_Emu::sample_t*, int );
template<int algo>
void ym2612_update_chan<algo>::func( tables_t& g, channel_t& ch,
OPNMIDI_Ym2612_Emu::sample_t* buf, int length )
Ym2612_Emu::sample_t* buf, int length )
{
int not_end = ch.SLOT [S3].Ecnt - ENV_END;
@ -1201,7 +1201,7 @@ static const ym2612_update_chan_t UPDATE_CHAN [8] = {
&ym2612_update_chan<7>::func
};
void OPNMIDI_Ym2612_Impl::run_timer( int length )
void Ym2612_Impl::run_timer( int length )
{
int const step = 6;
int remain = length;
@ -1247,7 +1247,7 @@ void OPNMIDI_Ym2612_Impl::run_timer( int length )
while ( remain > 0 );
}
void OPNMIDI_Ym2612_Impl::run( int pair_count, OPNMIDI_Ym2612_Emu::sample_t* out )
void Ym2612_Impl::run( int pair_count, Ym2612_Emu::sample_t* out )
{
if ( pair_count <= 0 )
return;
@ -1316,4 +1316,4 @@ void OPNMIDI_Ym2612_Impl::run( int pair_count, OPNMIDI_Ym2612_Emu::sample_t* out
g.LFOcnt += g.LFOinc * pair_count;
}
void OPNMIDI_Ym2612_Emu::run( int pair_count, sample_t* out ) { impl->run( pair_count, out ); }
void Ym2612_Emu::run( int pair_count, sample_t* out ) { impl->run( pair_count, out ); }

View file

@ -4,31 +4,31 @@
#ifndef YM2612_EMU_H
#define YM2612_EMU_H
struct OPNMIDI_Ym2612_Impl;
struct Ym2612_Impl;
class OPNMIDI_Ym2612_Emu {
OPNMIDI_Ym2612_Impl* impl;
class Ym2612_Emu {
Ym2612_Impl* impl;
public:
OPNMIDI_Ym2612_Emu() { impl = 0; }
~OPNMIDI_Ym2612_Emu();
Ym2612_Emu() { impl = 0; }
~Ym2612_Emu();
// Set output sample rate and chip clock rates, in Hz. Returns non-zero
// if error.
const char* set_rate( double sample_rate, double clock_rate );
// Reset to power-up state
void reset();
// Mute voice n if bit n (1 << n) of mask is set
enum { channel_count = 6 };
void mute_voices( int mask );
// Write addr to register 0 then data to register 1
void write0( int addr, int data );
// Write addr to register 2 then data to register 3
void write1( int addr, int data );
// Run and add pair_count samples into current output buffer contents
typedef short sample_t;
enum { out_chan_count = 2 }; // stereo

View file

@ -0,0 +1,52 @@
#include "gens_opn2.h"
#include <cstring>
#include "gens/Ym2612_Emu.h"
GensOPN2::GensOPN2()
: chip(new Ym2612_Emu())
{
setRate(m_rate, m_clock);
}
GensOPN2::~GensOPN2()
{
delete chip;
}
void GensOPN2::setRate(uint32_t rate, uint32_t clock)
{
OPNChipBaseBufferedT::setRate(rate, clock);
uint32_t chipRate = isRunningAtPcmRate() ? rate : static_cast<uint32_t>(nativeRate);
chip->set_rate(chipRate, clock); // implies reset()
}
void GensOPN2::reset()
{
OPNChipBaseBufferedT::reset();
chip->reset();
}
void GensOPN2::writeReg(uint32_t port, uint16_t addr, uint8_t data)
{
switch (port)
{
case 0:
chip->write0(addr, data);
break;
case 1:
chip->write1(addr, data);
break;
}
}
void GensOPN2::nativeGenerateN(int16_t *output, size_t frames)
{
std::memset(output, 0, frames * sizeof(int16_t) * 2);
chip->run((int)frames, output);
}
const char *GensOPN2::emulatorName()
{
return "GENS 2.10 OPN2";
}

View file

@ -0,0 +1,24 @@
#ifndef GENS_OPN2_H
#define GENS_OPN2_H
#include "opn_chip_base.h"
class Ym2612_Emu;
class GensOPN2 final : public OPNChipBaseBufferedT<GensOPN2>
{
Ym2612_Emu *chip;
public:
GensOPN2();
~GensOPN2() override;
bool canRunAtPcmRate() const override { return true; }
void setRate(uint32_t rate, uint32_t clock) override;
void reset() override;
void writeReg(uint32_t port, uint16_t addr, uint8_t data) override;
void nativePreGenerate() override {}
void nativePostGenerate() override {}
void nativeGenerateN(int16_t *output, size_t frames) override;
const char *emulatorName() override;
};
#endif // GENS_OPN2_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,164 @@
/*
File: fm.h -- header file for software emulation for FM sound generator
*/
#ifndef FM_HHHHH
#define FM_HHHHH
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "mamedef.h"
/* --- select emulation chips --- */
/*
#define BUILD_YM2203 (HAS_YM2203) // build YM2203(OPN) emulator
#define BUILD_YM2608 (HAS_YM2608) // build YM2608(OPNA) emulator
#define BUILD_YM2610 (HAS_YM2610) // build YM2610(OPNB) emulator
#define BUILD_YM2610B (HAS_YM2610B) // build YM2610B(OPNB?)emulator
#define BUILD_YM2612 (HAS_YM2612) // build YM2612(OPN2) emulator
#define BUILD_YM3438 (HAS_YM3438) // build YM3438(OPN) emulator
*/
#define BUILD_YM2203 0
#define BUILD_YM2608 0
#define BUILD_YM2610 0
#define BUILD_YM2610B 0
#define BUILD_YM2612 1
#define BUILD_YM3438 0
#define FM_BUSY_FLAG_SUPPORT 0
/* select bit size of output : 8 or 16 */
#define FM_SAMPLE_BITS 16
/* select timer system internal or external */
#define FM_INTERNAL_TIMER 1
/* --- speedup optimize --- */
/* busy flag enulation , The definition of FM_GET_TIME_NOW() is necessary. */
/* #define FM_BUSY_FLAG_SUPPORT 1 */
/* --- external SSG(YM2149/AY-3-8910)emulator interface port */
/* used by YM2203,YM2608,and YM2610 */
typedef struct _ssg_callbacks ssg_callbacks;
struct _ssg_callbacks
{
void (*set_clock)(void *param, int clock);
void (*write)(void *param, int address, int data);
int (*read)(void *param);
void (*reset)(void *param);
};
/* --- external callback funstions for realtime update --- */
#if FM_BUSY_FLAG_SUPPORT
#define TIME_TYPE attotime
#define UNDEFINED_TIME attotime_zero
#define FM_GET_TIME_NOW(machine) timer_get_time(machine)
#define ADD_TIMES(t1, t2) attotime_add((t1), (t2))
#define COMPARE_TIMES(t1, t2) attotime_compare((t1), (t2))
#define MULTIPLY_TIME_BY_INT(t,i) attotime_mul(t, i)
#endif
/* compiler dependence */
#if 0
#ifndef OSD_CPU_H
#define OSD_CPU_H
typedef unsigned char UINT8; /* unsigned 8bit */
typedef unsigned short UINT16; /* unsigned 16bit */
typedef unsigned int UINT32; /* unsigned 32bit */
typedef signed char INT8; /* signed 8bit */
typedef signed short INT16; /* signed 16bit */
typedef signed int INT32; /* signed 32bit */
#endif /* OSD_CPU_H */
#endif
typedef stream_sample_t FMSAMPLE;
/*
#if (FM_SAMPLE_BITS==16)
typedef INT16 FMSAMPLE;
#endif
#if (FM_SAMPLE_BITS==8)
typedef unsigned char FMSAMPLE;
#endif
*/
typedef void (*FM_TIMERHANDLER)(void *param,int c,int cnt,int clock);
typedef void (*FM_IRQHANDLER)(void *param,int irq);
/* FM_TIMERHANDLER : Stop or Start timer */
/* int n = chip number */
/* int c = Channel 0=TimerA,1=TimerB */
/* int count = timer count (0=stop) */
/* doube stepTime = step time of one count (sec.)*/
/* FM_IRQHHANDLER : IRQ level changing sense */
/* int n = chip number */
/* int irq = IRQ level 0=OFF,1=ON */
#if (BUILD_YM2612||BUILD_YM3438)
/**
* @brief Initialize chip and return the instance
* @param param Unused, keep NULL
* @param baseclock YM2612 clock
* @param rate Output sample rate
* @param TimerHandler Keep NULL
* @param IRQHandler Keep NULL
* @return Chip instance or NULL on any error
*/
void * ym2612_init(void *param, int baseclock, int rate,
FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler);
/**
* @brief Free chip instance
* @param chip Chip instance
*/
void ym2612_shutdown(void *chip);
/**
* @brief Reset state of the chip
* @param chip Chip instance
*/
void ym2612_reset_chip(void *chip);
/**
* @brief Generate stereo output of specified length
* @param chip Chip instance
* @param buffer Output sound buffer
* @param frames Output buffer size in frames (one frame - two array entries of the buffer)
* @param mix 0 - override buffer data, 1 - mix output data with a content of the buffer
*/
void ym2612_generate(void *chip, FMSAMPLE *buffer, int frames, int mix);
#define ym2612_update_one(chip, buffer, length) ym2612_generate(chip, buffer, length, 0)
/**
* @brief Single-Sample generation prepare
* @param chip Chip instance
*/
void ym2612_pre_generate(void *chip);
/**
* @brief Generate single stereo PCM frame. Will be used native sample rate of 53267 Hz
* @param chip Chip instance
* @param buffer One stereo PCM frame
*/
void ym2612_generate_one_native(void *chip, FMSAMPLE buffer[2]);
/* void ym2612_post_generate(void *chip, int length); */
int ym2612_write(void *chip, int a,unsigned char v);
unsigned char ym2612_read(void *chip,int a);
int ym2612_timer_over(void *chip, int c );
void ym2612_postload(void *chip);
void ym2612_set_mutemask(void *chip, UINT32 MuteMask);
void ym2612_setoptions(UINT8 Flags);
#endif /* (BUILD_YM2612||BUILD_YM3438) */
#ifdef __cplusplus
}
#endif
#endif /* FM_HHHHH */

View file

@ -0,0 +1,65 @@
#ifndef MAMEDEF_H_
#define MAMEDEF_H_
/* typedefs to use MAME's (U)INTxx types (copied from MAME\src\ods\odscomm.h) */
/* 8-bit values */
typedef unsigned char UINT8;
typedef signed char INT8;
/* 16-bit values */
typedef unsigned short UINT16;
typedef signed short INT16;
/* 32-bit values */
#ifndef _WINDOWS_H
typedef unsigned int UINT32;
typedef signed int INT32;
#endif
/* 64-bit values */
#ifndef _WINDOWS_H
#ifdef _MSC_VER
typedef signed __int64 INT64;
typedef unsigned __int64 UINT64;
#else
__extension__ typedef unsigned long long UINT64;
__extension__ typedef signed long long INT64;
#endif
#endif
/* offsets and addresses are 32-bit (for now...) */
typedef UINT32 offs_t;
/* stream_sample_t is used to represent a single sample in a sound stream */
typedef INT16 stream_sample_t;
#if defined(VGM_BIG_ENDIAN)
#define BYTE_XOR_BE(x) (x)
#elif defined(VGM_LITTLE_ENDIAN)
#define BYTE_XOR_BE(x) ((x) ^ 0x01)
#else
/* don't define BYTE_XOR_BE so that it throws an error when compiling */
#endif
#if defined(_MSC_VER)
//#define INLINE static __forceinline
#define INLINE static __inline
#elif defined(__GNUC__)
#define INLINE static __inline__
#else
#define INLINE static inline
#endif
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#ifdef _DEBUG
#define logerror printf
#else
#define logerror
#endif
typedef void (*SRATE_CALLBACK)(void*, UINT32);
#endif /* __MAMEDEF_H__ */

View file

@ -0,0 +1,54 @@
#include "mame_opn2.h"
#include "mame/mame_ym2612fm.h"
#include <cstdlib>
#include <assert.h>
MameOPN2::MameOPN2()
{
chip = NULL;
setRate(m_rate, m_clock);
}
MameOPN2::~MameOPN2()
{
ym2612_shutdown(chip);
}
void MameOPN2::setRate(uint32_t rate, uint32_t clock)
{
OPNChipBaseT::setRate(rate, clock);
if(chip)
ym2612_shutdown(chip);
uint32_t chipRate = isRunningAtPcmRate() ? rate : static_cast<uint32_t>(nativeRate);
chip = ym2612_init(NULL, (int)clock, (int)chipRate, NULL, NULL);
ym2612_reset_chip(chip);
}
void MameOPN2::reset()
{
OPNChipBaseT::reset();
ym2612_reset_chip(chip);
}
void MameOPN2::writeReg(uint32_t port, uint16_t addr, uint8_t data)
{
ym2612_write(chip, 0 + (int)(port) * 2, (uint8_t)addr);
ym2612_write(chip, 1 + (int)(port) * 2, data);
}
void MameOPN2::nativePreGenerate()
{
void *chip = this->chip;
ym2612_pre_generate(chip);
}
void MameOPN2::nativeGenerate(int16_t *frame)
{
void *chip = this->chip;
ym2612_generate_one_native(chip, frame);
}
const char *MameOPN2::emulatorName()
{
return "MAME YM2612";
}

View file

@ -0,0 +1,23 @@
#ifndef MAME_OPN2_H
#define MAME_OPN2_H
#include "opn_chip_base.h"
class MameOPN2 final : public OPNChipBaseT<MameOPN2>
{
void *chip;
public:
MameOPN2();
~MameOPN2() override;
bool canRunAtPcmRate() const override { return true; }
void setRate(uint32_t rate, uint32_t clock) override;
void reset() override;
void writeReg(uint32_t port, uint16_t addr, uint8_t data) override;
void nativePreGenerate() override;
void nativePostGenerate() override {}
void nativeGenerate(int16_t *frame) override;
const char *emulatorName() override;
};
#endif // MAME_OPN2_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,246 @@
/*
* Copyright (C) 2017 Alexey Khokholov (Nuke.YKT)
*
* 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 2
* of the License, or (at your option) 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
* Nuked OPN2(Yamaha YM3438) emulator.
* Thanks:
* Silicon Pr0n:
* Yamaha YM3438 decap and die shot(digshadow).
* OPLx decapsulated(Matthew Gambrell, Olli Niemitalo):
* OPL2 ROMs.
*
* version: 1.0.7
*/
#ifndef YM3438_H
#define YM3438_H
#ifdef __cplusplus
extern "C" {
#endif
/*EXTRA*/
#define RSM_FRAC 10
#define OPN_WRITEBUF_SIZE 2048
#define OPN_WRITEBUF_DELAY 15
enum {
ym3438_type_discrete = 0, /* Discrete YM3438 (Teradrive) */
ym3438_type_asic = 1, /* ASIC YM3438 (MD1 VA7, MD2, MD3, etc) */
ym3438_type_ym2612 = 2 /* YM2612 (MD1, MD2 VA2) */
};
#include <stdint.h>
typedef uintptr_t Bitu;
typedef intptr_t Bits;
typedef uint64_t Bit64u;
typedef int64_t Bit64s;
typedef uint32_t Bit32u;
typedef int32_t Bit32s;
typedef uint16_t Bit16u;
typedef int16_t Bit16s;
typedef uint8_t Bit8u;
typedef int8_t Bit8s;
/*EXTRA*/
typedef struct _opn2_writebuf {
Bit64u time;
Bit8u port;
Bit8u data;
Bit8u reserved[6];
} opn2_writebuf;
typedef struct
{
Bit32u cycles;
Bit32u channel;
Bit16s mol, mor;
/* IO */
Bit16u write_data;
Bit8u write_a;
Bit8u write_d;
Bit8u write_a_en;
Bit8u write_d_en;
Bit8u write_busy;
Bit8u write_busy_cnt;
Bit8u write_fm_address;
Bit8u write_fm_data;
Bit8u write_fm_mode_a;
Bit16u address;
Bit8u data;
Bit8u pin_test_in;
Bit8u pin_irq;
Bit8u busy;
/* LFO */
Bit8u lfo_en;
Bit8u lfo_freq;
Bit8u lfo_pm;
Bit8u lfo_am;
Bit8u lfo_cnt;
Bit8u lfo_inc;
Bit8u lfo_quotient;
/* Phase generator */
Bit16u pg_fnum;
Bit8u pg_block;
Bit8u pg_kcode;
Bit32u pg_inc[24];
Bit32u pg_phase[24];
Bit8u pg_reset[24];
Bit32u pg_read;
/* Envelope generator */
Bit8u eg_cycle;
Bit8u eg_cycle_stop;
Bit8u eg_shift;
Bit8u eg_shift_lock;
Bit8u eg_timer_low_lock;
Bit16u eg_timer;
Bit8u eg_timer_inc;
Bit16u eg_quotient;
Bit8u eg_custom_timer;
Bit8u eg_rate;
Bit8u eg_ksv;
Bit8u eg_inc;
Bit8u eg_ratemax;
Bit8u eg_sl[2];
Bit8u eg_lfo_am;
Bit8u eg_tl[2];
Bit8u eg_state[24];
Bit16u eg_level[24];
Bit16u eg_out[24];
Bit8u eg_kon[24];
Bit8u eg_kon_csm[24];
Bit8u eg_kon_latch[24];
Bit8u eg_csm_mode[24];
Bit8u eg_ssg_enable[24];
Bit8u eg_ssg_pgrst_latch[24];
Bit8u eg_ssg_repeat_latch[24];
Bit8u eg_ssg_hold_up_latch[24];
Bit8u eg_ssg_dir[24];
Bit8u eg_ssg_inv[24];
Bit32u eg_read[2];
Bit8u eg_read_inc;
/* FM */
Bit16s fm_op1[6][2];
Bit16s fm_op2[6];
Bit16s fm_out[24];
Bit16u fm_mod[24];
/* Channel */
Bit16s ch_acc[6];
Bit16s ch_out[6];
Bit16s ch_lock;
Bit8u ch_lock_l;
Bit8u ch_lock_r;
Bit16s ch_read;
/* Timer */
Bit16u timer_a_cnt;
Bit16u timer_a_reg;
Bit8u timer_a_load_lock;
Bit8u timer_a_load;
Bit8u timer_a_enable;
Bit8u timer_a_reset;
Bit8u timer_a_load_latch;
Bit8u timer_a_overflow_flag;
Bit8u timer_a_overflow;
Bit16u timer_b_cnt;
Bit8u timer_b_subcnt;
Bit16u timer_b_reg;
Bit8u timer_b_load_lock;
Bit8u timer_b_load;
Bit8u timer_b_enable;
Bit8u timer_b_reset;
Bit8u timer_b_load_latch;
Bit8u timer_b_overflow_flag;
Bit8u timer_b_overflow;
/* Register set */
Bit8u mode_test_21[8];
Bit8u mode_test_2c[8];
Bit8u mode_ch3;
Bit8u mode_kon_channel;
Bit8u mode_kon_operator[4];
Bit8u mode_kon[24];
Bit8u mode_csm;
Bit8u mode_kon_csm;
Bit8u dacen;
Bit16s dacdata;
Bit8u ks[24];
Bit8u ar[24];
Bit8u sr[24];
Bit8u dt[24];
Bit8u multi[24];
Bit8u sl[24];
Bit8u rr[24];
Bit8u dr[24];
Bit8u am[24];
Bit8u tl[24];
Bit8u ssg_eg[24];
Bit16u fnum[6];
Bit8u block[6];
Bit8u kcode[6];
Bit16u fnum_3ch[6];
Bit8u block_3ch[6];
Bit8u kcode_3ch[6];
Bit8u reg_a4;
Bit8u reg_ac;
Bit8u connect[6];
Bit8u fb[6];
Bit8u pan_l[6], pan_r[6];
Bit8u ams[6];
Bit8u pms[6];
/*EXTRA*/
Bit32u mute[7];
Bit32s rateratio;
Bit32s samplecnt;
Bit32s oldsamples[2];
Bit32s samples[2];
Bit64u writebuf_samplecnt;
Bit32u writebuf_cur;
Bit32u writebuf_last;
Bit64u writebuf_lasttime;
opn2_writebuf writebuf[OPN_WRITEBUF_SIZE];
} ym3438_t;
/* EXTRA, original was "void OPN2_Reset(ym3438_t *chip)" */
void OPN2_Reset(ym3438_t *chip, Bit32u rate, Bit32u clock);
void OPN2_SetChipType(Bit32u type);
void OPN2_Clock(ym3438_t *chip, Bit16s *buffer);
void OPN2_Write(ym3438_t *chip, Bit32u port, Bit8u data);
void OPN2_SetTestPin(ym3438_t *chip, Bit32u value);
Bit32u OPN2_ReadTestPin(ym3438_t *chip);
Bit32u OPN2_ReadIRQPin(ym3438_t *chip);
Bit8u OPN2_Read(ym3438_t *chip, Bit32u port);
/*EXTRA*/
void OPN2_WriteBuffered(ym3438_t *chip, Bit32u port, Bit8u data);
void OPN2_Generate(ym3438_t *chip, Bit16s *buf);
void OPN2_GenerateResampled(ym3438_t *chip, Bit16s *buf);
void OPN2_GenerateStream(ym3438_t *chip, Bit16s *output, Bit32u numsamples);
void OPN2_GenerateStreamMix(ym3438_t *chip, Bit16s *output, Bit32u numsamples);
void OPN2_SetOptions(Bit8u flags);
void OPN2_SetMute(ym3438_t *chip, Bit32u mute);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,49 @@
#include "nuked_opn2.h"
#include "nuked/ym3438.h"
#include <cstring>
NukedOPN2::NukedOPN2()
{
OPN2_SetChipType(ym3438_type_asic);
chip = new ym3438_t;
setRate(m_rate, m_clock);
}
NukedOPN2::~NukedOPN2()
{
ym3438_t *chip_r = reinterpret_cast<ym3438_t*>(chip);
delete chip_r;
}
void NukedOPN2::setRate(uint32_t rate, uint32_t clock)
{
OPNChipBaseT::setRate(rate, clock);
ym3438_t *chip_r = reinterpret_cast<ym3438_t*>(chip);
OPN2_Reset(chip_r, rate, clock);
}
void NukedOPN2::reset()
{
OPNChipBaseT::reset();
ym3438_t *chip_r = reinterpret_cast<ym3438_t*>(chip);
OPN2_Reset(chip_r, m_rate, m_clock);
}
void NukedOPN2::writeReg(uint32_t port, uint16_t addr, uint8_t data)
{
ym3438_t *chip_r = reinterpret_cast<ym3438_t*>(chip);
OPN2_WriteBuffered(chip_r, 0 + (port) * 2, (uint8_t)addr);
OPN2_WriteBuffered(chip_r, 1 + (port) * 2, data);
//qDebug() << QString("%1: 0x%2 => 0x%3").arg(port).arg(addr, 2, 16, QChar('0')).arg(data, 2, 16, QChar('0'));
}
void NukedOPN2::nativeGenerate(int16_t *frame)
{
ym3438_t *chip_r = reinterpret_cast<ym3438_t*>(chip);
OPN2_Generate(chip_r, frame);
}
const char *NukedOPN2::emulatorName()
{
return "Nuked OPN2";
}

View file

@ -0,0 +1,25 @@
#ifndef NUKED_OPN2_H
#define NUKED_OPN2_H
#include "opn_chip_base.h"
class NukedOPN2 final : public OPNChipBaseT<NukedOPN2>
{
void *chip;
public:
NukedOPN2();
~NukedOPN2() override;
bool canRunAtPcmRate() const override { return false; }
void setRate(uint32_t rate, uint32_t clock) override;
void reset() override;
void writeReg(uint32_t port, uint16_t addr, uint8_t data) override;
void nativePreGenerate() override {}
void nativePostGenerate() override {}
void nativeGenerate(int16_t *frame) override;
const char *emulatorName() override;
// amplitude scale factors to use in resampling
enum { resamplerPreAmplify = 11, resamplerPostAttenuate = 2 };
};
#endif // NUKED_OPN2_H

View file

@ -0,0 +1,110 @@
#ifndef ONP_CHIP_BASE_H
#define ONP_CHIP_BASE_H
#include <stdint.h>
#include <stddef.h>
#if !defined(_MSC_VER) && (__cplusplus <= 199711L)
#define final
#define override
#endif
#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER)
class VResampler;
#endif
class OPNChipBase
{
public:
enum { nativeRate = 53267 };
protected:
uint32_t m_rate;
uint32_t m_clock;
public:
OPNChipBase();
virtual ~OPNChipBase();
virtual bool canRunAtPcmRate() const = 0;
virtual bool isRunningAtPcmRate() const = 0;
virtual bool setRunningAtPcmRate(bool r) = 0;
virtual void setRate(uint32_t rate, uint32_t clock) = 0;
virtual void reset() = 0;
virtual void writeReg(uint32_t port, uint16_t addr, uint8_t data) = 0;
virtual void nativePreGenerate() = 0;
virtual void nativePostGenerate() = 0;
virtual void nativeGenerate(int16_t *frame) = 0;
virtual void generate(int16_t *output, size_t frames) = 0;
virtual void generateAndMix(int16_t *output, size_t frames) = 0;
virtual void generate32(int32_t *output, size_t frames) = 0;
virtual void generateAndMix32(int32_t *output, size_t frames) = 0;
virtual const char* emulatorName() = 0;
private:
OPNChipBase(const OPNChipBase &c);
OPNChipBase &operator=(const OPNChipBase &c);
};
// A base class providing F-bounded generic and efficient implementations,
// supporting resampling of chip outputs
template <class T>
class OPNChipBaseT : public OPNChipBase
{
public:
OPNChipBaseT();
virtual ~OPNChipBaseT();
bool isRunningAtPcmRate() const override;
bool setRunningAtPcmRate(bool r) override;
virtual void setRate(uint32_t rate, uint32_t clock) override;
virtual void reset() override;
void generate(int16_t *output, size_t frames) override;
void generateAndMix(int16_t *output, size_t frames) override;
void generate32(int32_t *output, size_t frames) override;
void generateAndMix32(int32_t *output, size_t frames) override;
private:
bool m_runningAtPcmRate;
void setupResampler(uint32_t rate);
void resetResampler();
void resampledGenerate(int32_t *output);
#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER)
VResampler *m_resampler;
#else
int32_t m_oldsamples[2];
int32_t m_samples[2];
int32_t m_samplecnt;
int32_t m_rateratio;
enum { rsm_frac = 10 };
#endif
// amplitude scale factors in and out of resampler, varying for chips;
// values are OK to "redefine", the static polymorphism will accept it.
enum { resamplerPreAmplify = 1, resamplerPostAttenuate = 1 };
};
// A base class which provides frame-by-frame interfaces on emulations which
// don't have a routine for it. It produces outputs in fixed size buffers.
// Fast register updates will suffer some latency because of buffering.
template <class T, unsigned Buffer = 256>
class OPNChipBaseBufferedT : public OPNChipBaseT<T>
{
public:
OPNChipBaseBufferedT()
: OPNChipBaseT<T>(), m_bufferIndex(0) {}
virtual ~OPNChipBaseBufferedT()
{}
public:
void reset() override;
void nativeGenerate(int16_t *frame) override;
protected:
virtual void nativeGenerateN(int16_t *output, size_t frames) = 0;
private:
unsigned m_bufferIndex;
int16_t m_buffer[2 * Buffer];
};
#include "opn_chip_base.tcc"
#endif // ONP_CHIP_BASE_H

View file

@ -0,0 +1,268 @@
#include "opn_chip_base.h"
#include <cmath>
#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER)
#include <zita-resampler/vresampler.h>
#endif
#if !defined(LIKELY) && defined(__GNUC__)
#define LIKELY(x) __builtin_expect((x), 1)
#elif !defined(LIKELY)
#define LIKELY(x) (x)
#endif
#if !defined(UNLIKELY) && defined(__GNUC__)
#define UNLIKELY(x) __builtin_expect((x), 0)
#elif !defined(UNLIKELY)
#define UNLIKELY(x) (x)
#endif
/* OPNChipBase */
inline OPNChipBase::OPNChipBase() :
m_rate(44100),
m_clock(7670454)
{
}
inline OPNChipBase::~OPNChipBase()
{
}
/* OPNChipBaseT */
template <class T>
OPNChipBaseT<T>::OPNChipBaseT()
: OPNChipBase(),
m_runningAtPcmRate(false)
{
#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER)
m_resampler = new VResampler;
#endif
setupResampler(m_rate);
}
template <class T>
OPNChipBaseT<T>::~OPNChipBaseT()
{
#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER)
delete m_resampler;
#endif
}
template <class T>
bool OPNChipBaseT<T>::isRunningAtPcmRate() const
{
return m_runningAtPcmRate;
}
template <class T>
bool OPNChipBaseT<T>::setRunningAtPcmRate(bool r)
{
if(r != m_runningAtPcmRate)
{
if(r && !static_cast<T *>(this)->canRunAtPcmRate())
return false;
m_runningAtPcmRate = r;
static_cast<T *>(this)->setRate(m_rate, m_clock);
}
return true;
}
template <class T>
void OPNChipBaseT<T>::setRate(uint32_t rate, uint32_t clock)
{
uint32_t oldRate = m_rate;
m_rate = rate;
m_clock = clock;
if(rate != oldRate)
setupResampler(rate);
else
resetResampler();
}
template <class T>
void OPNChipBaseT<T>::reset()
{
resetResampler();
}
template <class T>
void OPNChipBaseT<T>::generate(int16_t *output, size_t frames)
{
static_cast<T *>(this)->nativePreGenerate();
for(size_t i = 0; i < frames; ++i)
{
int32_t frame[2];
static_cast<T *>(this)->resampledGenerate(frame);
for (unsigned c = 0; c < 2; ++c) {
int32_t temp = frame[c];
temp = (temp > -32768) ? temp : -32768;
temp = (temp < 32767) ? temp : 32767;
output[c] = (int16_t)temp;
}
output += 2;
}
static_cast<T *>(this)->nativePostGenerate();
}
template <class T>
void OPNChipBaseT<T>::generateAndMix(int16_t *output, size_t frames)
{
static_cast<T *>(this)->nativePreGenerate();
for(size_t i = 0; i < frames; ++i)
{
int32_t frame[2];
static_cast<T *>(this)->resampledGenerate(frame);
for (unsigned c = 0; c < 2; ++c) {
int32_t temp = (int32_t)output[c] + frame[c];
temp = (temp > -32768) ? temp : -32768;
temp = (temp < 32767) ? temp : 32767;
output[c] = (int16_t)temp;
}
output += 2;
}
static_cast<T *>(this)->nativePostGenerate();
}
template <class T>
void OPNChipBaseT<T>::generate32(int32_t *output, size_t frames)
{
static_cast<T *>(this)->nativePreGenerate();
for(size_t i = 0; i < frames; ++i)
{
static_cast<T *>(this)->resampledGenerate(output);
output += 2;
}
static_cast<T *>(this)->nativePostGenerate();
}
template <class T>
void OPNChipBaseT<T>::generateAndMix32(int32_t *output, size_t frames)
{
static_cast<T *>(this)->nativePreGenerate();
for(size_t i = 0; i < frames; ++i)
{
int32_t frame[2];
static_cast<T *>(this)->resampledGenerate(frame);
output[0] += frame[0];
output[1] += frame[1];
output += 2;
}
static_cast<T *>(this)->nativePostGenerate();
}
template <class T>
void OPNChipBaseT<T>::setupResampler(uint32_t rate)
{
#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER)
m_resampler->setup(rate * (1.0 / 53267), 2, 48);
#else
m_oldsamples[0] = m_oldsamples[1] = 0;
m_samples[0] = m_samples[1] = 0;
m_samplecnt = 0;
m_rateratio = (int32_t)(uint32_t)((((uint64_t)144 * rate) << rsm_frac) / m_clock);
#endif
}
template <class T>
void OPNChipBaseT<T>::resetResampler()
{
#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER)
m_resampler->reset();
#else
m_oldsamples[0] = m_oldsamples[1] = 0;
m_samples[0] = m_samples[1] = 0;
m_samplecnt = 0;
#endif
}
#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER)
template <class T>
void OPNChipBaseT<T>::resampledGenerate(int32_t *output)
{
if(UNLIKELY(m_runningAtPcmRate))
{
int16_t in[2];
static_cast<T *>(this)->nativeGenerate(in);
output[0] = (int32_t)in[0] * T::resamplerPreAmplify / T::resamplerPostAttenuate;
output[1] = (int32_t)in[1] * T::resamplerPreAmplify / T::resamplerPostAttenuate;
return;
}
VResampler *rsm = m_resampler;
float scale = (float)T::resamplerPreAmplify /
(float)T::resamplerPostAttenuate;
float f_in[2];
float f_out[2];
rsm->inp_count = 0;
rsm->inp_data = f_in;
rsm->out_count = 1;
rsm->out_data = f_out;
while(rsm->process(), rsm->out_count != 0)
{
int16_t in[2];
static_cast<T *>(this)->nativeGenerate(in);
f_in[0] = scale * (float)in[0];
f_in[1] = scale * (float)in[1];
rsm->inp_count = 1;
rsm->inp_data = f_in;
rsm->out_count = 1;
rsm->out_data = f_out;
}
output[0] = std::lround(f_out[0]);
output[1] = std::lround(f_out[1]);
}
#else
template <class T>
void OPNChipBaseT<T>::resampledGenerate(int32_t *output)
{
if(UNLIKELY(m_runningAtPcmRate))
{
int16_t in[2];
static_cast<T *>(this)->nativeGenerate(in);
output[0] = (int32_t)in[0] * T::resamplerPreAmplify / T::resamplerPostAttenuate;
output[1] = (int32_t)in[1] * T::resamplerPreAmplify / T::resamplerPostAttenuate;
return;
}
int32_t samplecnt = m_samplecnt;
const int32_t rateratio = m_rateratio;
while(samplecnt >= rateratio)
{
m_oldsamples[0] = m_samples[0];
m_oldsamples[1] = m_samples[1];
int16_t buffer[2];
static_cast<T *>(this)->nativeGenerate(buffer);
m_samples[0] = buffer[0] * T::resamplerPreAmplify;
m_samples[1] = buffer[1] * T::resamplerPreAmplify;
samplecnt -= rateratio;
}
output[0] = (int32_t)(((m_oldsamples[0] * (rateratio - samplecnt)
+ m_samples[0] * samplecnt) / rateratio)/T::resamplerPostAttenuate);
output[1] = (int32_t)(((m_oldsamples[1] * (rateratio - samplecnt)
+ m_samples[1] * samplecnt) / rateratio)/T::resamplerPostAttenuate);
m_samplecnt = samplecnt + (1 << rsm_frac);
}
#endif
/* OPNChipBaseBufferedT */
template <class T, unsigned Buffer>
void OPNChipBaseBufferedT<T, Buffer>::reset()
{
OPNChipBaseT<T>::reset();
m_bufferIndex = 0;
}
template <class T, unsigned Buffer>
void OPNChipBaseBufferedT<T, Buffer>::nativeGenerate(int16_t *frame)
{
unsigned bufferIndex = m_bufferIndex;
if(bufferIndex == 0)
static_cast<T *>(this)->nativeGenerateN(m_buffer, Buffer);
frame[0] = m_buffer[2 * bufferIndex];
frame[1] = m_buffer[2 * bufferIndex + 1];
bufferIndex = (bufferIndex + 1 < Buffer) ? (bufferIndex + 1) : 0;
m_bufferIndex = bufferIndex;
}

View file

@ -82,6 +82,10 @@ public:
self &operator= (long double orig);
};
#ifdef _MSC_VER
#pragma warning(disable:4146)
#endif
template<typename inttype>
void fraction<inttype>::Optim()
{
@ -110,6 +114,10 @@ void fraction<inttype>::Optim()
//fprintf(stderr, "result: %d/%d\n\n", nom(), denom());
}
#ifdef _MSC_VER
#pragma warning(default:4146)
#endif
template<typename inttype>
inline const fraction<inttype> abs(const fraction<inttype> &f)
{

View file

@ -21,6 +21,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPNMIDI_OPNBANK_H
#define OPNMIDI_OPNBANK_H
#include <string.h>
#include <stdint.h>
#ifdef ADLMIDI_buildAsApp
@ -36,13 +40,25 @@ public:
};
#endif
enum { opnNoteOnMaxTime = 40000 };
/* *********** FM Operator indexes *********** */
#define OPERATOR1 0
#define OPERATOR2 1
#define OPERATOR3 2
#define OPERATOR4 3
enum
{
OPERATOR1 = 0,
OPERATOR2 = 1,
OPERATOR3 = 2,
OPERATOR4 = 3,
};
/* *********** FM Operator indexes *end******* */
#pragma pack(push, 1)
#define OPNDATA_BYTE_COMPARABLE(T) \
inline bool operator==(const T &a, const T &b) \
{ return !memcmp(&a, &b, sizeof(T)); } \
inline bool operator!=(const T &a, const T &b) \
{ return !operator==(a, b); }
struct OPN_Operator
{
//! Raw register data
@ -58,6 +74,7 @@ struct OPN_Operator
6 - SSG-EG byte
*/
};
OPNDATA_BYTE_COMPARABLE(struct OPN_Operator)
struct opnInstData
{
@ -68,6 +85,7 @@ struct opnInstData
//! Note offset
int16_t finetune;
};
OPNDATA_BYTE_COMPARABLE(struct opnInstData)
struct opnInstMeta
{
@ -79,3 +97,41 @@ struct opnInstMeta
uint16_t ms_sound_koff;
double fine_tune;
};
OPNDATA_BYTE_COMPARABLE(struct opnInstMeta)
/**
* @brief Instrument data with operators included
*/
struct opnInstMeta2
{
opnInstData opn[2];
uint8_t tone;
uint8_t flags;
uint16_t ms_sound_kon; // Number of milliseconds it produces sound;
uint16_t ms_sound_koff;
double fine_tune;
#if 0
opnInstMeta2() {}
explicit opnInstMeta2(const opnInstMeta &d);
#endif
};
OPNDATA_BYTE_COMPARABLE(struct opnInstMeta2)
#undef OPNDATA_BYTE_COMPARABLE
#pragma pack(pop)
#if 0
/**
* @brief Conversion of storage formats
*/
inline opnInstMeta2::opnInstMeta2(const opnInstMeta &d)
: tone(d.tone), flags(d.flags),
ms_sound_kon(d.ms_sound_kon), ms_sound_koff(d.ms_sound_koff),
fine_tune(d.fine_tune)
{
opn[0] = ::opn[d.opnno1];
opn[1] = ::opn[d.opnno2];
}
#endif
#endif // OPNMIDI_OPNBANK_H

View file

@ -32,6 +32,13 @@ static OPN2_Version opn2_version = {
OPNMIDI_VERSION_PATCHLEVEL
};
static const OPNMIDI_AudioFormat opn2_DefaultAudioFormat =
{
OPNMIDI_SampleType_S16,
sizeof(int16_t),
2 * sizeof(int16_t),
};
/*---------------------------EXPORTS---------------------------*/
OPNMIDI_EXPORT struct OPN2_MIDIPlayer *opn2_init(long sample_rate)
@ -129,7 +136,14 @@ OPNMIDI_EXPORT void opn2_setScaleModulators(OPN2_MIDIPlayer *device, int smod)
if(!device) return;
OPNMIDIplay *play = reinterpret_cast<OPNMIDIplay *>(device->opn2_midiPlayer);
play->m_setup.ScaleModulators = smod;
play->opn.ScaleModulators = play->m_setup.ScaleModulators;
play->opn.ScaleModulators = (play->m_setup.ScaleModulators != 0);
}
OPNMIDI_EXPORT void opn2_setFullRangeBrightness(struct OPN2_MIDIPlayer *device, int fr_brightness)
{
if(!device) return;
OPNMIDIplay *play = reinterpret_cast<OPNMIDIplay *>(device->opn2_midiPlayer);
play->m_setup.fullRangeBrightnessCC74 = (fr_brightness != 0);
}
OPNMIDI_EXPORT void opn2_setLoopEnabled(OPN2_MIDIPlayer *device, int loopEn)
@ -139,12 +153,16 @@ OPNMIDI_EXPORT void opn2_setLoopEnabled(OPN2_MIDIPlayer *device, int loopEn)
play->m_setup.loopingIsEnabled = (loopEn != 0);
}
/* !!!DEPRECATED!!! */
OPNMIDI_EXPORT void opn2_setLogarithmicVolumes(struct OPN2_MIDIPlayer *device, int logvol)
{
if(!device) return;
OPNMIDIplay *play = reinterpret_cast<OPNMIDIplay *>(device->opn2_midiPlayer);
play->m_setup.LogarithmicVolumes = static_cast<unsigned int>(logvol);
play->opn.LogarithmicVolumes = play->m_setup.LogarithmicVolumes;
if(play->m_setup.LogarithmicVolumes != 0)
play->opn.ChangeVolumeRangesModel(OPNMIDI_VolumeModel_CMF);
else
play->opn.ChangeVolumeRangesModel(static_cast<OPNMIDI_VolumeModels>(play->m_setup.VolumeModel));
}
OPNMIDI_EXPORT void opn2_setVolumeRangeModel(OPN2_MIDIPlayer *device, int volumeModel)
@ -209,16 +227,60 @@ OPNMIDI_EXPORT int opn2_openData(OPN2_MIDIPlayer *device, const void *mem, unsig
OPNMIDI_EXPORT const char *opn2_emulatorName()
{
#ifdef OPNMIDI_USE_LEGACY_EMULATOR
return "GENS 2.10 YM2612";
#else
return "Nuked OPN2 YM3438";
#endif
return "<opn2_emulatorName() is deprecated! Use opn2_chipEmulatorName() instead!>";
}
OPNMIDI_EXPORT const char *opn2_chipEmulatorName(struct OPN2_MIDIPlayer *device)
{
if(device)
{
OPNMIDIplay *play = reinterpret_cast<OPNMIDIplay *>(device->opn2_midiPlayer);
if(play && !play->opn.cardsOP2.empty())
return play->opn.cardsOP2[0]->emulatorName();
}
return "Unknown";
}
OPNMIDI_EXPORT int opn2_switchEmulator(struct OPN2_MIDIPlayer *device, int emulator)
{
if(device)
{
OPNMIDIplay *play = reinterpret_cast<OPNMIDIplay *>(device->opn2_midiPlayer);
if(play && (emulator >= 0) && (emulator < OPNMIDI_EMU_end))
{
play->m_setup.emulator = emulator;
opn2_reset(device);
return 0;
}
play->setErrorString("OPN2 MIDI: Unknown emulation core!");
}
return -1;
}
OPNMIDI_EXPORT int opn2_setRunAtPcmRate(OPN2_MIDIPlayer *device, int enabled)
{
if(device)
{
OPNMIDIplay *play = reinterpret_cast<OPNMIDIplay *>(device->opn2_midiPlayer);
if(play)
{
play->m_setup.runAtPcmRate = (enabled != 0);
opn2_reset(device);
return 0;
}
}
return -1;
}
OPNMIDI_EXPORT const char *opn2_linkedLibraryVersion()
{
#if !defined(OPNMIDI_ENABLE_HQ_RESAMPLER)
return OPNMIDI_VERSION;
#else
return OPNMIDI_VERSION " (HQ)";
#endif
}
OPNMIDI_EXPORT const OPN2_Version *opn2_linkedVersion()
@ -270,7 +332,8 @@ OPNMIDI_EXPORT void opn2_reset(OPN2_MIDIPlayer *device)
return;
OPNMIDIplay *play = reinterpret_cast<OPNMIDIplay *>(device->opn2_midiPlayer);
play->m_setup.tick_skip_samples_delay = 0;
play->opn.Reset(play->m_setup.PCM_RATE);
play->opn.runAtPcmRate = play->m_setup.runAtPcmRate;
play->opn.Reset(play->m_setup.emulator, play->m_setup.PCM_RATE);
play->ch.clear();
play->ch.resize(play->opn.NumChannels);
}
@ -491,24 +554,139 @@ OPNMIDI_EXPORT void opn2_setDebugMessageHook(struct OPN2_MIDIPlayer *device, OPN
}
template <class Dst>
static void CopySamplesRaw(OPN2_UInt8 *dstLeft, OPN2_UInt8 *dstRight, const int32_t *src,
size_t frameCount, unsigned sampleOffset)
{
for(size_t i = 0; i < frameCount; ++i) {
*(Dst *)(dstLeft + (i * sampleOffset)) = src[2 * i];
*(Dst *)(dstRight + (i * sampleOffset)) = src[(2 * i) + 1];
}
}
inline static void SendStereoAudio(int &samples_requested,
ssize_t &in_size,
short *_in,
ssize_t out_pos,
short *_out)
template <class Dst, class Ret>
static void CopySamplesTransformed(OPN2_UInt8 *dstLeft, OPN2_UInt8 *dstRight, const int32_t *src,
size_t frameCount, unsigned sampleOffset,
Ret(&transform)(int32_t))
{
for(size_t i = 0; i < frameCount; ++i) {
*(Dst *)(dstLeft + (i * sampleOffset)) = transform(src[2 * i]);
*(Dst *)(dstRight + (i * sampleOffset)) = transform(src[(2 * i) + 1]);
}
}
static int SendStereoAudio(int samples_requested,
ssize_t in_size,
int32_t *_in,
ssize_t out_pos,
OPN2_UInt8 *left,
OPN2_UInt8 *right,
const OPNMIDI_AudioFormat *format)
{
if(!in_size)
return;
size_t offset = static_cast<size_t>(out_pos);
return 0;
size_t outputOffset = static_cast<size_t>(out_pos);
size_t inSamples = static_cast<size_t>(in_size * 2);
size_t maxSamples = static_cast<size_t>(samples_requested) - offset;
size_t maxSamples = static_cast<size_t>(samples_requested) - outputOffset;
size_t toCopy = std::min(maxSamples, inSamples);
std::memcpy(_out + out_pos, _in, toCopy * sizeof(short));
OPNMIDI_SampleType sampleType = format->type;
const unsigned containerSize = format->containerSize;
const unsigned sampleOffset = format->sampleOffset;
left += (outputOffset / 2) * sampleOffset;
right += (outputOffset / 2) * sampleOffset;
typedef int32_t(&pfnConvert)(int32_t);
switch(sampleType) {
case OPNMIDI_SampleType_S8:
case OPNMIDI_SampleType_U8:
{
pfnConvert cvt = (sampleType == OPNMIDI_SampleType_S8) ? opn2_cvtS8 : opn2_cvtU8;
switch(containerSize) {
case sizeof(int8_t):
CopySamplesTransformed<int8_t>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
case sizeof(int16_t):
CopySamplesTransformed<int16_t>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
case sizeof(int32_t):
CopySamplesTransformed<int32_t>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
default:
return -1;
}
break;
}
case OPNMIDI_SampleType_S16:
case OPNMIDI_SampleType_U16:
{
pfnConvert cvt = (sampleType == OPNMIDI_SampleType_S16) ? opn2_cvtS16 : opn2_cvtU16;
switch(containerSize) {
case sizeof(int16_t):
CopySamplesTransformed<int16_t>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
case sizeof(int32_t):
CopySamplesRaw<int32_t>(left, right, _in, toCopy / 2, sampleOffset);
break;
default:
return -1;
}
break;
}
case OPNMIDI_SampleType_S24:
case OPNMIDI_SampleType_U24:
{
pfnConvert cvt = (sampleType == OPNMIDI_SampleType_S24) ? opn2_cvtS24 : opn2_cvtU24;
switch(containerSize) {
case sizeof(int32_t):
CopySamplesTransformed<int32_t>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
default:
return -1;
}
break;
}
case OPNMIDI_SampleType_S32:
case OPNMIDI_SampleType_U32:
{
pfnConvert cvt = (sampleType == OPNMIDI_SampleType_S32) ? opn2_cvtS32 : opn2_cvtU32;
switch(containerSize) {
case sizeof(int32_t):
CopySamplesTransformed<int32_t>(left, right, _in, toCopy / 2, sampleOffset, cvt);
break;
default:
return -1;
}
break;
}
case OPNMIDI_SampleType_F32:
if(containerSize != sizeof(float))
return -1;
CopySamplesTransformed<float>(left, right, _in, toCopy / 2, sampleOffset, opn2_cvtReal<float>);
break;
case OPNMIDI_SampleType_F64:
if(containerSize != sizeof(double))
return -1;
CopySamplesTransformed<double>(left, right, _in, toCopy / 2, sampleOffset, opn2_cvtReal<double>);
break;
default:
return -1;
}
return 0;
}
OPNMIDI_EXPORT int opn2_play(OPN2_MIDIPlayer *device, int sampleCount, short *out)
OPNMIDI_EXPORT int opn2_play(struct OPN2_MIDIPlayer *device, int sampleCount, short *out)
{
return opn2_playFormat(device, sampleCount, (OPN2_UInt8 *)out, (OPN2_UInt8 *)(out + 1), &opn2_DefaultAudioFormat);
}
OPNMIDI_EXPORT int opn2_playFormat(OPN2_MIDIPlayer *device, int sampleCount,
OPN2_UInt8 *out_left, OPN2_UInt8 *out_right,
const OPNMIDI_AudioFormat *format)
{
#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER
sampleCount -= sampleCount % 2; //Avoid even sample requests
@ -562,31 +740,20 @@ OPNMIDI_EXPORT int opn2_play(OPN2_MIDIPlayer *device, int sampleCount, short *ou
ssize_t in_generatedPhys = in_generatedStereo * 2;
//! Unsigned total sample count
//fill buffer with zeros
int16_t *out_buf = player->outBuf;
std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(int16_t));
int32_t *out_buf = player->outBuf;
std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(out_buf[0]));
unsigned int chips = player->opn.NumCards;
if(chips == 1)
{
#ifdef OPNMIDI_USE_LEGACY_EMULATOR
player->opn.cardsOP2[0]->run(int(in_generatedStereo), out_buf);
#else
OPN2_GenerateStream(player->opn.cardsOP2[0], out_buf, (Bit32u)in_generatedStereo);
#endif
}
player->opn.cardsOP2[0]->generate32(out_buf, (size_t)in_generatedStereo);
else/* if(n_periodCountStereo > 0)*/
{
/* Generate data from every chip and mix result */
for(unsigned card = 0; card < chips; ++card)
{
#ifdef OPNMIDI_USE_LEGACY_EMULATOR
player->opn.cardsOP2[card]->run(int(in_generatedStereo), out_buf);
#else
OPN2_GenerateStreamMix(player->opn.cardsOP2[card], out_buf, (Bit32u)in_generatedStereo);
#endif
}
for(size_t card = 0; card < chips; ++card)
player->opn.cardsOP2[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo);
}
/* Process it */
SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out);
if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1)
return 0;
left -= (int)in_generatedPhys;
gotten_len += (in_generatedPhys) /* - setup.stored_samples*/;
@ -609,6 +776,13 @@ OPNMIDI_EXPORT int opn2_play(OPN2_MIDIPlayer *device, int sampleCount, short *ou
OPNMIDI_EXPORT int opn2_generate(struct OPN2_MIDIPlayer *device, int sampleCount, short *out)
{
return opn2_generateFormat(device, sampleCount, (OPN2_UInt8 *)out, (OPN2_UInt8 *)(out + 1), &opn2_DefaultAudioFormat);
}
OPNMIDI_EXPORT int opn2_generateFormat(struct OPN2_MIDIPlayer *device, int sampleCount,
OPN2_UInt8 *out_left, OPN2_UInt8 *out_right,
const OPNMIDI_AudioFormat *format)
{
sampleCount -= sampleCount % 2; //Avoid even sample requests
if(sampleCount < 0)
@ -644,31 +818,20 @@ OPNMIDI_EXPORT int opn2_generate(struct OPN2_MIDIPlayer *device, int sampleCount
ssize_t in_generatedPhys = in_generatedStereo * 2;
//! Unsigned total sample count
//fill buffer with zeros
int16_t *out_buf = player->outBuf;
std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(int16_t));
int32_t *out_buf = player->outBuf;
std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(out_buf[0]));
unsigned int chips = player->opn.NumCards;
if(chips == 1)
{
#ifdef OPNMIDI_USE_LEGACY_EMULATOR
player->opn.cardsOP2[0]->run(int(in_generatedStereo), out_buf);
#else
OPN2_GenerateStream(player->opn.cardsOP2[0], out_buf, (Bit32u)in_generatedStereo);
#endif
}
player->opn.cardsOP2[0]->generate32(out_buf, (size_t)in_generatedStereo);
else/* if(n_periodCountStereo > 0)*/
{
/* Generate data from every chip and mix result */
for(unsigned card = 0; card < chips; ++card)
{
#ifdef OPNMIDI_USE_LEGACY_EMULATOR
player->opn.cardsOP2[card]->run(int(in_generatedStereo), out_buf);
#else
OPN2_GenerateStreamMix(player->opn.cardsOP2[card], out_buf, (Bit32u)in_generatedStereo);
#endif
}
for(size_t card = 0; card < chips; ++card)
player->opn.cardsOP2[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo);
}
/* Process it */
SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out);
if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1)
return 0;
left -= (int)in_generatedPhys;
gotten_len += (in_generatedPhys) /* - setup.stored_samples*/;

View file

@ -29,10 +29,11 @@ extern "C" {
#endif
#define OPNMIDI_VERSION_MAJOR 1
#define OPNMIDI_VERSION_MINOR 1
#define OPNMIDI_VERSION_PATCHLEVEL 1
#define OPNMIDI_VERSION_MINOR 3
#define OPNMIDI_VERSION_PATCHLEVEL 0
#define OPNMIDI_TOSTR(s) #s
#define OPNMIDI_TOSTR_I(s) #s
#define OPNMIDI_TOSTR(s) OPNMIDI_TOSTR_I(s)
#define OPNMIDI_VERSION \
OPNMIDI_TOSTR(OPNMIDI_VERSION_MAJOR) "." \
OPNMIDI_TOSTR(OPNMIDI_VERSION_MINOR) "." \
@ -63,6 +64,28 @@ enum OPNMIDI_VolumeModels
OPNMIDI_VolumeModel_9X
};
enum OPNMIDI_SampleType
{
OPNMIDI_SampleType_S16 = 0, /* signed PCM 16-bit */
OPNMIDI_SampleType_S8, /* signed PCM 8-bit */
OPNMIDI_SampleType_F32, /* float 32-bit */
OPNMIDI_SampleType_F64, /* float 64-bit */
OPNMIDI_SampleType_S24, /* signed PCM 24-bit */
OPNMIDI_SampleType_S32, /* signed PCM 32-bit */
OPNMIDI_SampleType_U8, /* unsigned PCM 8-bit */
OPNMIDI_SampleType_U16, /* unsigned PCM 16-bit */
OPNMIDI_SampleType_U24, /* unsigned PCM 24-bit */
OPNMIDI_SampleType_U32, /* unsigned PCM 32-bit */
OPNMIDI_SampleType_Count,
};
struct OPNMIDI_AudioFormat
{
enum OPNMIDI_SampleType type; /* type of sample */
unsigned containerSize; /* size in bytes of the storage type */
unsigned sampleOffset; /* distance in bytes between consecutive samples */
};
struct OPN2_MIDIPlayer
{
void *opn2_midiPlayer;
@ -80,10 +103,16 @@ extern int opn2_getNumChips(struct OPN2_MIDIPlayer *device);
/*Enable or disable Enables scaling of modulator volumes*/
extern void opn2_setScaleModulators(struct OPN2_MIDIPlayer *device, int smod);
/*Enable(1) or Disable(0) full-range brightness (MIDI CC74 used in XG music to filter result sounding) scaling.
By default, brightness affects sound between 0 and 64.
When this option is enabled, the range will use a full range from 0 up to 127.
*/
extern void opn2_setFullRangeBrightness(struct OPN2_MIDIPlayer *device, int fr_brightness);
/*Enable or disable built-in loop (built-in loop supports 'loopStart' and 'loopEnd' tags to loop specific part)*/
extern void opn2_setLoopEnabled(struct OPN2_MIDIPlayer *device, int loopEn);
/*Enable or disable Logariphmic volume changer (-1 sets default per bank, 0 disable, 1 enable) */
/* !!!DEPRECATED!!! */
extern void opn2_setLogarithmicVolumes(struct OPN2_MIDIPlayer *device, int logvol);
/*Set different volume range model */
@ -96,15 +125,33 @@ extern int opn2_openBankFile(struct OPN2_MIDIPlayer *device, const char *filePat
extern int opn2_openBankData(struct OPN2_MIDIPlayer *device, const void *mem, long size);
/*Returns chip emulator name string*/
/* DEPRECATED */
extern const char *opn2_emulatorName();
/*Returns chip emulator name string*/
extern const char *opn2_chipEmulatorName(struct OPN2_MIDIPlayer *device);
enum Opn2_Emulator
{
OPNMIDI_EMU_MAME = 0,
OPNMIDI_EMU_NUKED,
OPNMIDI_EMU_GENS,
OPNMIDI_EMU_GX,
OPNMIDI_EMU_end
};
/* Switch the emulation core */
extern int opn2_switchEmulator(struct OPN2_MIDIPlayer *device, int emulator);
typedef struct {
OPN2_UInt16 major;
OPN2_UInt16 minor;
OPN2_UInt16 patch;
} OPN2_Version;
/*Run emulator with PCM rate to reduce CPU usage on slow devices. May decrease sounding accuracy.*/
extern int opn2_setRunAtPcmRate(struct OPN2_MIDIPlayer *device, int enabled);
/*Returns string which contains a version number*/
extern const char *opn2_linkedLibraryVersion();
@ -186,11 +233,17 @@ extern struct Opn2_MarkerEntry opn2_metaMarker(struct OPN2_MIDIPlayer *device, s
/*Take a sample buffer and iterate MIDI timers */
extern int opn2_play(struct OPN2_MIDIPlayer *device, int sampleCount, short out[]);
extern int opn2_play(struct OPN2_MIDIPlayer *device, int sampleCount, short *out);
/*Take a sample buffer and iterate MIDI timers */
extern int opn2_playFormat(struct OPN2_MIDIPlayer *device, int sampleCount, OPN2_UInt8 *left, OPN2_UInt8 *right, const struct OPNMIDI_AudioFormat *format);
/*Generate audio output from chip emulators without iteration of MIDI timers.*/
extern int opn2_generate(struct OPN2_MIDIPlayer *device, int sampleCount, short *out);
/*Generate audio output from chip emulators without iteration of MIDI timers.*/
extern int opn2_generateFormat(struct OPN2_MIDIPlayer *device, int sampleCount, OPN2_UInt8 *left, OPN2_UInt8 *right, const struct OPNMIDI_AudioFormat *format);
/**
* @brief Periodic tick handler.
* @param device

View file

@ -0,0 +1,127 @@
/*
* libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation
*
* MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* ADLMIDI Library API: Copyright (c) 2015-2018 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 OPNMIDI_BANKMAP_H
#define OPNMIDI_BANKMAP_H
#include <list>
#include <utility>
#include <stdint.h>
#include <stddef.h>
#include "opnmidi_ptr.hpp"
/**
* A simple hash map which accepts bank numbers as keys, can be reserved to a
* fixed size, offers O(1) search and insertion, has a hash function to
* optimize for the worst case, and has some good cache locality properties.
*/
template <class T>
class BasicBankMap
{
public:
typedef uint16_t key_type; /* the bank identifier */
typedef T mapped_type;
typedef std::pair<key_type, T> value_type;
BasicBankMap();
void reserve(size_t capacity);
size_t size() const
{ return m_size; }
size_t capacity() const
{ return m_capacity; }
bool empty() const
{ return m_size == 0; }
class iterator;
iterator begin() const;
iterator end() const;
struct do_not_expand_t {};
iterator find(key_type key);
void erase(iterator it);
std::pair<iterator, bool> insert(const value_type &value);
std::pair<iterator, bool> insert(const value_type &value, do_not_expand_t);
void clear();
T &operator[](key_type key);
private:
struct Slot;
enum { minimum_allocation = 4 };
enum
{
hash_bits = 8, /* worst case # of collisions: 128^2/2^hash_bits */
hash_buckets = 1 << hash_bits,
};
public:
class iterator
{
public:
iterator();
value_type &operator*() const { return slot->value; }
value_type *operator->() const { return &slot->value; }
iterator &operator++();
bool operator==(const iterator &o) const;
bool operator!=(const iterator &o) const;
void to_ptrs(void *ptrs[3]);
static iterator from_ptrs(void *const ptrs[3]);
private:
Slot **buckets;
Slot *slot;
size_t index;
iterator(Slot **buckets, Slot *slot, size_t index);
#ifdef _MSC_VER
template<class _T>
friend class BasicBankMap;
#else
friend class BasicBankMap<T>;
#endif
};
private:
struct Slot {
Slot *next, *prev;
value_type value;
Slot() : next(NULL), prev(NULL) {}
};
AdlMIDI_SPtrArray<Slot *> m_buckets;
std::list< AdlMIDI_SPtrArray<Slot> > m_allocations;
Slot *m_freeslots;
size_t m_size;
size_t m_capacity;
static size_t hash(key_type key);
Slot *allocate_slot();
Slot *ensure_allocate_slot();
void free_slot(Slot *slot);
Slot *bucket_find(size_t index, key_type key);
void bucket_add(size_t index, Slot *slot);
void bucket_remove(size_t index, Slot *slot);
};
#include "opnmidi_bankmap.tcc"
#endif // OPNMIDI_BANKMAP_H

View file

@ -0,0 +1,283 @@
/*
* libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation
*
* MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* ADLMIDI Library API: Copyright (c) 2015-2018 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/>.
*/
#include "opnmidi_bankmap.h"
#include <cassert>
template <class T>
inline BasicBankMap<T>::BasicBankMap()
: m_freeslots(NULL),
m_size(0),
m_capacity(0)
{
m_buckets.reset(new Slot *[hash_buckets]());
}
template <class T>
inline size_t BasicBankMap<T>::hash(key_type key)
{
// disregard the 0 high bit in LSB
key = (key & 127) | ((key >> 8) << 7);
// take low part as hash value
return key & (hash_buckets - 1);
}
template <class T>
void BasicBankMap<T>::reserve(size_t capacity)
{
if(m_capacity >= capacity)
return;
size_t need = capacity - m_capacity;
const size_t minalloc = static_cast<size_t>(minimum_allocation);
need = (need < minalloc) ? minalloc : need;
AdlMIDI_SPtrArray<Slot> slotz;
slotz.reset(new Slot[need]);
m_allocations.push_back(slotz);
m_capacity += need;
for(size_t i = need; i-- > 0;)
free_slot(&slotz[i]);
}
template <class T>
typename BasicBankMap<T>::iterator
BasicBankMap<T>::begin() const
{
iterator it(m_buckets.get(), NULL, 0);
while(it.index < hash_buckets && !(it.slot = m_buckets[it.index]))
++it.index;
return it;
}
template <class T>
typename BasicBankMap<T>::iterator
BasicBankMap<T>::end() const
{
iterator it(m_buckets.get(), NULL, hash_buckets);
return it;
}
template <class T>
typename BasicBankMap<T>::iterator BasicBankMap<T>::find(key_type key)
{
size_t index = hash(key);
Slot *slot = bucket_find(index, key);
if(!slot)
return end();
return iterator(m_buckets.get(), slot, index);
}
template <class T>
void BasicBankMap<T>::erase(iterator it)
{
bucket_remove(it.index, it.slot);
free_slot(it.slot);
--m_size;
}
template <class T>
inline BasicBankMap<T>::iterator::iterator()
: buckets(NULL), slot(NULL), index(0)
{
}
template <class T>
inline BasicBankMap<T>::iterator::iterator(Slot **buckets, Slot *slot, size_t index)
: buckets(buckets), slot(slot), index(index)
{
}
template <class T>
typename BasicBankMap<T>::iterator &
BasicBankMap<T>::iterator::operator++()
{
if(slot->next)
slot = slot->next;
else {
Slot *slot = NULL;
++index;
while(index < hash_buckets && !(slot = buckets[index]))
++index;
this->slot = slot;
}
return *this;
}
template <class T>
bool BasicBankMap<T>::iterator::operator==(const iterator &o) const
{
return buckets == o.buckets && slot == o.slot && index == o.index;
}
template <class T>
inline bool BasicBankMap<T>::iterator::operator!=(const iterator &o) const
{
return !operator==(o);
}
template <class T>
void BasicBankMap<T>::iterator::to_ptrs(void *ptrs[3])
{
ptrs[0] = buckets;
ptrs[1] = slot;
ptrs[2] = (void *)index;
}
template <class T>
typename BasicBankMap<T>::iterator
BasicBankMap<T>::iterator::from_ptrs(void *const ptrs[3])
{
iterator it;
it.buckets = (Slot **)ptrs[0];
it.slot = (Slot *)ptrs[1];
it.index = (size_t)ptrs[2];
return it;
}
template <class T>
std::pair<typename BasicBankMap<T>::iterator, bool>
BasicBankMap<T>::insert(const value_type &value)
{
size_t index = hash(value.first);
Slot *slot = bucket_find(index, value.first);
if(slot)
return std::make_pair(iterator(m_buckets.get(), slot, index), false);
slot = allocate_slot();
if(!slot) {
reserve(m_capacity + minimum_allocation);
slot = ensure_allocate_slot();
}
slot->value = value;
bucket_add(index, slot);
++m_size;
return std::make_pair(iterator(m_buckets.get(), slot, index), true);
}
template <class T>
std::pair<typename BasicBankMap<T>::iterator, bool>
BasicBankMap<T>::insert(const value_type &value, do_not_expand_t)
{
size_t index = hash(value.first);
Slot *slot = bucket_find(index, value.first);
if(slot)
return std::make_pair(iterator(m_buckets.get(), slot, index), false);
slot = allocate_slot();
if(!slot)
return std::make_pair(end(), false);
slot->value = value;
bucket_add(index, slot);
++m_size;
return std::make_pair(iterator(m_buckets.get(), slot, index), true);
}
template <class T>
void BasicBankMap<T>::clear()
{
for(size_t i = 0; i < hash_buckets; ++i) {
Slot *slot = m_buckets[i];
while (Slot *cur = slot) {
slot = slot->next;
free_slot(cur);
}
m_buckets[i] = NULL;
}
m_size = 0;
}
template <class T>
inline T &BasicBankMap<T>::operator[](key_type key)
{
return insert(value_type(key, T())).first->second;
}
template <class T>
typename BasicBankMap<T>::Slot *
BasicBankMap<T>::allocate_slot()
{
Slot *slot = m_freeslots;
if(!slot)
return NULL;
Slot *next = slot->next;
if(next)
next->prev = NULL;
m_freeslots = next;
return slot;
}
template <class T>
inline typename BasicBankMap<T>::Slot *
BasicBankMap<T>::ensure_allocate_slot()
{
Slot *slot = allocate_slot();
assert(slot);
return slot;
}
template <class T>
void BasicBankMap<T>::free_slot(Slot *slot)
{
Slot *next = m_freeslots;
if(next)
next->prev = slot;
slot->prev = NULL;
slot->next = next;
m_freeslots = slot;
m_freeslots->value.second = T();
}
template <class T>
typename BasicBankMap<T>::Slot *
BasicBankMap<T>::bucket_find(size_t index, key_type key)
{
Slot *slot = m_buckets[index];
while(slot && slot->value.first != key)
slot = slot->next;
return slot;
}
template <class T>
void BasicBankMap<T>::bucket_add(size_t index, Slot *slot)
{
assert(slot);
Slot *next = m_buckets[index];
if(next)
next->prev = slot;
slot->next = next;
m_buckets[index] = slot;
}
template <class T>
void BasicBankMap<T>::bucket_remove(size_t index, Slot *slot)
{
assert(slot);
Slot *prev = slot->prev;
Slot *next = slot->next;
if(!prev)
m_buckets[index] = next;
else
prev->next = next;
if(next)
next->prev = prev;
}

View file

@ -161,7 +161,7 @@ bool OPNMIDIplay::LoadBank(OPNMIDIplay::fileReader &fr)
uint16_t version = 1;
uint16_t count_melodic_banks = 1;
uint16_t count_percusive_banks = 1;
uint16_t count_percussive_banks = 1;
if(fr.read(magic, 1, 11) != 11)
{
@ -194,15 +194,14 @@ bool OPNMIDIplay::LoadBank(OPNMIDIplay::fileReader &fr)
}
}
opn.dynamic_instruments.clear();
opn.dynamic_metainstruments.clear();
if((readU16BE(fr, count_melodic_banks) != 2) || (readU16BE(fr, count_percusive_banks) != 2))
opn.cleanInstrumentBanks();
if((readU16BE(fr, count_melodic_banks) != 2) || (readU16BE(fr, count_percussive_banks) != 2))
{
errorStringOut = "Can't load bank file: Can't read count of banks!";
return false;
}
if((count_melodic_banks < 1) || (count_percusive_banks < 1))
if((count_melodic_banks < 1) || (count_percussive_banks < 1))
{
errorStringOut = "Custom bank: Too few banks in this file!";
return false;
@ -214,8 +213,10 @@ bool OPNMIDIplay::LoadBank(OPNMIDIplay::fileReader &fr)
return false;
}
opn.dynamic_melodic_banks.clear();
opn.dynamic_percussion_banks.clear();
opn.cleanInstrumentBanks();
std::vector<OPN2::Bank *> banks;
banks.reserve(count_melodic_banks + count_percussive_banks);
if(version >= 2)//Read bank meta-entries
{
@ -224,49 +225,44 @@ bool OPNMIDIplay::LoadBank(OPNMIDIplay::fileReader &fr)
uint8_t bank_meta[34];
if(fr.read(bank_meta, 1, 34) != 34)
{
opn.dynamic_melodic_banks.clear();
opn.cleanInstrumentBanks();
errorStringOut = "Custom bank: Fail to read melodic bank meta-data!";
return false;
}
uint16_t bank = uint16_t(bank_meta[33]) * 256 + uint16_t(bank_meta[32]);
size_t offset = opn.dynamic_melodic_banks.size();
opn.dynamic_melodic_banks[bank] = offset;
//strncpy(bankMeta.name, char_p(bank_meta), 32);
uint16_t bankno = uint16_t(bank_meta[33]) * 256 + uint16_t(bank_meta[32]);
OPN2::Bank &bank = opn.dynamic_banks[bankno];
//strncpy(bank.name, char_p(bank_meta), 32);
banks.push_back(&bank);
}
for(uint16_t i = 0; i < count_percusive_banks; i++)
for(uint16_t i = 0; i < count_percussive_banks; i++)
{
uint8_t bank_meta[34];
if(fr.read(bank_meta, 1, 34) != 34)
{
opn.dynamic_melodic_banks.clear();
opn.dynamic_percussion_banks.clear();
opn.cleanInstrumentBanks();
errorStringOut = "Custom bank: Fail to read percussion bank meta-data!";
return false;
}
uint16_t bank = uint16_t(bank_meta[33]) * 256 + uint16_t(bank_meta[32]);
size_t offset = opn.dynamic_percussion_banks.size();
opn.dynamic_percussion_banks[bank] = offset;
//strncpy(bankMeta.name, char_p(bank_meta), 32);
uint16_t bankno = uint16_t(bank_meta[33]) * 256 + uint16_t(bank_meta[32]) + OPN2::PercussionTag;
OPN2::Bank &bank = opn.dynamic_banks[bankno];
//strncpy(bank.name, char_p(bank_meta), 32);
banks.push_back(&bank);
}
}
opn.dynamic_percussion_offset = count_melodic_banks * 128;
uint16_t total = 128 * count_melodic_banks + 128 * count_percusive_banks;
size_t total = 128 * opn.dynamic_banks.size();
for(uint16_t i = 0; i < total; i++)
for(size_t i = 0; i < total; i++)
{
opnInstData data;
opnInstMeta meta;
opnInstMeta2 &meta = banks[i / 128]->ins[i % 128];
opnInstData &data = meta.opn[0];
uint8_t idata[WOPL_INST_SIZE_V2];
size_t readSize = version >= 2 ? WOPL_INST_SIZE_V2 : WOPL_INST_SIZE_V1;
if(fr.read(idata, 1, readSize) != readSize)
{
opn.dynamic_instruments.clear();
opn.dynamic_metainstruments.clear();
opn.dynamic_melodic_banks.clear();
opn.dynamic_percussion_banks.clear();
opn.cleanInstrumentBanks();
errorStringOut = "Can't load bank file: Failed to read instrument data";
return false;
}
@ -295,15 +291,11 @@ bool OPNMIDIplay::LoadBank(OPNMIDIplay::fileReader &fr)
meta.ms_sound_koff = 500;
}
meta.opnno1 = uint16_t(opn.dynamic_instruments.size());
meta.opnno2 = uint16_t(opn.dynamic_instruments.size());
meta.opn[1] = meta.opn[0];
/* Junk, delete later */
meta.fine_tune = 0.0;
/* Junk, delete later */
opn.dynamic_instruments.push_back(data);
opn.dynamic_metainstruments.push_back(meta);
}
applySetup();
@ -336,7 +328,7 @@ bool OPNMIDIplay::LoadMIDI(OPNMIDIplay::fileReader &fr)
AdlMIDI_CPtr<uint8_t> cvt_buf;
errorString.clear();
if(opn.dynamic_instruments.empty())
if(opn.dynamic_banks.empty())
{
errorStringOut = "Bank is not set! Please load any instruments bank by using of adl_openBankFile() or adl_openBankData() functions!";
return false;
@ -470,7 +462,6 @@ riffskip:
fr.seek(0x7D, SEEK_SET);
TrackCount = 1;
DeltaTicks = 60;
opn.LogarithmicVolumes = true;
//opl.CartoonersVolumes = true;
opn.m_musicMode = OPN2::MODE_RSXX;
opn.m_volumeScale = OPN2::VOLUME_CMF;
@ -494,11 +485,8 @@ riffskip:
TrackData.clear();
TrackData.resize(TrackCount, std::vector<uint8_t>());
//CurrentPosition.track.clear();
//CurrentPosition.track.resize(TrackCount);
InvDeltaTicks = fraction<uint64_t>(1, 1000000l * static_cast<uint64_t>(DeltaTicks));
//Tempo = 1000000l * InvDeltaTicks;
Tempo = fraction<uint64_t>(1, static_cast<uint64_t>(DeltaTicks));
Tempo = fraction<uint64_t>(1, static_cast<uint64_t>(DeltaTicks) * 2);
static const unsigned char EndTag[4] = {0xFF, 0x2F, 0x00, 0x00};
size_t totalGotten = 0;
@ -560,14 +548,14 @@ riffskip:
return false;
}
//Build new MIDI events table (ALPHA!!!)
//Build new MIDI events table
if(!buildTrackData())
{
errorStringOut = fr._fileName + ": MIDI data parsing error has occouped!\n" + errorString;
return false;
}
opn.Reset(m_setup.PCM_RATE); // Reset AdLib
opn.Reset(m_setup.emulator, m_setup.PCM_RATE); // Reset OPN2 chip
ch.clear();
ch.resize(opn.NumChannels);
return true;

File diff suppressed because it is too large Load diff

View file

@ -23,6 +23,32 @@
#include "opnmidi_private.hpp"
#if defined(OPNMIDI_DISABLE_NUKED_EMULATOR) && defined(OPNMIDI_DISABLE_MAME_EMULATOR) && \
defined(OPNMIDI_DISABLE_GENS_EMULATOR) && defined(OPNMIDI_DISABLE_GX_EMULATOR)
#error "No emulators enabled. You must enable at least one emulator to use this library!"
#endif
// Nuked OPN2 emulator, Most accurate, but requires the powerful CPU
#ifndef OPNMIDI_DISABLE_NUKED_EMULATOR
#include "chips/nuked_opn2.h"
#endif
// MAME YM2612 emulator, Well-accurate and fast
#ifndef OPNMIDI_DISABLE_MAME_EMULATOR
#include "chips/mame_opn2.h"
#endif
// GENS 2.10 emulator, very outdated and inaccurate, but gives the best performance
#ifndef OPNMIDI_DISABLE_GENS_EMULATOR
#include "chips/gens_opn2.h"
#endif
// Genesis Plus GX emulator, Variant of MAME with enhancements
#ifndef OPNMIDI_DISABLE_GX_EMULATOR
#include "chips/gx_opn2.h"
#endif
static const uint8_t NoteChannels[6] = { 0, 1, 2, 4, 5, 6 };
static inline void getOpnChannel(uint32_t in_channel,
@ -36,43 +62,30 @@ static inline void getOpnChannel(uint32_t in_channel,
out_ch = ch4 % 3;
}
const opnInstMeta &OPN2::GetAdlMetaIns(size_t n)
void OPN2::cleanInstrumentBanks()
{
return dynamic_metainstruments[n];
dynamic_banks.clear();
}
size_t OPN2::GetAdlMetaNumber(size_t midiins)
static opnInstMeta2 makeEmptyInstrument()
{
return midiins;
opnInstMeta2 ins;
memset(&ins, 0, sizeof(opnInstMeta2));
ins.flags = opnInstMeta::Flag_NoSound;
return ins;
}
static const opnInstData opn2_emptyInstrument = {
{
{{0, 0, 0, 0, 0, 0, 0}},
{{0, 0, 0, 0, 0, 0, 0}},
{{0, 0, 0, 0, 0, 0, 0}},
{{0, 0, 0, 0, 0, 0, 0}}
},
0, 0, 0
};
const opnInstData &OPN2::GetAdlIns(size_t insno)
{
if(insno >= dynamic_instruments.size())
return opn2_emptyInstrument;
return dynamic_instruments[insno];
}
const opnInstMeta2 OPN2::emptyInstrument = makeEmptyInstrument();
OPN2::OPN2() :
regLFO(0),
dynamic_percussion_offset(128),
DynamicInstrumentTag(0x8000u),
DynamicMetaInstrumentTag(0x4000000u),
NumCards(1),
LogarithmicVolumes(false),
m_musicMode(MODE_MIDI),
m_volumeScale(VOLUME_Generic)
{}
{
// Initialize blank instruments banks
cleanInstrumentBanks();
}
OPN2::~OPN2()
{
@ -81,15 +94,7 @@ OPN2::~OPN2()
void OPN2::PokeO(size_t card, uint8_t port, uint8_t index, uint8_t value)
{
#ifdef OPNMIDI_USE_LEGACY_EMULATOR
if(port == 1)
cardsOP2[card]->write1(index, value);
else
cardsOP2[card]->write0(index, value);
#else
OPN2_WriteBuffered(cardsOP2[card], 0 + (port) * 2, index);
OPN2_WriteBuffered(cardsOP2[card], 1 + (port) * 2, value);
#endif
cardsOP2[card]->writeReg(port, index, value);
}
void OPN2::NoteOff(size_t c)
@ -113,7 +118,7 @@ void OPN2::NoteOn(unsigned c, double hertz) // Hertz range: 0..131071
if(hertz < 0 || hertz > 262143) // Avoid infinite loop
return;
while(hertz >= 2047.5)
while((hertz >= 1023.75) && (x2 < 0x3800))
{
hertz /= 2.0; // Calculate octave
x2 += 0x800;
@ -134,8 +139,7 @@ void OPN2::Touch_Real(unsigned c, unsigned volume, uint8_t brightness)
uint8_t port, cc;
getOpnChannel(c, card, port, cc);
size_t i = ins[c];
const opnInstData &adli = GetAdlIns(i);
const opnInstData &adli = ins[c];
uint8_t op_vol[4] =
{
@ -185,13 +189,12 @@ void OPN2::Touch_Real(unsigned c, unsigned volume, uint8_t brightness)
// 63 + chanvol * (instrvol / 63.0 - 1)
}
void OPN2::Patch(uint16_t c, size_t i)
void OPN2::Patch(uint16_t c, const opnInstData &adli)
{
unsigned card;
uint8_t port, cc;
getOpnChannel(uint16_t(c), card, port, cc);
ins[c] = i;
const opnInstData &adli = GetAdlIns(i);
ins[c] = adli;
#if 1 //Reg1-Op1, Reg1-Op2, Reg1-Op3, Reg1-Op4,....
for(uint8_t d = 0; d < 7; d++)
{
@ -221,7 +224,7 @@ void OPN2::Pan(unsigned c, unsigned value)
unsigned card;
uint8_t port, cc;
getOpnChannel(uint16_t(c), card, port, cc);
const opnInstData &adli = GetAdlIns(ins[c]);
const opnInstData &adli = ins[c];
uint8_t val = (value & 0xC0) | (adli.lfosens & 0x3F);
regBD[c] = val;
PokeO(card, port, 0xB4 + cc, val);
@ -248,7 +251,6 @@ void OPN2::ChangeVolumeRangesModel(OPNMIDI_VolumeModels volumeModel)
break;
case OPNMIDI_VolumeModel_CMF:
LogarithmicVolumes = true;
m_volumeScale = OPN2::VOLUME_CMF;
break;
@ -269,34 +271,51 @@ void OPN2::ChangeVolumeRangesModel(OPNMIDI_VolumeModels volumeModel)
void OPN2::ClearChips()
{
for(size_t i = 0; i < cardsOP2.size(); i++)
delete cardsOP2[i];
cardsOP2[i].reset(NULL);
cardsOP2.clear();
}
void OPN2::Reset(unsigned long PCM_RATE)
void OPN2::Reset(int emulator, unsigned long PCM_RATE)
{
ClearChips();
ins.clear();
pit.clear();
regBD.clear();
cardsOP2.resize(NumCards, NULL);
cardsOP2.resize(NumCards, AdlMIDI_SPtr<OPNChipBase>());
#ifndef OPNMIDI_USE_LEGACY_EMULATOR
OPN2_SetChipType(ym3438_type_asic);
#endif
for(size_t i = 0; i < cardsOP2.size(); i++)
{
#ifdef OPNMIDI_USE_LEGACY_EMULATOR
cardsOP2[i] = new OPNMIDI_Ym2612_Emu();
cardsOP2[i]->set_rate(PCM_RATE, 7670454.0);
#else
cardsOP2[i] = new ym3438_t;
std::memset(cardsOP2[i], 0, sizeof(ym3438_t));
OPN2_Reset(cardsOP2[i], (Bit32u)PCM_RATE, 7670454);
#endif
switch(emulator)
{
default:
#ifndef OPNMIDI_DISABLE_MAME_EMULATOR
case OPNMIDI_EMU_MAME:
cardsOP2[i].reset(new MameOPN2());
break;
#endif
#ifndef OPNMIDI_DISABLE_NUKED_EMULATOR
case OPNMIDI_EMU_NUKED:
cardsOP2[i].reset(new NukedOPN2());
break;
#endif
#ifndef OPNMIDI_DISABLE_GENS_EMULATOR
case OPNMIDI_EMU_GENS:
cardsOP2[i].reset(new GensOPN2());
break;
#endif
#ifndef OPNMIDI_DISABLE_GX_EMULATOR
case OPNMIDI_EMU_GX:
cardsOP2[i].reset(new GXOPN2());
break;
#endif
}
cardsOP2[i]->setRate((uint32_t)PCM_RATE, 7670454);
if(runAtPcmRate)
cardsOP2[i]->setRunningAtPcmRate(true);
}
NumChannels = NumCards * 6;
ins.resize(NumChannels, 189);
ins.resize(NumChannels, emptyInstrument.opn[0]);
pit.resize(NumChannels, 0);
regBD.resize(NumChannels, 0);

View file

@ -63,6 +63,7 @@ typedef __int32 ssize_t;
#include <cmath>
#include <cstdarg>
#include <cstdio>
#include <cassert>
#include <vector> // vector
#include <deque> // deque
#include <cmath> // exp, log, ceil
@ -77,63 +78,88 @@ typedef __int32 ssize_t;
#include <deque>
#include <algorithm>
#ifdef _MSC_VER
#pragma warning(disable:4244)
#pragma warning(disable:4267)
#pragma warning(disable:4146)
#pragma warning(disable:4800)
/*
* Workaround for some compilers are has no those macros in their headers!
*/
#ifndef INT8_MIN
#define INT8_MIN (-0x7f - 1)
#endif
#ifndef INT16_MIN
#define INT16_MIN (-0x7fff - 1)
#endif
#ifndef INT32_MIN
#define INT32_MIN (-0x7fffffff - 1)
#endif
#ifndef INT8_MAX
#define INT8_MAX 0x7f
#endif
#ifndef INT16_MAX
#define INT16_MAX 0x7fff
#endif
#ifndef INT32_MAX
#define INT32_MAX 0x7fffffff
#endif
#include "fraction.hpp"
#ifdef OPNMIDI_USE_LEGACY_EMULATOR
#include "Ym2612_ChipEmu.h"
#else
#include "ym3438.h"
#endif
#include "chips/opn_chip_base.h"
#include "opnbank.h"
#include "opnmidi.h"
#include "opnmidi_ptr.hpp"
#include "opnmidi_bankmap.h"
#define ADL_UNUSED(x) (void)x
#define OPN_PANNING_LEFT 0x80
#define OPN_PANNING_RIGHT 0x40
#define OPN_PANNING_BOTH 0xC0
extern std::string OPN2MIDI_ErrorString;
/*
Smart pointer for C heaps, created with malloc() call.
FAQ: Why not std::shared_ptr? Because of Android NDK now doesn't supports it
Sample conversions to various formats
*/
template<class PTR>
class AdlMIDI_CPtr
template <class Real>
inline Real opn2_cvtReal(int32_t x)
{
PTR *m_p;
public:
AdlMIDI_CPtr() : m_p(NULL) {}
~AdlMIDI_CPtr()
{
reset(NULL);
}
void reset(PTR *p = NULL)
{
if(m_p)
free(m_p);
m_p = p;
}
PTR *get()
{
return m_p;
}
PTR &operator*()
{
return *m_p;
}
PTR *operator->()
{
return m_p;
}
};
return x * ((Real)1 / INT16_MAX);
}
inline int32_t opn2_cvtS16(int32_t x)
{
x = (x < INT16_MIN) ? INT16_MIN : x;
x = (x > INT16_MAX) ? INT16_MAX : x;
return x;
}
inline int32_t opn2_cvtS8(int32_t x)
{
return opn2_cvtS16(x) / 256;
}
inline int32_t opn2_cvtS24(int32_t x)
{
return opn2_cvtS16(x) * 256;
}
inline int32_t opn2_cvtS32(int32_t x)
{
return opn2_cvtS16(x) * 65536;
}
inline int32_t opn2_cvtU16(int32_t x)
{
return opn2_cvtS16(x) - INT16_MIN;
}
inline int32_t opn2_cvtU8(int32_t x)
{
return opn2_cvtS8(x) - INT8_MIN;
}
inline int32_t opn2_cvtU24(int32_t x)
{
enum { int24_min = -(1 << 23) };
return opn2_cvtS24(x) - int24_min;
}
inline int32_t opn2_cvtU32(int32_t x)
{
// unsigned operation because overflow on signed integers is undefined
return (uint32_t)opn2_cvtS32(x) - (uint32_t)INT32_MIN;
}
class OPNMIDIplay;
class OPN2
@ -142,39 +168,32 @@ public:
friend class OPNMIDIplay;
uint32_t NumChannels;
char ____padding[4];
#ifdef OPNMIDI_USE_LEGACY_EMULATOR
std::vector<OPNMIDI_Ym2612_Emu*> cardsOP2;
#else
std::vector<ym3438_t*> cardsOP2;
#endif
std::vector<AdlMIDI_SPtr<OPNChipBase > > cardsOP2;
private:
std::vector<size_t> ins; // index to adl[], cached, needed by Touch()
std::vector<opnInstData> ins; // patch data, cached, needed by Touch()
std::vector<uint8_t> pit; // value poked to B0, cached, needed by NoteOff)(
std::vector<uint8_t> regBD;
uint8_t regLFO;
//! Meta information about every instrument
std::vector<opnInstMeta> dynamic_metainstruments;
//! Raw instrument data ready to be sent to the chip
std::vector<opnInstData> dynamic_instruments;
size_t dynamic_percussion_offset;
typedef std::map<uint16_t, size_t> BankMap;
BankMap dynamic_melodic_banks;
BankMap dynamic_percussion_banks;
const unsigned DynamicInstrumentTag /* = 0x8000u*/,
DynamicMetaInstrumentTag /* = 0x4000000u*/;
const opnInstMeta &GetAdlMetaIns(size_t n);
size_t GetAdlMetaNumber(size_t midiins);
const opnInstData &GetAdlIns(size_t insno);
void cleanInstrumentBanks();
public:
struct Bank
{
opnInstMeta2 ins[128];
};
typedef BasicBankMap<Bank> BankMap;
BankMap dynamic_banks;
public:
static const opnInstMeta2 emptyInstrument;
enum { PercussionTag = 1 << 15 };
//! Total number of running concurrent emulated chips
unsigned int NumCards;
//! Carriers-only are scaled by default by volume level. This flag will tell to scale modulators too.
bool ScaleModulators;
//! Required to play CMF files. Can be turned on by using of "CMF" volume model
bool LogarithmicVolumes;
//! Run emulator at PCM rate if that possible. Reduces sounding accuracy, but decreases CPU usage on lower rates.
bool runAtPcmRate;
char ___padding2[3];
enum MusicMode
@ -211,12 +230,12 @@ public:
void NoteOn(unsigned c, double hertz);
void Touch_Real(unsigned c, unsigned volume, uint8_t brightness = 127);
void Patch(uint16_t c, size_t i);
void Patch(uint16_t c, const opnInstData &adli);
void Pan(unsigned c, unsigned value);
void Silence();
void ChangeVolumeRangesModel(OPNMIDI_VolumeModels volumeModel);
void ClearChips();
void Reset(unsigned long PCM_RATE);
void Reset(int emulator, unsigned long PCM_RATE);
};
@ -402,7 +421,7 @@ public:
bool eof()
{
if(fp)
return std::feof(fp);
return (std::feof(fp) != 0);
else
return mp_tell >= mp_size;
}
@ -420,9 +439,15 @@ public:
uint8_t bank_lsb, bank_msb;
uint8_t patch;
uint8_t volume, expression;
uint8_t panning, vibrato, sustain;
uint8_t panning, vibrato, aftertouch, sustain;
//! Per note Aftertouch values
uint8_t noteAftertouch[128];
//! Is note aftertouch has any non-zero value
bool noteAfterTouchInUse;
char ____padding[6];
double bend, bendsense;
int bend;
double bendsense;
int bendsense_lsb, bendsense_msb;
double vibpos, vibspeed, vibdepth;
int64_t vibdelay;
uint8_t lastlrpn, lastmrpn;
@ -431,53 +456,218 @@ public:
bool is_xg_percussion;
struct NoteInfo
{
uint8_t note;
bool active;
// Current pressure
uint8_t vol;
// Note vibrato (a part of Note Aftertouch feature)
uint8_t vibrato;
char ____padding[1];
// Tone selected on noteon:
int16_t tone;
char ____padding2[4];
char ____padding2[10];
// Patch selected on noteon; index to banks[AdlBank][]
size_t midiins;
// Index to physical adlib data structure, adlins[]
size_t insmeta;
typedef std::map<uint16_t, uint16_t> PhysMap;
typedef uint16_t Phys;
// Patch selected
const opnInstMeta2 *ains;
enum
{
MaxNumPhysChans = 2,
MaxNumPhysItemCount = MaxNumPhysChans,
};
struct Phys
{
//! Destination chip channel
uint16_t chip_chan;
//! ins, inde to adl[]
opnInstData ains;
void assign(const Phys &oth)
{
ains = oth.ains;
}
bool operator==(const Phys &oth) const
{
return (ains == oth.ains);
}
bool operator!=(const Phys &oth) const
{
return !operator==(oth);
}
};
// List of OPN2 channels it is currently occupying.
std::map<uint16_t /*adlchn*/, Phys> phys;
Phys chip_channels[MaxNumPhysItemCount];
//! Count of used channels.
unsigned chip_channels_count;
//
Phys *phys_find(unsigned chip_chan)
{
Phys *ph = NULL;
for(unsigned i = 0; i < chip_channels_count && !ph; ++i)
if(chip_channels[i].chip_chan == chip_chan)
ph = &chip_channels[i];
return ph;
}
Phys *phys_find_or_create(unsigned chip_chan)
{
Phys *ph = phys_find(chip_chan);
if(!ph) {
if(chip_channels_count < MaxNumPhysItemCount) {
ph = &chip_channels[chip_channels_count++];
ph->chip_chan = (uint16_t)chip_chan;
}
}
return ph;
}
Phys *phys_ensure_find_or_create(unsigned chip_chan)
{
Phys *ph = phys_find_or_create(chip_chan);
assert(ph);
return ph;
}
void phys_erase_at(const Phys *ph)
{
intptr_t pos = ph - chip_channels;
assert(pos < static_cast<intptr_t>(chip_channels_count));
for(intptr_t i = pos + 1; i < static_cast<intptr_t>(chip_channels_count); ++i)
chip_channels[i - 1] = chip_channels[i];
--chip_channels_count;
}
void phys_erase(unsigned chip_chan)
{
Phys *ph = phys_find(chip_chan);
if(ph)
phys_erase_at(ph);
}
};
typedef std::map<uint8_t, NoteInfo> activenotemap_t;
typedef activenotemap_t::iterator activenoteiterator;
char ____padding2[5];
activenotemap_t activenotes;
NoteInfo activenotes[128];
struct activenoteiterator
{
explicit activenoteiterator(NoteInfo *info = NULL)
: ptr(info) {}
activenoteiterator &operator++()
{
if(ptr->note == 127)
ptr = NULL;
else
for(++ptr; ptr && !ptr->active;)
ptr = (ptr->note == 127) ? NULL : (ptr + 1);
return *this;
}
activenoteiterator operator++(int)
{
activenoteiterator pos = *this;
++*this;
return pos;
}
NoteInfo &operator*() const
{ return *ptr; }
NoteInfo *operator->() const
{ return ptr; }
bool operator==(activenoteiterator other) const
{ return ptr == other.ptr; }
bool operator!=(activenoteiterator other) const
{ return ptr != other.ptr; }
operator NoteInfo *() const
{ return ptr; }
private:
NoteInfo *ptr;
};
activenoteiterator activenotes_begin()
{
activenoteiterator it(activenotes);
return (it->active) ? it : ++it;
}
activenoteiterator activenotes_find(uint8_t note)
{
assert(note < 128);
return activenoteiterator(
activenotes[note].active ? &activenotes[note] : NULL);
}
activenoteiterator activenotes_ensure_find(uint8_t note)
{
activenoteiterator it = activenotes_find(note);
assert(it);
return it;
}
std::pair<activenoteiterator, bool> activenotes_insert(uint8_t note)
{
assert(note < 128);
NoteInfo &info = activenotes[note];
bool inserted = !info.active;
if(inserted) info.active = true;
return std::pair<activenoteiterator, bool>(activenoteiterator(&info), inserted);
}
void activenotes_erase(activenoteiterator pos)
{
if(pos)
pos->active = false;
}
bool activenotes_empty()
{
return !activenotes_begin();
}
void activenotes_clear()
{
for(uint8_t i = 0; i < 128; ++i) {
activenotes[i].note = i;
activenotes[i].active = false;
}
}
void reset()
{
portamento = 0;
resetAllControllers();
patch = 0;
vibpos = 0;
bank_lsb = 0;
bank_msb = 0;
patch = 0;
volume = 100;
expression = 127;
panning = 0xC0;
vibrato = 0;
sustain = 0;
bend = 0.0;
bendsense = 2 / 8192.0;
vibpos = 0;
vibspeed = 2 * 3.141592653 * 5.0;
vibdepth = 0.5 / 127;
vibdelay = 0;
lastlrpn = 0;
lastmrpn = 0;
nrpn = false;
brightness = 127;
is_xg_percussion = false;
}
MIDIchannel()
: activenotes()
void resetAllControllers()
{
bend = 0;
bendsense_msb = 2;
bendsense_lsb = 0;
updateBendSensitivity();
volume = 100;
expression = 127;
sustain = 0;
vibrato = 0;
aftertouch = 0;
std::memset(noteAftertouch, 0, 128);
noteAfterTouchInUse = false;
vibspeed = 2 * 3.141592653 * 5.0;
vibdepth = 0.5 / 127;
vibdelay = 0;
panning = OPN_PANNING_BOTH;
portamento = 0;
brightness = 127;
}
bool hasVibrato()
{
return (vibrato > 0) || (aftertouch > 0) || noteAfterTouchInUse;
}
void updateBendSensitivity()
{
int cent = bendsense_msb * 128 + bendsense_lsb;
bendsense = cent * (1.0 / (128 * 8192));
}
MIDIchannel()
{
activenotes_clear();
reset();
}
};
@ -485,37 +675,71 @@ public:
// Additional information about OPN channels
struct OpnChannel
{
// For collisions
struct Location
{
uint16_t MidCh;
uint8_t note;
bool operator==(const Location &b) const
{
return MidCh == b.MidCh && note == b.note;
}
bool operator< (const Location &b) const
{
return MidCh < b.MidCh || (MidCh == b.MidCh && note < b.note);
}
bool operator==(const Location &l) const
{ return MidCh == l.MidCh && note == l.note; }
bool operator!=(const Location &l) const
{ return !operator==(l); }
char ____padding[1];
};
struct LocationData
{
LocationData *prev, *next;
Location loc;
bool sustained;
char ____padding[1];
char ____padding[3];
MIDIchannel::NoteInfo::Phys ins; // a copy of that in phys[]
char ____padding2[4];
//! Has fixed sustain, don't iterate "on" timeout
bool fixed_sustain;
//! Timeout until note will be allowed to be killed by channel manager while it is on
int64_t kon_time_until_neglible;
int64_t vibdelay;
};
typedef std::map<Location, LocationData> users_t;
users_t users;
// If the channel is keyoff'd
int64_t koff_time_until_neglible;
enum { users_max = 128 };
LocationData *users_first, *users_free_cells;
LocationData users_cells[users_max];
unsigned users_size;
bool users_empty() const;
LocationData *users_find(Location loc);
LocationData *users_allocate();
LocationData *users_find_or_create(Location loc);
LocationData *users_insert(const LocationData &x);
void users_erase(LocationData *user);
void users_clear();
void users_assign(const LocationData *users, size_t count);
// For channel allocation:
OpnChannel(): users(), koff_time_until_neglible(0) { }
OpnChannel(): koff_time_until_neglible(0)
{
users_clear();
}
OpnChannel(const OpnChannel &oth): koff_time_until_neglible(oth.koff_time_until_neglible)
{
if(oth.users_first)
{
users_first = NULL;
users_assign(oth.users_first, oth.users_size);
}
else
users_clear();
}
OpnChannel &operator=(const OpnChannel &oth)
{
koff_time_until_neglible = oth.koff_time_until_neglible;
users_assign(oth.users_first, oth.users_size);
return *this;
}
void AddAge(int64_t ms);
};
@ -648,6 +872,8 @@ public:
struct Setup
{
int emulator;
bool runAtPcmRate;
unsigned int OpnBank;
unsigned int NumCards;
unsigned int LogarithmicVolumes;
@ -655,6 +881,7 @@ public:
//unsigned int SkipForward;
bool loopingIsEnabled;
int ScaleModulators;
bool fullRangeBrightnessCC74;
double delay;
double carry;
@ -688,6 +915,8 @@ private:
std::map<uint64_t /*track*/, uint64_t /*channel begin index*/> current_device;
std::vector<OpnChannel> ch;
//! Counter of arpeggio processing
size_t m_arpeggioCounter;
#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER
std::vector<std::vector<uint8_t> > TrackData;
@ -760,7 +989,7 @@ public:
#endif
OPN2 opn;
int16_t outBuf[1024];
int32_t outBuf[1024];
Setup m_setup;
@ -879,7 +1108,9 @@ private:
Upd_Volume = 0x4,
Upd_Pitch = 0x8,
Upd_All = Upd_Pan + Upd_Volume + Upd_Pitch,
Upd_Off = 0x20
Upd_Off = 0x20,
Upd_Mute = 0x40,
Upd_OffMute = Upd_Off + Upd_Mute
};
void NoteUpdate(uint16_t MidCh,
@ -894,15 +1125,15 @@ private:
// Determine how good a candidate this adlchannel
// would be for playing a note from this instrument.
int64_t CalculateAdlChannelGoodness(size_t c, uint16_t ins, uint16_t /*MidCh*/) const;
int64_t CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins, uint16_t /*MidCh*/) const;
// A new note will be played on this channel using this instrument.
// Kill existing notes on this channel (or don't, if we do arpeggio)
void PrepareAdlChannelForNewNote(size_t c, size_t ins);
void PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins);
void KillOrEvacuate(
size_t from_channel,
OpnChannel::users_t::iterator j,
OpnChannel::LocationData *j,
MIDIchannel::activenoteiterator i);
void Panic();
void KillSustainingNotes(int32_t MidCh = -1, int32_t this_adlchn = -1);

View file

@ -0,0 +1,217 @@
/*
* libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation
*
* MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* ADLMIDI Library API: Copyright (c) 2015-2018 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 OPNMIDI_PTR_HPP_THING
#define OPNMIDI_PTR_HPP_THING
#include <algorithm> // swap
#include <stddef.h>
#include <stdlib.h>
/*
Generic deleters for smart pointers
*/
template <class T>
struct ADLMIDI_DefaultDelete
{
void operator()(T *x) { delete x; }
};
template <class T>
struct ADLMIDI_DefaultArrayDelete
{
void operator()(T *x) { delete[] x; }
};
struct ADLMIDI_CDelete
{
void operator()(void *x) { free(x); }
};
/*
Safe unique pointer for C++98, non-copyable but swappable.
*/
template< class T, class Deleter = ADLMIDI_DefaultDelete<T> >
class AdlMIDI_UPtr
{
T *m_p;
public:
explicit AdlMIDI_UPtr(T *p)
: m_p(p) {}
~AdlMIDI_UPtr()
{
reset();
}
void reset(T *p = NULL)
{
if(p != m_p) {
if(m_p) {
Deleter del;
del(m_p);
}
m_p = p;
}
}
void swap(AdlMIDI_UPtr &other)
{
std::swap(m_p, other.m_p);
}
T *get() const
{
return m_p;
}
T &operator*() const
{
return *m_p;
}
T *operator->() const
{
return m_p;
}
T &operator[](size_t index) const
{
return m_p[index];
}
private:
AdlMIDI_UPtr(const AdlMIDI_UPtr &);
AdlMIDI_UPtr &operator=(const AdlMIDI_UPtr &);
};
template <class T>
void swap(AdlMIDI_UPtr<T> &a, AdlMIDI_UPtr<T> &b)
{
a.swap(b);
}
/**
Unique pointer for arrays.
*/
template<class T>
class AdlMIDI_UPtrArray :
public AdlMIDI_UPtr< T, ADLMIDI_DefaultArrayDelete<T> >
{
public:
explicit AdlMIDI_UPtrArray(T *p = NULL)
: AdlMIDI_UPtr< T, ADLMIDI_DefaultArrayDelete<T> >(p) {}
};
/**
Unique pointer for C memory.
*/
template<class T>
class AdlMIDI_CPtr :
public AdlMIDI_UPtr< T, ADLMIDI_CDelete >
{
public:
explicit AdlMIDI_CPtr(T *p = NULL)
: AdlMIDI_UPtr< T, ADLMIDI_CDelete >(p) {}
};
/*
Shared pointer with non-atomic counter
FAQ: Why not std::shared_ptr? Because of Android NDK now doesn't supports it
*/
template< class T, class Deleter = ADLMIDI_DefaultDelete<T> >
class AdlMIDI_SPtr
{
T *m_p;
size_t *m_counter;
public:
explicit AdlMIDI_SPtr(T *p = NULL)
: m_p(p), m_counter(p ? new size_t(1) : NULL) {}
~AdlMIDI_SPtr()
{
reset(NULL);
}
AdlMIDI_SPtr(const AdlMIDI_SPtr &other)
: m_p(other.m_p), m_counter(other.m_counter)
{
if(m_counter)
++*m_counter;
}
AdlMIDI_SPtr &operator=(const AdlMIDI_SPtr &other)
{
if(this == &other)
return *this;
reset();
m_p = other.m_p;
m_counter = other.m_counter;
if(m_counter)
++*m_counter;
return *this;
}
void reset(T *p = NULL)
{
if(p != m_p) {
if(m_p && --*m_counter == 0) {
Deleter del;
del(m_p);
if(!p) {
delete m_counter;
m_counter = NULL;
}
}
m_p = p;
if(p) {
if(!m_counter)
m_counter = new size_t;
*m_counter = 1;
}
}
}
T *get() const
{
return m_p;
}
T &operator*() const
{
return *m_p;
}
T *operator->() const
{
return m_p;
}
T &operator[](size_t index) const
{
return m_p[index];
}
};
/**
Shared pointer for arrays.
*/
template<class T>
class AdlMIDI_SPtrArray :
public AdlMIDI_SPtr< T, ADLMIDI_DefaultArrayDelete<T> >
{
public:
explicit AdlMIDI_SPtrArray(T *p = NULL)
: AdlMIDI_SPtr< T, ADLMIDI_DefaultArrayDelete<T> >(p) {}
};
#endif //ADLMIDI_PTR_HPP_THING

View file

@ -2158,6 +2158,7 @@ ADVSNDMNU_OPLSYNTHESIS = "OPL Synthesis";
ADVSNDMNU_OPLNUMCHIPS = "Number of emulated OPL chips";
ADVSNDMNU_OPLFULLPAN = "Full MIDI stereo panning";
ADVSNDMNU_OPLCORES = "OPL Emulator Core";
ADVSNDMNU_OPNCORES = "OPN2 Emulator Core";
ADVSNDMNU_GUSEMULATION = "GUS Emulation";
ADVSNDMNU_GUSCONFIG = "GUS config file";
ADVSNDMNU_MIDIVOICES = "MIDI voices";
@ -2183,10 +2184,14 @@ ADVSNDMNU_FREEVERB = "Freeverb";
ADVSNDMNU_GLOBAL_FREEVERB = "Global Freeverb";
ADVSNDMNU_ADVRESAMPLING = "Advanced Resampling";
ADVSNDMNU_OPLBANK = "OPL Bank";
ADVSNDMNU_ADLOPLCORES = "OPL Emulator Core";
ADVSNDMNU_RUNPCMRATE = "Run emulator at PCM rate";
ADVSNDMNU_ADLNUMCHIPS = "Number of emulated OPL chips";
ADVSNDMNU_VLMODEL = "Volume model";
ADVSNDMNU_OPNNUMCHIPS = "Number of emulated OPN chips";
// ADLMIDI's emulation cores
// ADLMIDI's volume models
ADLVLMODEL_AUTO = "Auto (Use setup of bank)";
ADLVLMODEL_GENERIC = "Generic";
@ -2373,9 +2378,13 @@ OPTVAL_512K = "512K";
OPTVAL_768K = "768K";
OPTVAL_1024K = "1024K";
OPTVAL_MAMEOPL2 = "MAME OPL2";
OPTVAL_MAMEOPN2 = "MAME YM2612";
OPTVAL_DOSBOXOPL3 = "DOSBox OPL3";
OPTVAL_JAVAOPL3 = "Java OPL3";
OPTVAL_NUKEDOPL3 = "Nuked OPL3";
OPTVAL_NUKEDOPL3174 = "Nuked OPL3 v1.7.4";
OPTVAL_NUKEDOPN2 = "Nuked OPN2";
OPTVAL_GENSOPN2 = "GENS YM2612";
OPTVAL_SOUNDSYSTEM = "Sound System";
OPTVAL_FOO_DUMB = "foo_dumb";
OPTVAL_ALIASING = "Aliasing";

View file

@ -1823,18 +1823,36 @@ OptionMenu ModReplayerOptions protected
5, "$ADLVLMODEL_WIN9X"
}
OptionValue ADLOplCores
{
0, "$OPTVAL_NUKEDOPL3"
1, "$OPTVAL_NUKEDOPL3174"
2, "$OPTVAL_DOSBOXOPL3"
}
OptionValue OpnCores
{
0, "$OPTVAL_MAMEOPN2"
1, "$OPTVAL_NUKEDOPN2"
2, "$OPTVAL_GENSOPN2"
}
OptionMenu ADLOptions protected
{
Title "$ADVSNDMNU_ADLMIDI"
LabeledSubmenu "$ADVSNDMNU_OPLBANK", "adl_bank", "ADLBankMenu"
Slider "$ADVSNDMNU_ADLNUMCHIPS", "adl_chips_count", 1, 32, 1, 0
Option "$ADVSNDMNU_VLMODEL", "adl_volume_model", "AdlVolumeModels"
Title "$ADVSNDMNU_ADLMIDI"
LabeledSubmenu "$ADVSNDMNU_OPLBANK", "adl_bank", "ADLBankMenu"
Option "$ADVSNDMNU_OPLCORES", "adl_emulator_id", "ADLOplCores"
Option "$ADVSNDMNU_RUNPCMRATE", "adl_run_at_pcm_rate", "OnOff"
Slider "$ADVSNDMNU_ADLNUMCHIPS", "adl_chips_count", 1, 32, 1, 0
Option "$ADVSNDMNU_VLMODEL", "adl_volume_model", "AdlVolumeModels"
}
OptionMenu OPNOptions protected
{
Title "$ADVSNDMNU_OPNMIDI"
Slider "$ADVSNDMNU_OPNNUMCHIPS", "opn_chips_count", 1, 32, 1, 0
Title "$ADVSNDMNU_OPNMIDI"
Option "$ADVSNDMNU_OPNCORES", "opn_emulator_id", "OpnCores"
Option "$ADVSNDMNU_RUNPCMRATE", "opn_run_at_pcm_rate", "OnOff"
Slider "$ADVSNDMNU_OPNNUMCHIPS", "opn_chips_count", 1, 32, 1, 0
}
/*=======================================

Binary file not shown.