Merge remote-tracking branch 'remotes/origin/master' into modern

This commit is contained in:
Christoph Oelckers 2018-06-23 23:03:51 +02:00
commit a4622e70d0
66 changed files with 20517 additions and 8994 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
@ -857,14 +857,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

View file

@ -36,6 +36,25 @@
struct MapData
{
private:
struct ResourceHolder
{
FResourceFile *data = nullptr;
~ResourceHolder()
{
delete data;
}
ResourceHolder &operator=(FResourceFile *other) { data = other; return *this; }
FResourceFile *operator->() { return data; }
operator FResourceFile *() const { return data; }
};
// The order of members here is important
// Resource should be destructed after MapLumps as readers may share FResourceLump objects
// For example, this is the case when map .wad is loaded from .pk3 file
ResourceHolder resource;
struct MapLump
{
char Name[8] = { 0 };
@ -48,13 +67,6 @@ public:
bool isText = false;
bool InWad = false;
int lumpnum = -1;
FResourceFile * resource = nullptr;
~MapData()
{
if (resource != nullptr) delete resource;
resource = nullptr;
}
/*
void Seek(unsigned int lumpindex)

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((uint32_t)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.