Update libADLMIDI up to 1.5.0

## 1.5.0   2020-09-28
 * Drum note length expanding is now supported in real-time mode (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)
 * Channels manager has been improved (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)
 * Nuked OPL3 1.8 emulator got some optimizations ported from 1.7 where they are was applied previously (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)
 * Reworked rhythm-mode percussions system, WOPL banks with rhythm-mode percussions
 * Added Public Domain Opal OPL3 emulator made by Reality (a team who originally made the Reality Adlib Tracker) (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)
 * Added LGPL licensed JavaOPL3 emulator made by Robson Cozendey in Java and later rewritten into C++ for GZDoom (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)
 * Fully rewritten an embedded bank database format, embedded banks now supports a wider set (more than 127:127 instruments in one bank)
 * Improved accuracy of the DMX volume model, include the buggy AM interpretation
 * Improved accuracy of Apogee volume model, include the bug of AM instruments
 * Improved accuracy of the Win9X volume model
 * Removed C++ extras. C++-bounded instruments tester is useless since a real-time MIDI API can completely replace it
 * Added AIL volume model
 * Added Generic FM variant of Win9X volume model
 * Fixed an incorrect work of CC-121 (See https://github.com/Wohlstand/libADLMIDI/issues/227 for details)
 * Added HMI volume model (Thanks to [Alexey Khokholov](https://github.com/nukeykt) for help with research!)
 * Added frequency models, assigned to every volume model: AIL, HMI, DMX, Apogee, 9X, and the Generic formula
This commit is contained in:
Wohlstand 2020-09-28 21:17:46 +03:00 committed by Christoph Oelckers
parent 9461e3cc62
commit 1d4a016c41
49 changed files with 15243 additions and 13623 deletions

View file

@ -6,18 +6,20 @@ use_fast_math()
add_definitions(-DADLMIDI_DISABLE_MIDI_SEQUENCER)
add_library( adl STATIC
adldata.cpp
adlmidi.cpp
adlmidi_load.cpp
adlmidi_midiplay.cpp
adlmidi_opl3.cpp
adlmidi_private.cpp
adlmidi.cpp
adlmidi_load.cpp
inst_db.cpp
chips/opal_opl3.cpp
chips/dosbox/dbopl.cpp
chips/nuked_opl3_v174.cpp
chips/java_opl3.cpp
chips/dosbox_opl3.cpp
chips/nuked_opl3.cpp
chips/nuked/nukedopl3_174.c
chips/nuked/nukedopl3.c
chips/nuked_opl3.cpp
chips/nuked_opl3_v174.cpp
wopl/wopl_file.c
)
target_link_libraries( adl )

File diff suppressed because it is too large Load diff

View file

@ -1,138 +0,0 @@
/*
* 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) 2016 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 ADLDATA_H
#define ADLDATA_H
#include <string.h>
#include <stdint.h>
#include <cstring>
#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); }
struct adldata
{
uint32_t modulator_E862, carrier_E862; // See below
uint8_t modulator_40, carrier_40; // KSL/attenuation settings
uint8_t feedconn; // Feedback/connection bits for the channel
int8_t finetune;
};
ADLDATA_BYTE_COMPARABLE(struct adldata)
struct adlinsdata
{
enum { Flag_Pseudo4op = 0x01, Flag_NoSound = 0x02, Flag_Real4op = 0x04 };
enum { Flag_RM_BassDrum = 0x08, Flag_RM_Snare = 0x10, Flag_RM_TomTom = 0x18,
Flag_RM_Cymbal = 0x20, Flag_RM_HiHat = 0x28, Mask_RhythmMode = 0x38 };
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;
int8_t midi_velocity_offset;
double voice2_fine_tune;
};
ADLDATA_BYTE_COMPARABLE(struct adlinsdata)
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;
int8_t midi_velocity_offset;
double voice2_fine_tune;
static adlinsdata2 from_adldata(const adlinsdata &d);
};
ADLDATA_BYTE_COMPARABLE(struct adlinsdata2)
#undef ADLDATA_BYTE_COMPARABLE
#pragma pack(pop)
/**
* @brief Bank global setup
*/
struct AdlBankSetup
{
int volumeModel;
bool deepTremolo;
bool deepVibrato;
bool adLibPercussions;
bool scaleModulators;
};
#ifndef DISABLE_EMBEDDED_BANKS
int maxAdlBanks();
extern const adldata adl[];
extern const adlinsdata adlins[];
extern const unsigned short banks[][256];
extern const char* const banknames[];
extern const AdlBankSetup adlbanksetup[];
#endif
/**
* @brief Conversion of storage formats
*/
inline adlinsdata2 adlinsdata2::from_adldata(const adlinsdata &d)
{
adlinsdata2 ins;
ins.tone = d.tone;
ins.flags = d.flags;
ins.ms_sound_kon = d.ms_sound_kon;
ins.ms_sound_koff = d.ms_sound_koff;
ins.midi_velocity_offset = d.midi_velocity_offset;
ins.voice2_fine_tune = d.voice2_fine_tune;
#ifdef DISABLE_EMBEDDED_BANKS
std::memset(ins.adl, 0, sizeof(adldata) * 2);
#else
ins.adl[0] = ::adl[d.adlno1];
ins.adl[1] = ::adl[d.adlno2];
#endif
return ins;
}
/**
* @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

@ -1,8 +1,8 @@
/*
* libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation
* libADLMIDI is a free Software MIDI synthesizer 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>
* ADLMIDI Library API: Copyright (c) 2015-2020 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
@ -21,7 +21,45 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "adlmidi_midiplay.hpp"
#include "adlmidi_opl3.hpp"
#include "adlmidi_private.hpp"
#ifndef ADLMIDI_HW_OPL
#include "chips/opl_chip_base.h"
#endif
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
#include "midi_sequencer.hpp"
#endif
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf c99_snprintf
#define vsnprintf c99_vsnprintf
__inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap)
{
int count = -1;
if (size != 0)
count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
if (count == -1)
count = _vscprintf(format, ap);
return count;
}
__inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...)
{
int count;
va_list ap;
va_start(ap, format);
count = c99_vsnprintf(outBuf, size, format, ap);
va_end(ap);
return count;
}
#endif
/* Unify MIDI player casting and interface between ADLMIDI and OPNMIDI */
#define GET_MIDI_PLAYER(device) reinterpret_cast<MIDIplay *>((device)->adl_midiPlayer)
@ -112,13 +150,14 @@ ADLMIDI_EXPORT int adl_setNumChips(ADL_MIDIPlayer *device, int numChips)
else if(play->m_setup.numFourOps < -1)
play->m_setup.numFourOps = -1;
if(!play->m_synth.setupLocked())
Synth &synth = *play->m_synth;
if(!synth.setupLocked())
{
play->m_synth.m_numChips = play->m_setup.numChips;
synth.m_numChips = play->m_setup.numChips;
if(play->m_setup.numFourOps < 0)
adlCalculateFourOpChannels(play, true);
else
play->m_synth.m_numFourOps = static_cast<uint32_t>(play->m_setup.numFourOps);
synth.m_numFourOps = static_cast<uint32_t>(play->m_setup.numFourOps);
play->partialReset();
return 0;
}
@ -141,7 +180,7 @@ ADLMIDI_EXPORT int adl_getNumChipsObtained(struct ADL_MIDIPlayer *device)
return -2;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return (int)play->m_synth.m_numChips;
return (int)play->m_synth->m_numChips;
}
ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank)
@ -155,7 +194,7 @@ ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank)
"adl_openBankData() functions instead of adl_setBank().");
return -1;
#else
const uint32_t NumBanks = static_cast<uint32_t>(maxAdlBanks());
const uint32_t NumBanks = static_cast<uint32_t>(g_embeddedBanksCount);
int32_t bankno = bank;
if(bankno < 0)
@ -171,8 +210,9 @@ ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank)
return -1;
}
Synth &synth = *play->m_synth;
play->m_setup.bankId = static_cast<uint32_t>(bankno);
play->m_synth.setEmbeddedBank(play->m_setup.bankId);
synth.setEmbeddedBank(play->m_setup.bankId);
play->applySetup();
return 0;
@ -182,7 +222,7 @@ ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank)
ADLMIDI_EXPORT int adl_getBanksCount()
{
#ifndef DISABLE_EMBEDDED_BANKS
return maxAdlBanks();
return static_cast<int>(g_embeddedBanksCount);
#else
return 0;
#endif
@ -191,7 +231,7 @@ ADLMIDI_EXPORT int adl_getBanksCount()
ADLMIDI_EXPORT const char *const *adl_getBankNames()
{
#ifndef DISABLE_EMBEDDED_BANKS
return banknames;
return g_embeddedBankNames;
#else
return NULL;
#endif
@ -203,7 +243,7 @@ ADLMIDI_EXPORT int adl_reserveBanks(ADL_MIDIPlayer *device, unsigned banks)
return -1;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
OPL3::BankMap &map = play->m_synth.m_insBanks;
Synth::BankMap &map = play->m_synth->m_insBanks;
map.reserve(banks);
return (int)map.capacity();
}
@ -216,13 +256,13 @@ ADLMIDI_EXPORT int adl_getBank(ADL_MIDIPlayer *device, const ADL_BankId *idp, in
ADL_BankId id = *idp;
if(id.lsb > 127 || id.msb > 127 || id.percussive > 1)
return -1;
size_t idnumber = ((id.msb << 8) | id.lsb | (id.percussive ? size_t(OPL3::PercussionTag) : 0));
size_t idnumber = ((id.msb << 8) | id.lsb | (id.percussive ? size_t(Synth::PercussionTag) : 0));
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
OPL3::BankMap &map = play->m_synth.m_insBanks;
Synth::BankMap &map = play->m_synth->m_insBanks;
OPL3::BankMap::iterator it;
Synth::BankMap::iterator it;
if(!(flags & ADLMIDI_Bank_Create))
{
it = map.find(idnumber);
@ -231,16 +271,16 @@ ADLMIDI_EXPORT int adl_getBank(ADL_MIDIPlayer *device, const ADL_BankId *idp, in
}
else
{
std::pair<size_t, OPL3::Bank> value;
std::pair<size_t, Synth::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;
value.second.ins[i].flags = OplInstMeta::Flag_NoSound;
std::pair<OPL3::BankMap::iterator, bool> ir;
if(flags & ADLMIDI_Bank_CreateRt)
std::pair<Synth::BankMap::iterator, bool> ir;
if((flags & ADLMIDI_Bank_CreateRt) == ADLMIDI_Bank_CreateRt)
{
ir = map.insert(value, OPL3::BankMap::do_not_expand_t());
ir = map.insert(value, Synth::BankMap::do_not_expand_t());
if(ir.first == map.end())
return -1;
}
@ -258,11 +298,11 @@ ADLMIDI_EXPORT int adl_getBankId(ADL_MIDIPlayer *device, const ADL_Bank *bank, A
if(!device || !bank)
return -1;
OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
OPL3::BankMap::key_type idnumber = it->first;
Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer);
Synth::BankMap::key_type idnumber = it->first;
id->msb = (idnumber >> 8) & 127;
id->lsb = idnumber & 127;
id->percussive = (idnumber & OPL3::PercussionTag) ? 1 : 0;
id->percussive = (idnumber & Synth::PercussionTag) ? 1 : 0;
return 0;
}
@ -273,8 +313,8 @@ ADLMIDI_EXPORT int adl_removeBank(ADL_MIDIPlayer *device, ADL_Bank *bank)
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
OPL3::BankMap &map = play->m_synth.m_insBanks;
OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
Synth::BankMap &map = play->m_synth->m_insBanks;
Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer);
size_t size = map.size();
map.erase(it);
return (map.size() != size) ? 0 : -1;
@ -287,9 +327,9 @@ ADLMIDI_EXPORT int adl_getFirstBank(ADL_MIDIPlayer *device, ADL_Bank *bank)
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
OPL3::BankMap &map = play->m_synth.m_insBanks;
Synth::BankMap &map = play->m_synth->m_insBanks;
OPL3::BankMap::iterator it = map.begin();
Synth::BankMap::iterator it = map.begin();
if(it == map.end())
return -1;
@ -304,9 +344,9 @@ ADLMIDI_EXPORT int adl_getNextBank(ADL_MIDIPlayer *device, ADL_Bank *bank)
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
OPL3::BankMap &map = play->m_synth.m_insBanks;
Synth::BankMap &map = play->m_synth->m_insBanks;
OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer);
if(++it == map.end())
return -1;
@ -319,7 +359,7 @@ ADLMIDI_EXPORT int adl_getInstrument(ADL_MIDIPlayer *device, const ADL_Bank *ban
if(!device || !bank || index > 127 || !ins)
return -1;
OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer);
cvt_FMIns_to_ADLI(*ins, it->second.ins[index]);
ins->version = 0;
return 0;
@ -333,7 +373,7 @@ ADLMIDI_EXPORT int adl_setInstrument(ADL_MIDIPlayer *device, ADL_Bank *bank, uns
if(ins->version != 0)
return -1;
OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer);
cvt_ADLI_to_FMIns(it->second.ins[index], *ins);
return 0;
}
@ -353,16 +393,32 @@ ADLMIDI_EXPORT int adl_loadEmbeddedBank(struct ADL_MIDIPlayer *device, ADL_Bank
"adl_openBankData() functions instead of adl_loadEmbeddedBank().");
return -1;
#else
if(num < 0 || num >= maxAdlBanks())
if(num < 0 || num >= static_cast<int>(g_embeddedBanksCount))
return -1;
OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer);
size_t id = it->first;
for (unsigned i = 0; i < 128; ++i) {
size_t insno = i + ((id & OPL3::PercussionTag) ? 128 : 0);
size_t adlmeta = ::banks[num][insno];
it->second.ins[i] = adlinsdata2::from_adldata(::adlins[adlmeta]);
const BanksDump::BankEntry &bankEntry = g_embeddedBanks[num];
bool ss = (id & Synth::PercussionTag);
const size_t bankID = 0;
// bank_count_t maxBanks = ss ? bankEntry.banksPercussionCount : bankEntry.banksMelodicCount;
bank_count_t banksOffset = ss ? bankEntry.banksOffsetPercussive : bankEntry.banksOffsetMelodic;
size_t bankIndex = g_embeddedBanksMidiIndex[banksOffset + bankID];
const BanksDump::MidiBank &bankData = g_embeddedBanksMidi[bankIndex];
for (unsigned i = 0; i < 128; ++i)
{
midi_bank_idx_t instIdx = bankData.insts[i];
if(instIdx < 0)
{
it->second.ins[i].flags = OplInstMeta::Flag_NoSound;
continue;
}
BanksDump::InstrumentEntry instIn = g_embeddedBanksInstruments[instIdx];
adlFromInstrument(instIn, it->second.ins[i]);
}
return 0;
#endif
@ -383,14 +439,15 @@ ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4)
return -1;
}
Synth &synth = *play->m_synth;
play->m_setup.numFourOps = ops4;
if(!play->m_synth.setupLocked())
if(!synth.setupLocked())
{
if(play->m_setup.numFourOps < 0)
adlCalculateFourOpChannels(play, true);
else
play->m_synth.m_numFourOps = static_cast<uint32_t>(play->m_setup.numFourOps);
play->m_synth.updateChannelCategories();
synth.m_numFourOps = static_cast<uint32_t>(play->m_setup.numFourOps);
synth.updateChannelCategories();
}
return 0;
@ -411,23 +468,14 @@ ADLMIDI_EXPORT int adl_getNumFourOpsChnObtained(struct ADL_MIDIPlayer *device)
return -2;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return (int)play->m_synth.m_numFourOps;
return (int)play->m_synth->m_numFourOps;
}
/* !!!DEPRECATED!!! AND !!DUMMIED!! */
ADLMIDI_EXPORT void adl_setPercMode(ADL_MIDIPlayer *device, int percmod)
{
if(!device) return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_setup.rhythmMode = percmod;
if(!play->m_synth.setupLocked())
{
play->m_synth.m_rhythmMode = play->m_setup.rhythmMode < 0 ?
(play->m_synth.m_insBankSetup.adLibPercussions) :
(play->m_setup.rhythmMode != 0);
play->m_synth.updateChannelCategories();
}
ADL_UNUSED(device);
ADL_UNUSED(percmod);
}
ADLMIDI_EXPORT void adl_setHVibrato(ADL_MIDIPlayer *device, int hvibro)
@ -435,13 +483,14 @@ ADLMIDI_EXPORT void adl_setHVibrato(ADL_MIDIPlayer *device, int hvibro)
if(!device) return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
Synth &synth = *play->m_synth;
play->m_setup.deepVibratoMode = hvibro;
if(!play->m_synth.setupLocked())
if(!synth.setupLocked())
{
play->m_synth.m_deepVibratoMode = play->m_setup.deepVibratoMode < 0 ?
play->m_synth.m_insBankSetup.deepVibrato :
synth.m_deepVibratoMode = play->m_setup.deepVibratoMode < 0 ?
synth.m_insBankSetup.deepVibrato :
(play->m_setup.deepVibratoMode != 0);
play->m_synth.commitDeepFlags();
synth.commitDeepFlags();
}
}
@ -450,7 +499,7 @@ ADLMIDI_EXPORT int adl_getHVibrato(struct ADL_MIDIPlayer *device)
if(!device) return -1;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_synth.m_deepVibratoMode;
return play->m_synth->m_deepVibratoMode;
}
ADLMIDI_EXPORT void adl_setHTremolo(ADL_MIDIPlayer *device, int htremo)
@ -458,13 +507,14 @@ ADLMIDI_EXPORT void adl_setHTremolo(ADL_MIDIPlayer *device, int htremo)
if(!device) return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
Synth &synth = *play->m_synth;
play->m_setup.deepTremoloMode = htremo;
if(!play->m_synth.setupLocked())
if(!synth.setupLocked())
{
play->m_synth.m_deepTremoloMode = play->m_setup.deepTremoloMode < 0 ?
play->m_synth.m_insBankSetup.deepTremolo :
synth.m_deepTremoloMode = play->m_setup.deepTremoloMode < 0 ?
synth.m_insBankSetup.deepTremolo :
(play->m_setup.deepTremoloMode != 0);
play->m_synth.commitDeepFlags();
synth.commitDeepFlags();
}
}
@ -473,7 +523,7 @@ ADLMIDI_EXPORT int adl_getHTremolo(struct ADL_MIDIPlayer *device)
if(!device) return -1;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_synth.m_deepTremoloMode;
return play->m_synth->m_deepTremoloMode;
}
ADLMIDI_EXPORT void adl_setScaleModulators(ADL_MIDIPlayer *device, int smod)
@ -482,11 +532,12 @@ ADLMIDI_EXPORT void adl_setScaleModulators(ADL_MIDIPlayer *device, int smod)
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
Synth &synth = *play->m_synth;
play->m_setup.scaleModulators = smod;
if(!play->m_synth.setupLocked())
if(!synth.setupLocked())
{
play->m_synth.m_scaleModulators = play->m_setup.scaleModulators < 0 ?
play->m_synth.m_insBankSetup.scaleModulators :
synth.m_scaleModulators = play->m_setup.scaleModulators < 0 ?
synth.m_insBankSetup.scaleModulators :
(play->m_setup.scaleModulators != 0);
}
}
@ -502,13 +553,14 @@ ADLMIDI_EXPORT void adl_setFullRangeBrightness(struct ADL_MIDIPlayer *device, in
ADLMIDI_EXPORT void adl_setLoopEnabled(ADL_MIDIPlayer *device, int loopEn)
{
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
if(!device)
return;
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_sequencer.setLoopEnabled(loopEn != 0);
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_sequencer->setLoopEnabled(loopEn != 0);
#else
ADL_UNUSED(device);
ADL_UNUSED(loopEn);
#endif
}
@ -519,7 +571,7 @@ ADLMIDI_EXPORT void adl_setSoftPanEnabled(ADL_MIDIPlayer *device, int softPanEn)
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_synth.m_softPanning = (softPanEn != 0);
play->m_synth->m_softPanning = (softPanEn != 0);
}
/* !!!DEPRECATED!!! */
@ -529,13 +581,14 @@ ADLMIDI_EXPORT void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
Synth &synth = *play->m_synth;
play->m_setup.logarithmicVolumes = (logvol != 0);
if(!play->m_synth.setupLocked())
if(!synth.setupLocked())
{
if(play->m_setup.logarithmicVolumes)
play->m_synth.setVolumeScaleModel(ADLMIDI_VolumeModel_NativeOPL3);
synth.setVolumeScaleModel(ADLMIDI_VolumeModel_NativeOPL3);
else
play->m_synth.setVolumeScaleModel(static_cast<ADLMIDI_VolumeModels>(play->m_synth.m_volumeScale));
synth.setVolumeScaleModel(static_cast<ADLMIDI_VolumeModels>(synth.m_volumeScale));
}
}
@ -545,13 +598,14 @@ ADLMIDI_EXPORT void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int v
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
Synth &synth = *play->m_synth;
play->m_setup.volumeScaleModel = volumeModel;
if(!play->m_synth.setupLocked())
if(!synth.setupLocked())
{
if(play->m_setup.volumeScaleModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model
play->m_synth.m_volumeScale = (OPL3::VolumesScale)play->m_synth.m_insBankSetup.volumeModel;
synth.m_volumeScale = (Synth::VolumesScale)synth.m_insBankSetup.volumeModel;
else
play->m_synth.setVolumeScaleModel(static_cast<ADLMIDI_VolumeModels>(volumeModel));
synth.setVolumeScaleModel(static_cast<ADLMIDI_VolumeModels>(volumeModel));
}
}
@ -561,7 +615,7 @@ ADLMIDI_EXPORT int adl_getVolumeRangeModel(struct ADL_MIDIPlayer *device)
return -1;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_synth.getVolumeScaleModel();
return play->m_synth->getVolumeScaleModel();
}
ADLMIDI_EXPORT int adl_openBankFile(struct ADL_MIDIPlayer *device, const char *filePath)
@ -675,8 +729,9 @@ ADLMIDI_EXPORT const char *adl_chipEmulatorName(struct ADL_MIDIPlayer *device)
#ifndef ADLMIDI_HW_OPL
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
if(!play->m_synth.m_chips.empty())
return play->m_synth.m_chips[0]->emulatorName();
Synth &synth = *play->m_synth;
if(!synth.m_chips.empty())
return synth.m_chips[0]->emulatorName();
#else
return "Hardware OPL3 chip on 0x330";
#endif
@ -708,8 +763,9 @@ ADLMIDI_EXPORT int adl_setRunAtPcmRate(ADL_MIDIPlayer *device, int enabled)
{
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
Synth &synth = *play->m_synth;
play->m_setup.runAtPcmRate = (enabled != 0);
if(!play->m_synth.setupLocked())
if(!synth.setupLocked())
play->partialReset();
return 0;
}
@ -763,7 +819,7 @@ ADLMIDI_EXPORT double adl_totalTimeLength(struct ADL_MIDIPlayer *device)
return -1.0;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer.timeLength();
return play->m_sequencer->timeLength();
#else
ADL_UNUSED(device);
return -1.0;
@ -777,7 +833,7 @@ ADLMIDI_EXPORT double adl_loopStartTime(struct ADL_MIDIPlayer *device)
return -1.0;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer.getLoopStart();
return play->m_sequencer->getLoopStart();
#else
ADL_UNUSED(device);
return -1.0;
@ -791,7 +847,7 @@ ADLMIDI_EXPORT double adl_loopEndTime(struct ADL_MIDIPlayer *device)
return -1.0;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer.getLoopEnd();
return play->m_sequencer->getLoopEnd();
#else
ADL_UNUSED(device);
return -1.0;
@ -805,7 +861,7 @@ ADLMIDI_EXPORT double adl_positionTell(struct ADL_MIDIPlayer *device)
return -1.0;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer.tell();
return play->m_sequencer->tell();
#else
ADL_UNUSED(device);
return -1.0;
@ -822,7 +878,7 @@ ADLMIDI_EXPORT void adl_positionSeek(struct ADL_MIDIPlayer *device, double secon
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->realTime_panic();
play->m_setup.delay = play->m_sequencer.seek(seconds, play->m_setup.mindelay);
play->m_setup.delay = play->m_sequencer->seek(seconds, play->m_setup.mindelay);
play->m_setup.carry = 0.0;
#else
ADL_UNUSED(device);
@ -838,7 +894,7 @@ ADLMIDI_EXPORT void adl_positionRewind(struct ADL_MIDIPlayer *device)
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->realTime_panic();
play->m_sequencer.rewind();
play->m_sequencer->rewind();
#else
ADL_UNUSED(device);
#endif
@ -851,7 +907,7 @@ ADLMIDI_EXPORT void adl_setTempo(struct ADL_MIDIPlayer *device, double tempo)
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_sequencer.setTempo(tempo);
play->m_sequencer->setTempo(tempo);
#else
ADL_UNUSED(device);
ADL_UNUSED(tempo);
@ -877,7 +933,7 @@ ADLMIDI_EXPORT const char *adl_metaMusicTitle(struct ADL_MIDIPlayer *device)
return "";
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer.getMusicTitle().c_str();
return play->m_sequencer->getMusicTitle().c_str();
#else
ADL_UNUSED(device);
return "";
@ -892,7 +948,7 @@ ADLMIDI_EXPORT const char *adl_metaMusicCopyright(struct ADL_MIDIPlayer *device)
return "";
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer.getMusicCopyright().c_str();
return play->m_sequencer->getMusicCopyright().c_str();
#else
ADL_UNUSED(device);
return "";
@ -906,7 +962,7 @@ ADLMIDI_EXPORT size_t adl_metaTrackTitleCount(struct ADL_MIDIPlayer *device)
return 0;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer.getTrackTitles().size();
return play->m_sequencer->getTrackTitles().size();
#else
ADL_UNUSED(device);
return 0;
@ -920,7 +976,7 @@ ADLMIDI_EXPORT const char *adl_metaTrackTitle(struct ADL_MIDIPlayer *device, siz
return "";
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
const std::vector<std::string> &titles = play->m_sequencer.getTrackTitles();
const std::vector<std::string> &titles = play->m_sequencer->getTrackTitles();
if(index >= titles.size())
return "INVALID";
return titles[index].c_str();
@ -939,7 +995,7 @@ ADLMIDI_EXPORT size_t adl_metaMarkerCount(struct ADL_MIDIPlayer *device)
return 0;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer.getMarkers().size();
return play->m_sequencer->getMarkers().size();
#else
ADL_UNUSED(device);
return 0;
@ -962,7 +1018,7 @@ ADLMIDI_EXPORT Adl_MarkerEntry adl_metaMarker(struct ADL_MIDIPlayer *device, siz
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
const std::vector<MidiSequencer::MIDI_MarkerEntry> &markers = play->m_sequencer.getMarkers();
const std::vector<MidiSequencer::MIDI_MarkerEntry> &markers = play->m_sequencer->getMarkers();
if(index >= markers.size())
{
marker.label = "INVALID";
@ -992,8 +1048,8 @@ ADLMIDI_EXPORT void adl_setRawEventHook(struct ADL_MIDIPlayer *device, ADL_RawEv
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_sequencerInterface.onEvent = rawEventHook;
play->m_sequencerInterface.onEvent_userData = userData;
play->m_sequencerInterface->onEvent = rawEventHook;
play->m_sequencerInterface->onEvent_userData = userData;
#else
ADL_UNUSED(device);
ADL_UNUSED(rawEventHook);
@ -1022,8 +1078,8 @@ ADLMIDI_EXPORT void adl_setDebugMessageHook(struct ADL_MIDIPlayer *device, ADL_D
play->hooks.onDebugMessage = debugMessageHook;
play->hooks.onDebugMessage_userData = userData;
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
play->m_sequencerInterface.onDebugMessage = debugMessageHook;
play->m_sequencerInterface.onDebugMessage_userData = userData;
play->m_sequencerInterface->onDebugMessage = debugMessageHook;
play->m_sequencerInterface->onDebugMessage_userData = userData;
#endif
}
@ -1273,7 +1329,7 @@ ADLMIDI_EXPORT int adl_playFormat(ADL_MIDIPlayer *device, int sampleCount,
// setup.SkipForward -= 1;
//else
{
if((player->m_sequencer.positionAtEnd()) && (setup.delay <= 0.0))
if((player->m_sequencer->positionAtEnd()) && (setup.delay <= 0.0))
break;//Stop to fetch samples at reaching the song end with disabled loop
ssize_t leftSamples = left / 2;
@ -1290,16 +1346,17 @@ ADLMIDI_EXPORT int adl_playFormat(ADL_MIDIPlayer *device, int sampleCount,
//fill buffer with zeros
int32_t *out_buf = player->m_outBuf;
std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(out_buf[0]));
unsigned int chips = player->m_synth.m_numChips;
Synth &synth = *player->m_synth;
unsigned int chips = synth.m_numChips;
if(chips == 1)
{
player->m_synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo);
synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo);
}
else if(n_periodCountStereo > 0)
{
/* Generate data from every chip and mix result */
for(size_t card = 0; card < chips; ++card)
player->m_synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo);
synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo);
}
/* Process it */
@ -1380,14 +1437,15 @@ ADLMIDI_EXPORT int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleC
//fill buffer with zeros
int32_t *out_buf = player->m_outBuf;
std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(out_buf[0]));
unsigned int chips = player->m_synth.m_numChips;
Synth &synth = *player->m_synth;
unsigned int chips = synth.m_numChips;
if(chips == 1)
player->m_synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo);
synth.m_chips[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)
player->m_synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo);
synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo);
}
/* Process it */
if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1)
@ -1428,7 +1486,7 @@ ADLMIDI_EXPORT int adl_atEnd(struct ADL_MIDIPlayer *device)
return 1;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return (int)play->m_sequencer.positionAtEnd();
return (int)play->m_sequencer->positionAtEnd();
#else
ADL_UNUSED(device);
return 1;
@ -1442,7 +1500,7 @@ ADLMIDI_EXPORT size_t adl_trackCount(struct ADL_MIDIPlayer *device)
return 0;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer.getTrackCount();
return play->m_sequencer->getTrackCount();
#else
ADL_UNUSED(device);
return 0;
@ -1456,7 +1514,7 @@ ADLMIDI_EXPORT int adl_setTrackOptions(struct ADL_MIDIPlayer *device, size_t tra
return -1;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
MidiSequencer &seq = play->m_sequencer;
MidiSequencer &seq = *play->m_sequencer;
unsigned enableFlag = trackOptions & 3;
trackOptions &= ~3u;
@ -1497,7 +1555,7 @@ ADLMIDI_EXPORT int adl_setTriggerHandler(struct ADL_MIDIPlayer *device, ADL_Trig
return -1;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
MidiSequencer &seq = play->m_sequencer;
MidiSequencer &seq = *play->m_sequencer;
seq.setTriggerHandler(handler, userData);
return 0;
#else

View file

@ -1,8 +1,8 @@
/*
* libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation
* libADLMIDI is a free Software MIDI synthesizer 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>
* ADLMIDI Library API: Copyright (c) 2015-2020 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
@ -29,7 +29,7 @@ extern "C" {
#endif
#define ADLMIDI_VERSION_MAJOR 1
#define ADLMIDI_VERSION_MINOR 4
#define ADLMIDI_VERSION_MINOR 5
#define ADLMIDI_VERSION_PATCHLEVEL 0
#define ADLMIDI_TOSTR_I(s) #s
@ -39,6 +39,7 @@ extern "C" {
ADLMIDI_TOSTR(ADLMIDI_VERSION_MINOR) "." \
ADLMIDI_TOSTR(ADLMIDI_VERSION_PATCHLEVEL)
#define ADL_CHIP_SAMPLE_RATE 49716
#include <stddef.h>
@ -111,8 +112,20 @@ enum ADLMIDI_VolumeModels
ADLMIDI_VolumeModel_DMX = 3,
/*! Logarithmic volume scale, used in Apogee Sound System. */
ADLMIDI_VolumeModel_APOGEE = 4,
/*! Aproximated and shorted volume map table. Similar to general, but has less granularity. */
ADLMIDI_VolumeModel_9X = 5
/*! Aproximated and shorted volume map table (SB16 driver). Similar to general, but has less granularity. */
ADLMIDI_VolumeModel_9X = 5,
/*! DMX model with a fixed bug of AM voices */
ADLMIDI_VolumeModel_DMX_Fixed = 6,
/*! Apogee model with a fixed bug of AM voices*/
ADLMIDI_VolumeModel_APOGEE_Fixed = 7,
/*! Audio Interface Library volume scaling model */
ADLMIDI_VolumeModel_AIL = 8,
/*! Aproximated and shorted volume map table (Generic FM driver). Similar to general, but has less granularity. */
ADLMIDI_VolumeModel_9X_GENERIC_FM = 9,
/*! HMI Sound Operating System volume scaling model */
ADLMIDI_VolumeModel_HMI = 10,
/*! HMI Sound Operating System volume scaling model, older variant with bugs */
ADLMIDI_VolumeModel_HMI_OLD = 11
};
/**
@ -246,7 +259,125 @@ enum ADL_BankAccessFlags
ADLMIDI_Bank_CreateRt = 1|2
};
typedef struct ADL_Instrument ADL_Instrument;
/* ======== Instrument structures ======== */
/**
* @brief Version of the instrument data format
*/
enum
{
ADLMIDI_InstrumentVersion = 0
};
/**
* @brief Instrument flags
*/
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,
/*! RythmMode flags mask */
ADLMIDI_Ins_RhythmModeMask = 0x38,
/*! Mask of the flags range */
ADLMIDI_Ins_ALL_MASK = 0x07
} ADL_InstrumentFlags;
/**
* @brief Rhythm-mode drum type
*/
typedef enum ADL_RhythmMode
{
/*! RythmMode: BassDrum */
ADLMIDI_RM_BassDrum = 0x08,
/*! RythmMode: Snare */
ADLMIDI_RM_Snare = 0x10,
/*! RythmMode: TomTom */
ADLMIDI_RM_TomTom = 0x18,
/*! RythmMode: Cymbal */
ADLMIDI_RM_Cymbal = 0x20,
/*! RythmMode: HiHat */
ADLMIDI_RM_HiHat = 0x28
} ADL_RhythmMode;
/**
* @brief Operator structure, part of Instrument structure
*/
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;
/**
* @brief Instrument structure
*/
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;
/**
* @var inst_flags
* @brief Instrument flags
*
* Enums: #ADL_InstrumentFlags and #ADL_RhythmMode
*
* Bitwise flags bit map:
* ```
* [0EEEDCBA]
* A) 0x00 - 2-operator mode
* B) 0x01 - 4-operator mode
* C) 0x02 - pseudo-4-operator (two 2-operator voices) mode
* D) 0x04 - is 'blank' instrument (instrument which has no sound)
* E) 0x38 - Reserved for rhythm-mode percussion type number (three bits number)
* -> 0x00 - Melodic or Generic drum (rhythm-mode is disabled)
* -> 0x08 - is Bass drum
* -> 0x10 - is Snare
* -> 0x18 - is Tom-tom
* -> 0x20 - is Cymbal
* -> 0x28 - is Hi-hat
* 0) Reserved / Unused
* ```
*/
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;
@ -361,11 +492,14 @@ extern ADLMIDI_DECLSPEC int adl_getNumFourOpsChnObtained(struct ADL_MIDIPlayer *
/**
* @brief Override Enable(1) or Disable(0) AdLib percussion mode. -1 - use bank default AdLib percussion mode
*
* [DEPRECATED] This function is no more useful and now it makes nothing. Kept for ABI compatibility. Rhythm mode can be set by the bank data only.
*
* This function forces rhythm-mode on any bank. The result will work glitchy.
*
* @param device Instance of the library
* @param percmod 0 - disabled, 1 - enabled
*/
ADLMIDI_DEPRECATED("This function is no more useful and now it makes nothing. Kept for ABI compatibility. Rhythm mode can be set by the bank data only.")
extern ADLMIDI_DECLSPEC void adl_setPercMode(struct ADL_MIDIPlayer *device, int percmod);
/**
@ -502,6 +636,10 @@ enum ADL_Emulator
ADLMIDI_EMU_NUKED_174,
/*! DosBox */
ADLMIDI_EMU_DOSBOX,
/*! Opal */
ADLMIDI_EMU_OPAL,
/*! Java */
ADLMIDI_EMU_JAVA,
/*! Count instrument on the level */
ADLMIDI_EMU_end
};
@ -588,6 +726,7 @@ extern ADLMIDI_DECLSPEC const char *adl_errorInfo(struct ADL_MIDIPlayer *device)
* Tip 1: You can initialize multiple instances and run them in parallel
* Tip 2: Library is NOT thread-safe, therefore don't use same instance in different threads or use mutexes
* Tip 3: Changing of sample rate on the fly is not supported. Re-create the instance again.
* Top 4: To generate output in OPL chip native sample rate, please initialize it with sample rate value as `ADL_CHIP_SAMPLE_RATE`
*
* @param sample_rate Output sample rate
* @return Instance of the library. If NULL was returned, check the `adl_errorString` message for more info.
@ -1117,128 +1256,6 @@ extern ADLMIDI_DECLSPEC void adl_setDebugMessageHook(struct ADL_MIDIPlayer *devi
*/
extern ADLMIDI_DECLSPEC int adl_describeChannels(struct ADL_MIDIPlayer *device, char *text, char *attr, size_t size);
/* ======== Instrument structures ======== */
/**
* @brief Version of the instrument data format
*/
enum
{
ADLMIDI_InstrumentVersion = 0
};
/**
* @brief Instrument flags
*/
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,
/*! RythmMode flags mask */
ADLMIDI_Ins_RhythmModeMask = 0x38,
/*! Mask of the flags range */
ADLMIDI_Ins_ALL_MASK = 0x07
} ADL_InstrumentFlags;
/**
* @brief Rhythm-mode drum type
*/
typedef enum ADL_RhythmMode
{
/*! RythmMode: BassDrum */
ADLMIDI_RM_BassDrum = 0x08,
/*! RythmMode: Snare */
ADLMIDI_RM_Snare = 0x10,
/*! RythmMode: TomTom */
ADLMIDI_RM_TomTom = 0x18,
/*! RythmMode: Cymbal */
ADLMIDI_RM_Cymbal = 0x20,
/*! RythmMode: HiHat */
ADLMIDI_RM_HiHat = 0x28
} ADL_RhythmMode;
/**
* @brief Operator structure, part of Instrument structure
*/
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;
/**
* @brief Instrument structure
*/
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;
/**
* @var inst_flags
* @brief Instrument flags
*
* Enums: #ADL_InstrumentFlags and #ADL_RhythmMode
*
* Bitwise flags bit map:
* ```
* [0EEEDCBA]
* A) 0x00 - 2-operator mode
* B) 0x01 - 4-operator mode
* C) 0x02 - pseudo-4-operator (two 2-operator voices) mode
* D) 0x04 - is 'blank' instrument (instrument which has no sound)
* E) 0x38 - Reserved for rhythm-mode percussion type number (three bits number)
* -> 0x00 - Melodic or Generic drum (rhythm-mode is disabled)
* -> 0x08 - is Bass drum
* -> 0x10 - is Snare
* -> 0x18 - is Tom-tom
* -> 0x20 - is Cymbal
* -> 0x28 - is Hi-hat
* 0) Reserved / Unused
* ```
*/
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

@ -1,54 +0,0 @@
/*
* 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_HPP
#define ADLMIDI_HPP
#include "adlmidi.h"
struct ADL_MIDIPlayer;
class ADLMIDI_DECLSPEC AdlInstrumentTester
{
struct Impl;
Impl *P;
public:
explicit AdlInstrumentTester(ADL_MIDIPlayer *device);
virtual ~AdlInstrumentTester();
// Find list of adlib instruments that supposedly implement this GM
void FindAdlList();
void Touch(unsigned c, unsigned volume);
void DoNote(int note);
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

@ -1,8 +1,8 @@
/*
* libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation
* libADLMIDI is a free Software MIDI synthesizer 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>
* ADLMIDI Library API: Copyright (c) 2015-2020 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

View file

@ -2,7 +2,7 @@
* 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>
* ADLMIDI Library API: Copyright (c) 2015-2020 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

View file

@ -1,8 +1,8 @@
/*
* libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation
* libADLMIDI is a free Software MIDI synthesizer 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>
* ADLMIDI Library API: Copyright (c) 2015-2020 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
@ -21,89 +21,80 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "adldata.hh"
#include "oplinst.h"
#include "wopl/wopl_file.h"
#include <cmath>
template <class WOPLI>
static void cvt_generic_to_FMIns(adlinsdata2 &ins, const WOPLI &in)
static void cvt_generic_to_FMIns(OplInstMeta &ins, const WOPLI &in)
{
ins.voice2_fine_tune = 0.0;
int8_t voice2_fine_tune = in.second_voice_detune;
int voice2_fine_tune = in.second_voice_detune;
if(voice2_fine_tune != 0)
{
if(voice2_fine_tune == 1)
ins.voice2_fine_tune = 0.000025;
else if(voice2_fine_tune == -1)
ins.voice2_fine_tune = -0.000025;
else
ins.voice2_fine_tune = voice2_fine_tune * (15.625 / 1000.0);
// Simulate behavior of DMX second voice detune
ins.voice2_fine_tune = (double)(((voice2_fine_tune + 128) >> 1) - 64) / 32.0;
}
ins.midi_velocity_offset = in.midi_velocity_offset;
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;
ins.midiVelocityOffset = in.midi_velocity_offset;
ins.drumTone = in.percussion_key_number;
ins.flags = (in.inst_flags & WOPL_Ins_4op) && (in.inst_flags & WOPL_Ins_Pseudo4op) ? OplInstMeta::Flag_Pseudo4op : 0;
ins.flags|= (in.inst_flags & WOPL_Ins_4op) && ((in.inst_flags & WOPL_Ins_Pseudo4op) == 0) ? OplInstMeta::Flag_Real4op : 0;
ins.flags|= (in.inst_flags & WOPL_Ins_IsBlank) ? OplInstMeta::Flag_NoSound : 0;
ins.flags|= in.inst_flags & WOPL_RhythmModeMask;
for(size_t op = 0, slt = 0; op < 4; op++, slt++)
{
ins.adl[slt].carrier_E862 =
ins.op[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
ins.op[slt].carrier_40 = in.operators[op].ksl_l_40;//KSLL
op++;
ins.adl[slt].modulator_E862 =
ins.op[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
ins.op[slt].modulator_40 = in.operators[op].ksl_l_40;//KSLL
}
ins.adl[0].finetune = static_cast<int8_t>(in.note_offset1);
ins.adl[0].feedconn = in.fb_conn1_C0;
ins.adl[1].finetune = static_cast<int8_t>(in.note_offset2);
ins.adl[1].feedconn = in.fb_conn2_C0;
ins.op[0].noteOffset = static_cast<int8_t>(in.note_offset1);
ins.op[0].feedconn = in.fb_conn1_C0;
ins.op[1].noteOffset = static_cast<int8_t>(in.note_offset2);
ins.op[1].feedconn = in.fb_conn2_C0;
ins.ms_sound_kon = in.delay_on_ms;
ins.ms_sound_koff = in.delay_off_ms;
ins.soundKeyOnMs = in.delay_on_ms;
ins.soundKeyOffMs = in.delay_off_ms;
}
template <class WOPLI>
static void cvt_FMIns_to_generic(WOPLI &ins, const adlinsdata2 &in)
static void cvt_FMIns_to_generic(WOPLI &ins, const OplInstMeta &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 = static_cast<long>(round(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);
}
int m = (int)(voice2_fine_tune * 32.0);
m += 64;
m <<= 1;
m -= 128;
ins.second_voice_detune = (uint8_t)m;
}
ins.midi_velocity_offset = in.midi_velocity_offset;
ins.percussion_key_number = in.tone;
ins.inst_flags = (in.flags & (adlinsdata::Flag_Pseudo4op|adlinsdata::Flag_Real4op)) ? 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;
ins.inst_flags |= in.flags & adlinsdata::Mask_RhythmMode;
ins.midi_velocity_offset = in.midiVelocityOffset;
ins.percussion_key_number = in.drumTone;
ins.inst_flags = (in.flags & (OplInstMeta::Flag_Pseudo4op|OplInstMeta::Flag_Real4op)) ? WOPL_Ins_4op : 0;
ins.inst_flags|= (in.flags & OplInstMeta::Flag_Pseudo4op) ? WOPL_Ins_Pseudo4op : 0;
ins.inst_flags|= (in.flags & OplInstMeta::Flag_NoSound) ? WOPL_Ins_IsBlank : 0;
ins.inst_flags |= in.flags & OplInstMeta::Mask_RhythmMode;
for(size_t op = 0; op < 4; op++)
{
const adldata &in2op = in.adl[(op < 2) ? 0 : 1];
const OplTimbre &in2op = in.op[(op < 2) ? 0 : 1];
uint32_t regE862 = ((op & 1) == 0) ? in2op.carrier_E862 : in2op.modulator_E862;
uint8_t reg40 = ((op & 1) == 0) ? in2op.carrier_40 : in2op.modulator_40;
@ -114,11 +105,11 @@ static void cvt_FMIns_to_generic(WOPLI &ins, const adlinsdata2 &in)
ins.operators[op].ksl_l_40 = reg40;
}
ins.note_offset1 = in.adl[0].finetune;
ins.fb_conn1_C0 = in.adl[0].feedconn;
ins.note_offset2 = in.adl[1].finetune;
ins.fb_conn2_C0 = in.adl[1].feedconn;
ins.note_offset1 = in.op[0].noteOffset;
ins.fb_conn1_C0 = in.op[0].feedconn;
ins.note_offset2 = in.op[1].noteOffset;
ins.fb_conn2_C0 = in.op[1].feedconn;
ins.delay_on_ms = in.ms_sound_kon;
ins.delay_off_ms = in.ms_sound_koff;
ins.delay_on_ms = in.soundKeyOnMs;
ins.delay_off_ms = in.soundKeyOffMs;
}

98
thirdparty/adlmidi/adlmidi_db.h vendored Normal file
View file

@ -0,0 +1,98 @@
/*
* libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation
*
* Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* ADLMIDI Library API: Copyright (c) 2015-2020 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 ADLDATA_DB_H
#define ADLDATA_DB_H
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <vector>
#ifndef _MSC_VER
#define ATTRIB_PACKED __attribute__((__packed__))
#else
#define ATTRIB_PACKED
#endif
typedef uint16_t bank_count_t;
typedef int16_t midi_bank_idx_t;
#ifndef DISABLE_EMBEDDED_BANKS
extern const size_t g_embeddedBanksCount;
#endif
namespace BanksDump
{
struct BankEntry
{
uint16_t bankSetup;
bank_count_t banksMelodicCount;
bank_count_t banksPercussionCount;
const char *title;
bank_count_t banksOffsetMelodic;
bank_count_t banksOffsetPercussive;
} ATTRIB_PACKED;
struct MidiBank
{
uint8_t msb;
uint8_t lsb;
midi_bank_idx_t insts[128];
} ATTRIB_PACKED;
struct InstrumentEntry
{
int16_t noteOffset1;
int16_t noteOffset2;
int8_t midiVelocityOffset;
uint8_t percussionKeyNumber;
uint8_t instFlags;
int8_t secondVoiceDetune;
uint16_t fbConn;
uint16_t delay_on_ms;
uint16_t delay_off_ms;
int16_t ops[4];
} ATTRIB_PACKED;
struct Operator
{
uint32_t d_E862;
uint8_t d_40;
} ATTRIB_PACKED;
} /* namespace BanksDump */
#ifndef DISABLE_EMBEDDED_BANKS
extern const char* const g_embeddedBankNames[];
extern const BanksDump::BankEntry g_embeddedBanks[];
extern const size_t g_embeddedBanksMidiIndex[];
extern const BanksDump::MidiBank g_embeddedBanksMidi[];
extern const BanksDump::InstrumentEntry g_embeddedBanksInstruments[];
extern const BanksDump::Operator g_embeddedBanksOperators[];
#endif
#endif // ADLDATA_DB_H

View file

@ -1,8 +1,8 @@
/*
* libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation
/*
* libADLMIDI is a free Software MIDI synthesizer 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>
* ADLMIDI Library API: Copyright (c) 2015-2020 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
@ -21,8 +21,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "adlmidi_midiplay.hpp"
#include "adlmidi_opl3.hpp"
#include "adlmidi_private.hpp"
#include "adlmidi_cvt.hpp"
#include "file_reader.hpp"
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
#include "midi_sequencer.hpp"
#endif
#include "wopl/wopl_file.h"
bool MIDIplay::LoadBank(const std::string &filename)
@ -39,12 +45,12 @@ bool MIDIplay::LoadBank(const void *data, size_t size)
return LoadBank(file);
}
void cvt_ADLI_to_FMIns(adlinsdata2 &ins, const ADL_Instrument &in)
void cvt_ADLI_to_FMIns(OplInstMeta &ins, const ADL_Instrument &in)
{
return cvt_generic_to_FMIns(ins, in);
}
void cvt_FMIns_to_ADLI(ADL_Instrument &ins, const adlinsdata2 &in)
void cvt_FMIns_to_ADLI(ADL_Instrument &ins, const OplInstMeta &in)
{
cvt_FMIns_to_generic(ins, in);
}
@ -104,17 +110,18 @@ bool MIDIplay::LoadBank(FileAndMemReader &fr)
}
}
m_synth.m_insBankSetup.adLibPercussions = false;
m_synth.m_insBankSetup.scaleModulators = false;
m_synth.m_insBankSetup.deepTremolo = (wopl->opl_flags & WOPL_FLAG_DEEP_TREMOLO) != 0;
m_synth.m_insBankSetup.deepVibrato = (wopl->opl_flags & WOPL_FLAG_DEEP_VIBRATO) != 0;
m_synth.m_insBankSetup.volumeModel = wopl->volume_model;
Synth &synth = *m_synth;
synth.setEmbeddedBank(m_setup.bankId);
synth.m_insBankSetup.scaleModulators = false;
synth.m_insBankSetup.deepTremolo = (wopl->opl_flags & WOPL_FLAG_DEEP_TREMOLO) != 0;
synth.m_insBankSetup.deepVibrato = (wopl->opl_flags & WOPL_FLAG_DEEP_VIBRATO) != 0;
synth.m_insBankSetup.volumeModel = wopl->volume_model;
m_setup.deepTremoloMode = -1;
m_setup.deepVibratoMode = -1;
m_setup.volumeScaleModel = ADLMIDI_VolumeModel_AUTO;
m_synth.setEmbeddedBank(m_setup.bankId);
uint16_t slots_counts[2] = {wopl->banks_count_melodic, wopl->banks_count_percussion};
WOPLBank *slots_src_ins[2] = { wopl->banks_melodic, wopl->banks_percussive };
@ -124,19 +131,19 @@ bool MIDIplay::LoadBank(FileAndMemReader &fr)
{
size_t bankno = (slots_src_ins[ss][i].bank_midi_msb * 256) +
(slots_src_ins[ss][i].bank_midi_lsb) +
(ss ? size_t(OPL3::PercussionTag) : 0);
OPL3::Bank &bank = m_synth.m_insBanks[bankno];
(ss ? size_t(Synth::PercussionTag) : 0);
Synth::Bank &bank = synth.m_insBanks[bankno];
for(int j = 0; j < 128; j++)
{
adlinsdata2 &ins = bank.ins[j];
std::memset(&ins, 0, sizeof(adlinsdata2));
OplInstMeta &ins = bank.ins[j];
std::memset(&ins, 0, sizeof(OplInstMeta));
WOPLInstrument &inIns = slots_src_ins[ss][i].ins[j];
cvt_generic_to_FMIns(ins, inIns);
}
}
}
m_synth.m_embeddedBank = OPL3::CustomBankTag; // Use dynamic banks!
synth.m_embeddedBank = Synth::CustomBankTag; // Use dynamic banks!
//Percussion offset is count of instruments multipled to count of melodic banks
applySetup();
@ -150,7 +157,8 @@ bool MIDIplay::LoadBank(FileAndMemReader &fr)
bool MIDIplay::LoadMIDI_pre()
{
#ifdef DISABLE_EMBEDDED_BANKS
if((m_synth.m_embeddedBank != OPL3::CustomBankTag) || m_synth.m_insBanks.empty())
Synth &synth = *m_synth;
if((synth.m_embeddedBank != Synth::CustomBankTag) || synth.m_insBanks.empty())
{
errorStringOut = "Bank is not set! Please load any instruments bank by using of adl_openBankFile() or adl_openBankData() functions!";
return false;
@ -165,89 +173,97 @@ bool MIDIplay::LoadMIDI_pre()
bool MIDIplay::LoadMIDI_post()
{
MidiSequencer::FileFormat format = m_sequencer.getFormat();
Synth &synth = *m_synth;
MidiSequencer &seq = *m_sequencer;
MidiSequencer::FileFormat format = seq.getFormat();
if(format == MidiSequencer::Format_CMF)
{
const std::vector<MidiSequencer::CmfInstrument> &instruments = m_sequencer.getRawCmfInstruments();
m_synth.m_insBanks.clear();//Clean up old banks
const std::vector<MidiSequencer::CmfInstrument> &instruments = seq.getRawCmfInstruments();
synth.m_insBanks.clear();//Clean up old banks
uint16_t ins_count = static_cast<uint16_t>(instruments.size());
for(uint16_t i = 0; i < ins_count; ++i)
{
const uint8_t *InsData = instruments[i].data;
const uint8_t *insData = instruments[i].data;
size_t bank = i / 256;
bank = ((bank & 127) + ((bank >> 7) << 8));
if(bank > 127 + (127 << 8))
break;
bank += (i % 256 < 128) ? 0 : size_t(OPL3::PercussionTag);
bank += (i % 256 < 128) ? 0 : size_t(Synth::PercussionTag);
/*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]);*/
adlinsdata2 &adlins = m_synth.m_insBanks[bank].ins[i % 128];
adldata adl;
OplInstMeta &adlins = synth.m_insBanks[bank].ins[i % 128];
OplTimbre adl;
adl.modulator_E862 =
((static_cast<uint32_t>(InsData[8] & 0x07) << 24) & 0xFF000000) //WaveForm
| ((static_cast<uint32_t>(InsData[6]) << 16) & 0x00FF0000) //Sustain/Release
| ((static_cast<uint32_t>(InsData[4]) << 8) & 0x0000FF00) //Attack/Decay
| ((static_cast<uint32_t>(InsData[0]) << 0) & 0x000000FF); //MultKEVA
((static_cast<uint32_t>(insData[8] & 0x07) << 24) & 0xFF000000) //WaveForm
| ((static_cast<uint32_t>(insData[6]) << 16) & 0x00FF0000) //Sustain/Release
| ((static_cast<uint32_t>(insData[4]) << 8) & 0x0000FF00) //Attack/Decay
| ((static_cast<uint32_t>(insData[0]) << 0) & 0x000000FF); //MultKEVA
adl.carrier_E862 =
((static_cast<uint32_t>(InsData[9] & 0x07) << 24) & 0xFF000000) //WaveForm
| ((static_cast<uint32_t>(InsData[7]) << 16) & 0x00FF0000) //Sustain/Release
| ((static_cast<uint32_t>(InsData[5]) << 8) & 0x0000FF00) //Attack/Decay
| ((static_cast<uint32_t>(InsData[1]) << 0) & 0x000000FF); //MultKEVA
adl.modulator_40 = InsData[2];
adl.carrier_40 = InsData[3];
adl.feedconn = InsData[10] & 0x0F;
adl.finetune = 0;
adlins.adl[0] = adl;
adlins.adl[1] = adl;
adlins.ms_sound_kon = 1000;
adlins.ms_sound_koff = 500;
adlins.tone = 0;
((static_cast<uint32_t>(insData[9] & 0x07) << 24) & 0xFF000000) //WaveForm
| ((static_cast<uint32_t>(insData[7]) << 16) & 0x00FF0000) //Sustain/Release
| ((static_cast<uint32_t>(insData[5]) << 8) & 0x0000FF00) //Attack/Decay
| ((static_cast<uint32_t>(insData[1]) << 0) & 0x000000FF); //MultKEVA
adl.modulator_40 = insData[2];
adl.carrier_40 = insData[3];
adl.feedconn = insData[10] & 0x0F;
adl.noteOffset = 0;
adlins.op[0] = adl;
adlins.op[1] = adl;
adlins.soundKeyOnMs = 1000;
adlins.soundKeyOffMs = 500;
adlins.drumTone = 0;
adlins.flags = 0;
adlins.voice2_fine_tune = 0.0;
}
m_synth.m_embeddedBank = OPL3::CustomBankTag; // Ignore AdlBank number, use dynamic banks instead
synth.m_embeddedBank = Synth::CustomBankTag; // Ignore AdlBank number, use dynamic banks instead
//std::printf("CMF deltas %u ticks %u, basictempo = %u\n", deltas, ticks, basictempo);
m_synth.m_rhythmMode = true;
m_synth.m_musicMode = OPL3::MODE_CMF;
m_synth.m_volumeScale = OPL3::VOLUME_NATIVE;
synth.m_rhythmMode = true;
synth.m_musicMode = Synth::MODE_CMF;
synth.m_volumeScale = Synth::VOLUME_NATIVE;
m_synth.m_numChips = 1;
m_synth.m_numFourOps = 0;
synth.m_numChips = 1;
synth.m_numFourOps = 0;
}
else if(format == MidiSequencer::Format_RSXX)
{
//opl.CartoonersVolumes = true;
m_synth.m_musicMode = OPL3::MODE_RSXX;
m_synth.m_volumeScale = OPL3::VOLUME_NATIVE;
synth.m_musicMode = Synth::MODE_RSXX;
synth.m_volumeScale = Synth::VOLUME_NATIVE;
m_synth.m_numChips = 1;
m_synth.m_numFourOps = 0;
synth.m_numChips = 1;
synth.m_numFourOps = 0;
}
else if(format == MidiSequencer::Format_IMF)
{
//std::fprintf(stderr, "Done reading IMF file\n");
m_synth.m_numFourOps = 0; //Don't use 4-operator channels for IMF playing!
m_synth.m_musicMode = OPL3::MODE_IMF;
synth.m_numFourOps = 0; //Don't use 4-operator channels for IMF playing!
synth.m_rhythmMode = false;//Don't enforce rhythm-mode when it's unneeded
synth.m_musicMode = Synth::MODE_IMF;
m_synth.m_numChips = 1;
m_synth.m_numFourOps = 0;
synth.m_numChips = 1;
synth.m_numFourOps = 0;
}
else
{
m_synth.m_numChips = m_setup.numChips;
if(format == MidiSequencer::Format_XMIDI)
synth.m_musicMode = Synth::MODE_XMIDI;
synth.m_numChips = m_setup.numChips;
if(m_setup.numFourOps < 0)
adlCalculateFourOpChannels(this, true);
}
resetMIDIDefaults();
m_setup.tick_skip_samples_delay = 0;
m_synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPL3 chip
synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPL3 chip
//opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously)
m_chipChannels.clear();
m_chipChannels.resize(m_synth.m_numChannels);
m_chipChannels.resize(synth.m_numChannels);
return true;
}
@ -258,9 +274,10 @@ bool MIDIplay::LoadMIDI(const std::string &filename)
file.openFile(filename.c_str());
if(!LoadMIDI_pre())
return false;
if(!m_sequencer.loadMIDI(file))
MidiSequencer &seq = *m_sequencer;
if(!seq.loadMIDI(file))
{
errorStringOut = m_sequencer.getErrorString();
errorStringOut = seq.getErrorString();
return false;
}
if(!LoadMIDI_post())
@ -274,9 +291,10 @@ bool MIDIplay::LoadMIDI(const void *data, size_t size)
file.openData(data, size);
if(!LoadMIDI_pre())
return false;
if(!m_sequencer.loadMIDI(file))
MidiSequencer &seq = *m_sequencer;
if(!seq.loadMIDI(file))
{
errorStringOut = m_sequencer.getErrorString();
errorStringOut = seq.getErrorString();
return false;
}
if(!LoadMIDI_post())

File diff suppressed because it is too large Load diff

1025
thirdparty/adlmidi/adlmidi_midiplay.hpp vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

356
thirdparty/adlmidi/adlmidi_opl3.hpp vendored Normal file
View file

@ -0,0 +1,356 @@
/*
* libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation
*
* Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* ADLMIDI Library API: Copyright (c) 2015-2020 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_OPL3_HPP
#define ADLMIDI_OPL3_HPP
#include "oplinst.h"
#include "adlmidi_ptr.hpp"
#include "adlmidi_private.hpp"
#include "adlmidi_bankmap.h"
#define BEND_COEFFICIENT 172.4387
#define OPL3_CHANNELS_MELODIC_BASE 0
#define OPL3_CHANNELS_RHYTHM_BASE 18
#define NUM_OF_CHANNELS 23
#define NUM_OF_4OP_CHANNELS 6
#define NUM_OF_2OP_CHANNELS 18
#define NUM_OF_2x2_CHANNELS 9
#define NUM_OF_RM_CHANNELS 5
/**
* @brief OPL3 Chip management class
*/
class OPL3
{
friend class MIDIplay;
friend class AdlInstrumentTester;
friend int adlCalculateFourOpChannels(MIDIplay *play, bool silent);
public:
enum
{
PercussionTag = 1 << 15,
CustomBankTag = 0xFFFFFFFF
};
//! Total number of chip channels between all running emulators
uint32_t m_numChannels;
//! Just a padding. Reserved.
char _padding[4];
#ifndef ADLMIDI_HW_OPL
//! Running chip emulators
std::vector<AdlMIDI_SPtr<OPLChipBase > > m_chips;
#endif
private:
//! Cached patch data, needed by Touch()
std::vector<OplTimbre> m_insCache;
//! Value written to B0, cached, needed by NoteOff.
/*! Contains Key on/off state, octave block and frequency number values
*/
std::vector<uint32_t> m_keyBlockFNumCache;
//! Cached BD registry value (flags register: DeepTremolo, DeepVibrato, and RhythmMode)
std::vector<uint32_t> m_regBD;
public:
/**
* @brief MIDI bank entry
*/
struct Bank
{
//! MIDI Bank instruments
OplInstMeta ins[128];
};
typedef BasicBankMap<Bank> BankMap;
//! MIDI bank instruments data
BankMap m_insBanks;
//! MIDI bank-wide setup
OplBankSetup m_insBankSetup;
public:
//! Blank instrument template
static const OplInstMeta m_emptyInstrument;
//! Total number of running concurrent emulated chips
uint32_t m_numChips;
//! Currently running embedded bank number. "CustomBankTag" means usign of the custom bank.
uint32_t m_embeddedBank;
//! Total number of needed four-operator channels in all running chips
uint32_t m_numFourOps;
//! Turn global Deep Tremolo mode on
bool m_deepTremoloMode;
//! Turn global Deep Vibrato mode on
bool m_deepVibratoMode;
//! Use Rhythm Mode percussions
bool m_rhythmMode;
//! Carriers-only are scaled by default by volume level. This flag will tell to scale modulators too.
bool m_scaleModulators;
//! Run emulator at PCM rate if that possible. Reduces sounding accuracy, but decreases CPU usage on lower rates.
bool m_runAtPcmRate;
//! Enable soft panning
bool m_softPanning;
//! Master volume, controlled via SysEx (0...127)
uint8_t m_masterVolume;
//! Just a padding. Reserved.
char _padding2[3];
/**
* @brief Music playing mode
*/
enum MusicMode
{
//! MIDI mode
MODE_MIDI,
//! AIL XMIDI mode
MODE_XMIDI,
//! Id-Software Music mode
MODE_IMF,
//! Creative Music Files mode
MODE_CMF,
//! EA-MUS (a.k.a. RSXX) mode
MODE_RSXX
} m_musicMode;
/**
* @brief Volume models enum
*/
enum VolumesScale
{
//! Generic volume model (linearization of logarithmic scale)
VOLUME_Generic,
//! OPL3 native logarithmic scale
VOLUME_NATIVE,
//! DMX volume scale logarithmic table
VOLUME_DMX,
//! Apoge Sound System volume scaling model
VOLUME_APOGEE,
//! Windows 9x SB16 driver volume scale table
VOLUME_9X,
//! DMX model with a fixed bug of AM voices
VOLUME_DMX_FIXED,
//! Apogee model with a fixed bug of AM voices
VOLUME_APOGEE_FIXED,
//! Audio Interfaces Library volume scaling model
VOLUME_AIL,
//! Windows 9x Generic FM driver volume scale table
VOLUME_9X_GENERIC_FM,
//! HMI Sound Operating System volume scale table
VOLUME_HMI,
//! HMI Sound Operating System volume scale model, older variant
VOLUME_HMI_OLD
} m_volumeScale;
//! Reserved
char _padding3[8];
/**
* @brief Channel categiry enumeration
*/
enum ChanCat
{
//! Regular melodic/percussion channel
ChanCat_Regular = 0,
//! Four-op first part
ChanCat_4op_First = 1,
//! Four-op second part
ChanCat_4op_Second = 2,
//! Rhythm-mode Bass drum
ChanCat_Rhythm_Bass = 3,
//! Rhythm-mode Snare drum
ChanCat_Rhythm_Snare = 4,
//! Rhythm-mode Tom-Tom
ChanCat_Rhythm_Tom = 5,
//! Rhythm-mode Cymbal
ChanCat_Rhythm_Cymbal = 6,
//! Rhythm-mode Hi-Hat
ChanCat_Rhythm_HiHat = 7,
//! Rhythm-mode Secondary channel
ChanCat_Rhythm_Secondary = 8
};
//! Category of the channel
/*! 1 = quad-first, 2 = quad-second, 0 = regular
3 = percussion BassDrum
4 = percussion Snare
5 = percussion Tom
6 = percussion Crash cymbal
7 = percussion Hihat
8 = percussion Secondary
*/
std::vector<uint32_t> m_channelCategory;
/**
* @brief C.O. Constructor
*/
OPL3();
/**
* @brief C.O. Destructor
*/
~OPL3();
/**
* @brief Checks are setup locked to be changed on the fly or not
* @return true when setup on the fly is locked
*/
bool setupLocked();
/**
* @brief Choose one of embedded banks
* @param bank ID of the bank
*/
void setEmbeddedBank(uint32_t bank);
/**
* @brief Write data to OPL3 chip register
* @param chip Index of emulated chip. In hardware OPL3 builds, this parameter is ignored
* @param address Register address to write
* @param value Value to write
*/
void writeReg(size_t chip, uint16_t address, uint8_t value);
/**
* @brief Write data to OPL3 chip register
* @param chip Index of emulated chip. In hardware OPL3 builds, this parameter is ignored
* @param address Register address to write
* @param value Value to write
*/
void writeRegI(size_t chip, uint32_t address, uint32_t value);
/**
* @brief Write to soft panning control of OPL3 chip emulator
* @param chip Index of emulated chip.
* @param address Register of channel to write
* @param value Value to write
*/
void writePan(size_t chip, uint32_t address, uint32_t value);
/**
* @brief Off the note in specified chip channel
* @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
*/
void noteOff(size_t c);
/**
* @brief On the note in specified chip channel with specified frequency of the tone
* @param c1 Channel of chip [or master 4-op channel] (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
* @param c2 Second 4-op channel of chip, unused for 2op (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
* @param tone The tone to play (integer part - MIDI halftone, decimal part - relative bend offset)
*/
void noteOn(size_t c1, size_t c2, double tone);
/**
* @brief Change setup of instrument in specified chip channel
* @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
* @param velocity Note velocity (from 0 to 127)
* @param channelVolume Channel volume level (from 0 to 127)
* @param channelExpression Channel expression level (from 0 to 127)
* @param brightness CC74 Brightness level (from 0 to 127)
* @param isDrum Is this a drum note? This flag is needed for some volume model algorithms
*/
void touchNote(size_t c,
uint_fast32_t velocity,
uint_fast32_t channelVolume = 127,
uint_fast32_t channelExpression = 127,
uint_fast32_t brightness = 127,
bool isDrum = false);
/**
* @brief Set the instrument into specified chip channel
* @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
* @param instrument Instrument data to set into the chip channel
*/
void setPatch(size_t c, const OplTimbre &instrument);
/**
* @brief Set panpot position
* @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
* @param value 3-bit panpot value
*/
void setPan(size_t c, uint8_t value);
/**
* @brief Shut up all chip channels
*/
void silenceAll();
/**
* @brief Commit updated flag states to chip registers
*/
void updateChannelCategories();
/**
* @brief commit deepTremolo and deepVibrato flags
*/
void commitDeepFlags();
/**
* @brief Set the volume scaling model
* @param volumeModel Type of volume scale model scale
*/
void setVolumeScaleModel(ADLMIDI_VolumeModels volumeModel);
/**
* @brief Get the volume scaling model
*/
ADLMIDI_VolumeModels getVolumeScaleModel();
#ifndef ADLMIDI_HW_OPL
/**
* @brief Clean up all running emulated chip instances
*/
void clearChips();
#endif
/**
* @brief Reset chip properties and initialize them
* @param emulator Type of chip emulator
* @param PCM_RATE Output sample rate to generate on output
* @param audioTickHandler PCM-accurate clock hook
*/
void reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler);
};
/**
* @brief Check emulator availability
* @param emulator Emulator ID (ADL_Emulator)
* @return true when emulator is available
*/
extern bool adl_isEmulatorAvailable(int emulator);
/**
* @brief Find highest emulator
* @return The ADL_Emulator enum value which contains ID of highest emulator
*/
extern int adl_getHighestEmulator();
/**
* @brief Find lowest emulator
* @return The ADL_Emulator enum value which contains ID of lowest emulator
*/
extern int adl_getLowestEmulator();
#endif // ADLMIDI_OPL3_HPP

View file

@ -1,8 +1,8 @@
/*
* libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation
* libADLMIDI is a free Software MIDI synthesizer 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>
* ADLMIDI Library API: Copyright (c) 2015-2020 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
@ -21,7 +21,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "adlmidi_midiplay.hpp"
#include "adlmidi_opl3.hpp"
#include "adlmidi_private.hpp"
#include "wopl/wopl_file.h"
std::string ADLMIDI_ErrorString;
@ -36,49 +40,33 @@ void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate)
int adlCalculateFourOpChannels(MIDIplay *play, bool silent)
{
Synth &synth = *play->m_synth;
size_t n_fourop[2] = {0, 0}, n_total[2] = {0, 0};
bool rhythmModeNeeded = false;
size_t numFourOps = 0;
//Automatically calculate how much 4-operator channels is necessary
#ifndef DISABLE_EMBEDDED_BANKS
if(play->m_synth.m_embeddedBank == OPL3::CustomBankTag)
#endif
{
//For custom bank
OPL3::BankMap::iterator it = play->m_synth.m_insBanks.begin();
OPL3::BankMap::iterator end = play->m_synth.m_insBanks.end();
Synth::BankMap::iterator it = synth.m_insBanks.begin();
Synth::BankMap::iterator end = synth.m_insBanks.end();
for(; it != end; ++it)
{
size_t bank = it->first;
size_t div = (bank & OPL3::PercussionTag) ? 1 : 0;
size_t div = (bank & Synth::PercussionTag) ? 1 : 0;
for(size_t i = 0; i < 128; ++i)
{
adlinsdata2 &ins = it->second.ins[i];
if(ins.flags & adlinsdata::Flag_NoSound)
OplInstMeta &ins = it->second.ins[i];
if(ins.flags & OplInstMeta::Flag_NoSound)
continue;
if((ins.flags & adlinsdata::Flag_Real4op) != 0)
if((ins.flags & OplInstMeta::Flag_Real4op) != 0)
++n_fourop[div];
++n_total[div];
if(div && ((ins.flags & OplInstMeta::Mask_RhythmMode) != 0))
rhythmModeNeeded = true;
}
}
}
#ifndef DISABLE_EMBEDDED_BANKS
else
{
//For embedded bank
for(size_t a = 0; a < 256; ++a)
{
size_t insno = banks[play->m_setup.bankId][a];
if(insno == 198)
continue;
++n_total[a / 128];
adlinsdata2 ins = adlinsdata2::from_adldata(::adlins[insno]);
if((ins.flags & adlinsdata::Flag_Real4op) != 0)
++n_fourop[a / 128];
}
}
#endif
size_t numFourOps = 0;
// All 2ops (no 4ops)
if((n_fourop[0] == 0) && (n_fourop[1] == 0))
@ -93,16 +81,47 @@ int adlCalculateFourOpChannels(MIDIplay *play, bool silent)
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);
*/
synth.m_numFourOps = static_cast<unsigned>(numFourOps * synth.m_numChips);
play->m_synth.m_numFourOps = static_cast<unsigned>(numFourOps * play->m_synth.m_numChips);
// Update channel categories and set up four-operator channels
if(!silent)
play->m_synth.updateChannelCategories();
synth.updateChannelCategories();
// Set rhythm mode when it needed
synth.m_rhythmMode = rhythmModeNeeded;
return 0;
}
#ifndef DISABLE_EMBEDDED_BANKS
void adlFromInstrument(const BanksDump::InstrumentEntry &instIn, OplInstMeta &instOut)
{
instOut.voice2_fine_tune = 0.0;
if(instIn.secondVoiceDetune != 0)
instOut.voice2_fine_tune = (double)((((int)instIn.secondVoiceDetune + 128) >> 1) - 64) / 32.0;
instOut.midiVelocityOffset = instIn.midiVelocityOffset;
instOut.drumTone = instIn.percussionKeyNumber;
instOut.flags = (instIn.instFlags & WOPL_Ins_4op) && (instIn.instFlags & WOPL_Ins_Pseudo4op) ? OplInstMeta::Flag_Pseudo4op : 0;
instOut.flags|= (instIn.instFlags & WOPL_Ins_4op) && ((instIn.instFlags & WOPL_Ins_Pseudo4op) == 0) ? OplInstMeta::Flag_Real4op : 0;
instOut.flags|= (instIn.instFlags & WOPL_Ins_IsBlank) ? OplInstMeta::Flag_NoSound : 0;
instOut.flags|= instIn.instFlags & WOPL_RhythmModeMask;
for(size_t op = 0; op < 2; op++)
{
if((instIn.ops[(op * 2) + 0] < 0) || (instIn.ops[(op * 2) + 1] < 0))
break;
const BanksDump::Operator &op1 = g_embeddedBanksOperators[instIn.ops[(op * 2) + 0]];
const BanksDump::Operator &op2 = g_embeddedBanksOperators[instIn.ops[(op * 2) + 1]];
instOut.op[op].modulator_E862 = op1.d_E862;
instOut.op[op].modulator_40 = op1.d_40;
instOut.op[op].carrier_E862 = op2.d_E862;
instOut.op[op].carrier_40 = op2.d_40;
instOut.op[op].feedconn = (instIn.fbConn >> (op * 8)) & 0xFF;
instOut.op[op].noteOffset = static_cast<int8_t>(op == 0 ? instIn.noteOffset1 : instIn.noteOffset2);
}
instOut.soundKeyOnMs = instIn.delay_on_ms;
instOut.soundKeyOffMs = instIn.delay_off_ms;
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,8 @@
/*
* libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation
* libADLMIDI is a free Software MIDI synthesizer 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>
* ADLMIDI Library API: Copyright (c) 2015-2020 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
@ -54,7 +54,7 @@ class AdlMIDI_UPtr
{
T *m_p;
public:
explicit AdlMIDI_UPtr(T *p)
explicit AdlMIDI_UPtr(T *p = NULL)
: m_p(p) {}
~AdlMIDI_UPtr()
{

160
thirdparty/adlmidi/adlmidi_sequencer.cpp vendored Normal file
View file

@ -0,0 +1,160 @@
/*
* libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation
*
* Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* ADLMIDI Library API: Copyright (c) 2015-2020 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_DISABLE_MIDI_SEQUENCER
// Rename class to avoid ABI collisions
#define BW_MidiSequencer AdlMidiSequencer
// Inlucde MIDI sequencer class implementation
#include "midi_sequencer_impl.hpp"
#include "adlmidi_midiplay.hpp"
#include "adlmidi_opl3.hpp"
#include "adlmidi_private.hpp"
/****************************************************
* Real-Time MIDI calls proxies *
****************************************************/
static void rtNoteOn(void *userdata, uint8_t channel, uint8_t note, uint8_t velocity)
{
MIDIplay *context = reinterpret_cast<MIDIplay *>(userdata);
context->realTime_NoteOn(channel, note, velocity);
}
static void rtNoteOff(void *userdata, uint8_t channel, uint8_t note)
{
MIDIplay *context = reinterpret_cast<MIDIplay *>(userdata);
context->realTime_NoteOff(channel, note);
}
static void rtNoteAfterTouch(void *userdata, uint8_t channel, uint8_t note, uint8_t atVal)
{
MIDIplay *context = reinterpret_cast<MIDIplay *>(userdata);
context->realTime_NoteAfterTouch(channel, note, atVal);
}
static void rtChannelAfterTouch(void *userdata, uint8_t channel, uint8_t atVal)
{
MIDIplay *context = reinterpret_cast<MIDIplay *>(userdata);
context->realTime_ChannelAfterTouch(channel, atVal);
}
static void rtControllerChange(void *userdata, uint8_t channel, uint8_t type, uint8_t value)
{
MIDIplay *context = reinterpret_cast<MIDIplay *>(userdata);
context->realTime_Controller(channel, type, value);
}
static void rtPatchChange(void *userdata, uint8_t channel, uint8_t patch)
{
MIDIplay *context = reinterpret_cast<MIDIplay *>(userdata);
context->realTime_PatchChange(channel, patch);
}
static void rtPitchBend(void *userdata, uint8_t channel, uint8_t msb, uint8_t lsb)
{
MIDIplay *context = reinterpret_cast<MIDIplay *>(userdata);
context->realTime_PitchBend(channel, msb, lsb);
}
static void rtSysEx(void *userdata, const uint8_t *msg, size_t size)
{
MIDIplay *context = reinterpret_cast<MIDIplay *>(userdata);
context->realTime_SysEx(msg, size);
}
/* NonStandard calls */
static void rtRawOPL(void *userdata, uint8_t reg, uint8_t value)
{
MIDIplay *context = reinterpret_cast<MIDIplay *>(userdata);
return context->realTime_rawOPL(reg, value);
}
static void rtDeviceSwitch(void *userdata, size_t track, const char *data, size_t length)
{
MIDIplay *context = reinterpret_cast<MIDIplay *>(userdata);
context->realTime_deviceSwitch(track, data, length);
}
static size_t rtCurrentDevice(void *userdata, size_t track)
{
MIDIplay *context = reinterpret_cast<MIDIplay *>(userdata);
return context->realTime_currentDevice(track);
}
static void rtSongBegin(void *userdata)
{
MIDIplay *context = reinterpret_cast<MIDIplay *>(userdata);
return context->realTime_ResetState();
}
/* NonStandard calls End */
void MIDIplay::initSequencerInterface()
{
BW_MidiRtInterface *seq = new BW_MidiRtInterface;
m_sequencerInterface.reset(seq);
std::memset(seq, 0, sizeof(BW_MidiRtInterface));
seq->onDebugMessage = hooks.onDebugMessage;
seq->onDebugMessage_userData = hooks.onDebugMessage_userData;
/* MIDI Real-Time calls */
seq->rtUserData = this;
seq->rt_noteOn = rtNoteOn;
seq->rt_noteOff = rtNoteOff;
seq->rt_noteAfterTouch = rtNoteAfterTouch;
seq->rt_channelAfterTouch = rtChannelAfterTouch;
seq->rt_controllerChange = rtControllerChange;
seq->rt_patchChange = rtPatchChange;
seq->rt_pitchBend = rtPitchBend;
seq->rt_systemExclusive = rtSysEx;
/* NonStandard calls */
seq->rt_rawOPL = rtRawOPL;
seq->rt_deviceSwitch = rtDeviceSwitch;
seq->rt_currentDevice = rtCurrentDevice;
seq->onSongStart = rtSongBegin;
seq->onSongStart_userData = this;
/* NonStandard calls End */
m_sequencer->setInterface(seq);
}
double MIDIplay::Tick(double s, double granularity)
{
MidiSequencer &seqr = *m_sequencer;
double ret = seqr.Tick(s, granularity);
s *= seqr.getTempoMultiplier();
TickIterators(s);
return ret;
}
#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */

View file

@ -0,0 +1,96 @@
/*
* libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation
*
* Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* ADLMIDI Library API: Copyright (c) 2015-2020 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/>.
*/
#if !defined(_WIN32)
#include <pthread.h>
typedef pthread_mutex_t MutexNativeObject;
#else
#include <windows.h>
typedef CRITICAL_SECTION MutexNativeObject;
#endif
class Mutex
{
public:
Mutex();
~Mutex();
void lock();
void unlock();
private:
MutexNativeObject m;
Mutex(const Mutex &);
Mutex &operator=(const Mutex &);
};
class MutexHolder
{
public:
explicit MutexHolder(Mutex &m) : m(m) { m.lock(); }
~MutexHolder() { m.unlock(); }
private:
Mutex &m;
MutexHolder(const MutexHolder &);
MutexHolder &operator=(const MutexHolder &);
};
#if !defined(_WIN32)
inline Mutex::Mutex()
{
pthread_mutex_init(&m, NULL);
}
inline Mutex::~Mutex()
{
pthread_mutex_destroy(&m);
}
inline void Mutex::lock()
{
pthread_mutex_lock(&m);
}
inline void Mutex::unlock()
{
pthread_mutex_unlock(&m);
}
#else
inline Mutex::Mutex()
{
InitializeCriticalSection(&m);
}
inline Mutex::~Mutex()
{
DeleteCriticalSection(&m);
}
inline void Mutex::lock()
{
EnterCriticalSection(&m);
}
inline void Mutex::unlock()
{
LeaveCriticalSection(&m);
}
#endif

View file

@ -40,6 +40,7 @@
#include <vector>
#include <memory>
#include "dbopl.h"
#include "../common/mutex.hpp"
#if defined(__GNUC__) && __GNUC__ > 3
#define INLINE inline __attribute__((__always_inline__))
@ -72,36 +73,16 @@
#define PI 3.14159265358979323846
#endif
struct NoCopy {
NoCopy() {}
private:
NoCopy(const NoCopy &);
NoCopy &operator=(const NoCopy &);
};
#if !defined(_WIN32)
#include <pthread.h>
struct Mutex : NoCopy {
Mutex() { pthread_mutex_init(&m, NULL);}
~Mutex() { pthread_mutex_destroy(&m); }
void lock() { pthread_mutex_lock(&m); }
void unlock() { pthread_mutex_unlock(&m); }
pthread_mutex_t m;
};
#else
#include <windows.h>
struct Mutex : NoCopy {
Mutex() { InitializeCriticalSection(&m); }
~Mutex() { DeleteCriticalSection(&m); }
void lock() { EnterCriticalSection(&m); }
void unlock() { LeaveCriticalSection(&m); }
CRITICAL_SECTION m;
};
/*
* Workaround for some compilers are has no those macros in their headers!
*/
#ifndef INT16_MIN
#define INT16_MIN (-0x7fff - 1)
#endif
struct MutexHolder : NoCopy {
explicit MutexHolder(Mutex &m) : m(m) { m.lock(); }
~MutexHolder() { m.unlock(); }
Mutex &m;
};
#ifndef INT16_MAX
#define INT16_MAX 0x7fff
#endif
namespace DBOPL {
@ -1333,10 +1314,14 @@ struct CacheEntry {
Bit32u linearRates[76];
Bit32u attackRates[76];
};
struct Cache : NoCopy {
~Cache();
Mutex mutex;
std::vector<CacheEntry *> entries;
struct Cache {
Cache() {}
~Cache();
Mutex mutex;
std::vector<CacheEntry *> entries;
private:
Cache(const Cache &);
Cache &operator=(const Cache &);
};
static Cache cache;
@ -1522,11 +1507,15 @@ void Chip::Setup( Bit32u rate ) {
}
}
static bool doneTables = false;
static volatile bool doneTables = false;
static Mutex mutexTables;
void InitTables( void ) {
if ( doneTables )
return;
doneTables = true;
MutexHolder lock( mutexTables );
if ( doneTables )
return;
#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG )
//Exponential volume table, same as the real adlib
for ( int i = 0; i < 256; i++ ) {
@ -1676,6 +1665,7 @@ void InitTables( void ) {
}
}
#endif
doneTables = true;
}
Bit32u Handler::WriteAddr( Bit32u port, Bit8u val ) {

View file

@ -286,5 +286,7 @@ struct Handler {
void Init( Bitu rate );
};
// Pre-Initialize internal tables
void InitTables(void);
} //Namespace

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPL3 (YMF262) chip emulators
*
* Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -37,6 +37,11 @@ DosBoxOPL3::~DosBoxOPL3()
delete chip_r;
}
void DosBoxOPL3::globalPreInit()
{
DBOPL::InitTables();
}
void DosBoxOPL3::setRate(uint32_t rate)
{
OPLChipBaseBufferedT::setRate(rate);

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPL3 (YMF262) chip emulators
*
* Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -30,6 +30,8 @@ public:
DosBoxOPL3();
~DosBoxOPL3() override;
static void globalPreInit();
bool canRunAtPcmRate() const override { return true; }
void setRate(uint32_t rate) override;
void reset() override;

1900
thirdparty/adlmidi/chips/java/JavaOPL3.hpp vendored Normal file

File diff suppressed because it is too large Load diff

112
thirdparty/adlmidi/chips/java_opl3.cpp vendored Normal file
View file

@ -0,0 +1,112 @@
/*
* Interfaces over Yamaha OPL3 (YMF262) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* 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 GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "java_opl3.h"
#include "java/JavaOPL3.hpp"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#ifndef M_SQRT1_2
#define M_SQRT1_2 0.70710678118654752440
#endif
JavaOPL3::JavaOPL3() :
OPLChipBaseBufferedT(),
m_chip(new ADL_JavaOPL3::OPL3(true))
{
reset();
}
JavaOPL3::~JavaOPL3()
{
ADL_JavaOPL3::OPL3 *chip_r = reinterpret_cast<ADL_JavaOPL3::OPL3 *>(m_chip);
delete chip_r;
}
void JavaOPL3::setRate(uint32_t rate)
{
OPLChipBaseBufferedT::setRate(rate);
ADL_JavaOPL3::OPL3 *chip_r = reinterpret_cast<ADL_JavaOPL3::OPL3 *>(m_chip);
chip_r->Reset();
float pan = sinf((float)M_SQRT1_2);
for (unsigned channel = 0; channel < 18; ++channel)
chip_r->SetPanning(channel, pan, pan);
}
void JavaOPL3::reset()
{
OPLChipBaseBufferedT::reset();
ADL_JavaOPL3::OPL3 *chip_r = reinterpret_cast<ADL_JavaOPL3::OPL3 *>(m_chip);
chip_r->Reset();
}
void JavaOPL3::writeReg(uint16_t addr, uint8_t data)
{
ADL_JavaOPL3::OPL3 *chip_r = reinterpret_cast<ADL_JavaOPL3::OPL3 *>(m_chip);
chip_r->WriteReg(addr, data);
}
void JavaOPL3::writePan(uint16_t addr, uint8_t data)
{
ADL_JavaOPL3::OPL3 *chip_r = reinterpret_cast<ADL_JavaOPL3::OPL3 *>(m_chip);
unsigned high = (addr >> 8) & 0x01;
unsigned regm = addr & 0xff;
unsigned channel = 9 * high + (regm & 0x0f);
float phase = (data == 63 || data == 64) ? 63.5f : (float)data;
phase *= (float)(M_PI / 2 / 127);
chip_r->SetPanning(channel, cosf(phase), sinf(phase));
}
void JavaOPL3::nativeGenerateN(int16_t *output, size_t frames)
{
ADL_JavaOPL3::OPL3 *chip_r = reinterpret_cast<ADL_JavaOPL3::OPL3 *>(m_chip);
enum { maxframes = 256 };
float buf[2 * maxframes];
while(frames > 0)
{
memset(buf, 0, sizeof(buf));
size_t curframes = (frames < (size_t)maxframes) ? frames : (size_t)maxframes;
chip_r->Update(buf, (int)curframes);
size_t cursamples = 2 * curframes;
for(size_t i = 0; i < cursamples; ++i)
{
int32_t sample = (int32_t)lround(4096 * buf[i]);
sample = (sample > -32768) ? sample : -32768;
sample = (sample < +32767) ? sample : +32767;
output[i] = (int16_t)sample;
}
output += cursamples;
frames -= curframes;
}
}
const char *JavaOPL3::emulatorName()
{
return "Java 1.0.6 OPL3";
}

44
thirdparty/adlmidi/chips/java_opl3.h vendored Normal file
View file

@ -0,0 +1,44 @@
/*
* Interfaces over Yamaha OPL3 (YMF262) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* 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 GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef JAVA_OPL3_H
#define JAVA_OPL3_H
#include "opl_chip_base.h"
class JavaOPL3 final : public OPLChipBaseBufferedT<JavaOPL3>
{
void *m_chip;
public:
JavaOPL3();
~JavaOPL3() 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 writePan(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 // JAVA_OPL3_H

View file

@ -53,6 +53,117 @@ enum {
};
#if OPL_FAST_WAVEGEN
/*
* logsin table
*/
static const Bit16u logsinrom[512] = {
0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471,
0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365,
0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd,
0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261,
0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f,
0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd,
0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195,
0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166,
0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c,
0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118,
0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8,
0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db,
0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1,
0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9,
0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094,
0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081,
0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070,
0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060,
0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052,
0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045,
0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039,
0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f,
0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026,
0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e,
0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017,
0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011,
0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c,
0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007,
0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004,
0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002,
0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001,
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x002,
0x002, 0x002, 0x002, 0x003, 0x003, 0x003, 0x004, 0x004,
0x004, 0x005, 0x005, 0x005, 0x006, 0x006, 0x007, 0x007,
0x007, 0x008, 0x008, 0x009, 0x009, 0x00a, 0x00a, 0x00b,
0x00c, 0x00c, 0x00d, 0x00d, 0x00e, 0x00f, 0x00f, 0x010,
0x011, 0x011, 0x012, 0x013, 0x014, 0x014, 0x015, 0x016,
0x017, 0x017, 0x018, 0x019, 0x01a, 0x01b, 0x01c, 0x01d,
0x01e, 0x01f, 0x020, 0x021, 0x022, 0x023, 0x024, 0x025,
0x026, 0x027, 0x028, 0x029, 0x02a, 0x02b, 0x02d, 0x02e,
0x02f, 0x030, 0x031, 0x033, 0x034, 0x035, 0x037, 0x038,
0x039, 0x03b, 0x03c, 0x03e, 0x03f, 0x040, 0x042, 0x043,
0x045, 0x046, 0x048, 0x04a, 0x04b, 0x04d, 0x04e, 0x050,
0x052, 0x053, 0x055, 0x057, 0x059, 0x05b, 0x05c, 0x05e,
0x060, 0x062, 0x064, 0x066, 0x068, 0x06a, 0x06c, 0x06e,
0x070, 0x072, 0x074, 0x076, 0x078, 0x07a, 0x07d, 0x07f,
0x081, 0x083, 0x086, 0x088, 0x08a, 0x08d, 0x08f, 0x092,
0x094, 0x097, 0x099, 0x09c, 0x09f, 0x0a1, 0x0a4, 0x0a7,
0x0a9, 0x0ac, 0x0af, 0x0b2, 0x0b5, 0x0b8, 0x0bb, 0x0be,
0x0c1, 0x0c4, 0x0c7, 0x0ca, 0x0cd, 0x0d1, 0x0d4, 0x0d7,
0x0db, 0x0de, 0x0e2, 0x0e5, 0x0e9, 0x0ec, 0x0f0, 0x0f4,
0x0f8, 0x0fb, 0x0ff, 0x103, 0x107, 0x10b, 0x10f, 0x114,
0x118, 0x11c, 0x121, 0x125, 0x129, 0x12e, 0x133, 0x137,
0x13c, 0x141, 0x146, 0x14b, 0x150, 0x155, 0x15b, 0x160,
0x166, 0x16b, 0x171, 0x177, 0x17c, 0x182, 0x188, 0x18f,
0x195, 0x19b, 0x1a2, 0x1a9, 0x1b0, 0x1b7, 0x1be, 0x1c5,
0x1cd, 0x1d4, 0x1dc, 0x1e4, 0x1ec, 0x1f5, 0x1fd, 0x206,
0x20f, 0x218, 0x222, 0x22c, 0x236, 0x240, 0x24b, 0x256,
0x261, 0x26d, 0x279, 0x286, 0x293, 0x2a0, 0x2af, 0x2bd,
0x2cd, 0x2dc, 0x2ed, 0x2ff, 0x311, 0x324, 0x339, 0x34e,
0x365, 0x37e, 0x398, 0x3b5, 0x3d3, 0x3f5, 0x41a, 0x443,
0x471, 0x4a6, 0x4e4, 0x52e, 0x58b, 0x607, 0x6c3, 0x859
};
/*
* exp table
*/
static const Bit16u exprom[256] = {
0xff4, 0xfea, 0xfde, 0xfd4, 0xfc8, 0xfbe, 0xfb4, 0xfa8,
0xf9e, 0xf92, 0xf88, 0xf7e, 0xf72, 0xf68, 0xf5c, 0xf52,
0xf48, 0xf3e, 0xf32, 0xf28, 0xf1e, 0xf14, 0xf08, 0xefe,
0xef4, 0xeea, 0xee0, 0xed4, 0xeca, 0xec0, 0xeb6, 0xeac,
0xea2, 0xe98, 0xe8e, 0xe84, 0xe7a, 0xe70, 0xe66, 0xe5c,
0xe52, 0xe48, 0xe3e, 0xe34, 0xe2a, 0xe20, 0xe16, 0xe0c,
0xe04, 0xdfa, 0xdf0, 0xde6, 0xddc, 0xdd2, 0xdca, 0xdc0,
0xdb6, 0xdac, 0xda4, 0xd9a, 0xd90, 0xd88, 0xd7e, 0xd74,
0xd6a, 0xd62, 0xd58, 0xd50, 0xd46, 0xd3c, 0xd34, 0xd2a,
0xd22, 0xd18, 0xd10, 0xd06, 0xcfe, 0xcf4, 0xcec, 0xce2,
0xcda, 0xcd0, 0xcc8, 0xcbe, 0xcb6, 0xcae, 0xca4, 0xc9c,
0xc92, 0xc8a, 0xc82, 0xc78, 0xc70, 0xc68, 0xc60, 0xc56,
0xc4e, 0xc46, 0xc3c, 0xc34, 0xc2c, 0xc24, 0xc1c, 0xc12,
0xc0a, 0xc02, 0xbfa, 0xbf2, 0xbea, 0xbe0, 0xbd8, 0xbd0,
0xbc8, 0xbc0, 0xbb8, 0xbb0, 0xba8, 0xba0, 0xb98, 0xb90,
0xb88, 0xb80, 0xb78, 0xb70, 0xb68, 0xb60, 0xb58, 0xb50,
0xb48, 0xb40, 0xb38, 0xb32, 0xb2a, 0xb22, 0xb1a, 0xb12,
0xb0a, 0xb02, 0xafc, 0xaf4, 0xaec, 0xae4, 0xade, 0xad6,
0xace, 0xac6, 0xac0, 0xab8, 0xab0, 0xaa8, 0xaa2, 0xa9a,
0xa92, 0xa8c, 0xa84, 0xa7c, 0xa76, 0xa6e, 0xa68, 0xa60,
0xa58, 0xa52, 0xa4a, 0xa44, 0xa3c, 0xa36, 0xa2e, 0xa28,
0xa20, 0xa18, 0xa12, 0xa0c, 0xa04, 0x9fe, 0x9f6, 0x9f0,
0x9e8, 0x9e2, 0x9da, 0x9d4, 0x9ce, 0x9c6, 0x9c0, 0x9b8,
0x9b2, 0x9ac, 0x9a4, 0x99e, 0x998, 0x990, 0x98a, 0x984,
0x97c, 0x976, 0x970, 0x96a, 0x962, 0x95c, 0x956, 0x950,
0x948, 0x942, 0x93c, 0x936, 0x930, 0x928, 0x922, 0x91c,
0x916, 0x910, 0x90a, 0x904, 0x8fc, 0x8f6, 0x8f0, 0x8ea,
0x8e4, 0x8de, 0x8d8, 0x8d2, 0x8cc, 0x8c6, 0x8c0, 0x8ba,
0x8b4, 0x8ae, 0x8a8, 0x8a2, 0x89c, 0x896, 0x890, 0x88a,
0x884, 0x87e, 0x878, 0x872, 0x86c, 0x866, 0x860, 0x85a,
0x854, 0x850, 0x84a, 0x844, 0x83e, 0x838, 0x832, 0x82c,
0x828, 0x822, 0x81c, 0x816, 0x810, 0x80c, 0x806, 0x800
};
#else
/*
* logsin table
*/
@ -130,6 +241,7 @@ static const Bit16u exprom[256] = {
0x42a, 0x428, 0x425, 0x422, 0x41f, 0x41c, 0x419, 0x416,
0x414, 0x411, 0x40e, 0x40b, 0x408, 0x406, 0x403, 0x400
};
#endif
/*
* freq mult table multiplied by 2
@ -207,8 +319,8 @@ static const Bit16u panlawtable[] =
* Envelope generator
*/
#if !OPL_FAST_WAVEGEN
typedef Bit16s(*envelope_sinfunc)(Bit16u phase, Bit16u envelope);
typedef void(*envelope_genfunc)(opl3_slot *slott);
static Bit16s OPL3_EnvelopeCalcExp(Bit32u level)
{
@ -366,6 +478,7 @@ static const envelope_sinfunc envelope_sin[8] = {
OPL3_EnvelopeCalcSin6,
OPL3_EnvelopeCalcSin7
};
#endif
enum envelope_gen_num
{
@ -401,6 +514,15 @@ static void OPL3_EnvelopeCalc(opl3_slot *slot)
Bit8u reset = 0;
slot->eg_out = slot->eg_rout + (slot->reg_tl << 2)
+ (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem;
#if OPL_FAST_WAVEGEN
if (slot->eg_out > 0x1ff)
{
slot->eg_out = 0x1ff;
}
slot->eg_out <<= 3;
#endif
if (slot->key && slot->eg_gen == envelope_gen_num_release)
{
reset = 1;
@ -687,12 +809,92 @@ static void OPL3_SlotWriteE0(opl3_slot *slot, Bit8u data)
{
slot->reg_wf &= 0x03;
}
#if OPL_FAST_WAVEGEN
switch (slot->reg_wf)
{
case 1:
case 4:
case 5:
slot->maskzero = 0x200;
break;
case 3:
slot->maskzero = 0x100;
break;
default:
slot->maskzero = 0;
break;
}
switch (slot->reg_wf)
{
case 4:
slot->signpos = (31-8); /* sigext of (phase & 0x100) */
break;
case 0:
case 6:
case 7:
slot->signpos = (31-9); /* sigext of (phase & 0x200) */
break;
default:
slot->signpos = (31-16); /* set "neg" to zero */
break;
}
switch (slot->reg_wf)
{
case 4:
case 5:
slot->phaseshift = 1;
break;
case 6:
slot->phaseshift = 16; /* set phase to zero and flag for non-sin wave */
break;
case 7:
slot->phaseshift = 32; /* no shift (work by mod 32), but flag for non-sin wave */
break;
default:
slot->phaseshift = 0;
break;
}
#endif
}
#if OPL_FAST_WAVEGEN
static void OPL3_SlotGenerate(opl3_slot *slot)
{
Bit16u phase = slot->pg_phase_out + *slot->mod;
Bit32u neg, level;
Bit8u phaseshift;
/* Fast paths for mute segments */
if (phase & slot->maskzero)
{
slot->out = 0;
return;
}
neg = (Bit32s)((Bit32u)phase << slot->signpos) >> 31;
phaseshift = slot->phaseshift;
level = slot->eg_out;
phase <<= phaseshift;
if (phaseshift <= 1)
{
level += logsinrom[phase & 0x1ff];
}
else
{
level += ((phase ^ neg) & 0x3ff) << 3;
}
slot->out = exprom[level & 0xff] >> (level >> 8) ^ neg;
}
#else
static void OPL3_SlotGenerate(opl3_slot *slot)
{
slot->out = envelope_sin[slot->reg_wf](slot->pg_phase_out + *slot->mod, slot->eg_out);
}
#endif
static void OPL3_SlotCalcFB(opl3_slot *slot)
{
@ -1176,7 +1378,7 @@ void OPL3_Generate(opl3_chip *chip, Bit16s *buf)
if (chip->eg_timerrem || chip->eg_state)
{
if (chip->eg_timer == (uint64_t)0xfffffffffU)
if (chip->eg_timer == 0xfffffffffULL)
{
chip->eg_timer = 0;
chip->eg_timerrem = 1;
@ -1231,10 +1433,17 @@ void OPL3_Reset(opl3_chip *chip, Bit32u samplerate)
chip->slot[slotnum].chip = chip;
chip->slot[slotnum].mod = &chip->zeromod;
chip->slot[slotnum].eg_rout = 0x1ff;
#if OPL_FAST_WAVEGEN
chip->slot[slotnum].eg_out = 0x1ff << 3;
#else
chip->slot[slotnum].eg_out = 0x1ff;
#endif
chip->slot[slotnum].eg_gen = envelope_gen_num_release;
chip->slot[slotnum].trem = (Bit8u*)&chip->zeromod;
chip->slot[slotnum].slot_num = slotnum;
#if OPL_FAST_WAVEGEN
chip->slot[slotnum].signpos = (31-9); /* for wf=0 need use sigext of (phase & 0x200) */
#endif
}
for (channum = 0; channum < 18; channum++)
{
@ -1420,16 +1629,23 @@ void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples)
}
}
#define OPL3_MIN(A, B) (((A) > (B)) ? (B) : (A))
#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)
{
Bit32u i;
Bit16s sample[2];
Bit32s mix[2];
for(i = 0; i < numsamples; i++)
{
OPL3_GenerateResampled(chip, sample);
sndptr[0] += sample[0];
sndptr[1] += sample[1];
mix[0] = sndptr[0] + sample[0];
mix[1] = sndptr[1] + sample[1];
sndptr[0] = OPL3_CLAMP(mix[0], INT16_MIN, INT16_MAX);
sndptr[1] = OPL3_CLAMP(mix[1], INT16_MIN, INT16_MAX);
sndptr += 2;
}
}

View file

@ -33,13 +33,16 @@
#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
#define OPL_FAST_WAVEGEN 1 /* optimized waveform generation */
typedef uintptr_t Bitu;
typedef intptr_t Bits;
@ -86,6 +89,12 @@ struct _opl3_slot {
Bit32u pg_phase;
Bit16u pg_phase_out;
Bit8u slot_num;
#if OPL_FAST_WAVEGEN
Bit16u maskzero;
Bit8u signpos;
Bit8u phaseshift;
#endif
};
struct _opl3_channel {

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPL3 (YMF262) chip emulators
*
* Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPL3 (YMF262) chip emulators
*
* Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPL3 (YMF262) chip emulators
*
* Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPL3 (YMF262) chip emulators
*
* Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public

View file

@ -0,0 +1,19 @@
A quote from a letter sent by Willy Reeve to Vitaly Novichkov
as a reply from a letter sent by Reality's (https://www.3eality.com/)
contact form
----------------------------------------------------------------
All source code supplied with RAD is in the public domain.
----------------------------------------------------------------
================================================================
The complete letter:
================================================================
Hi,
All source code supplied with RAD is in the public domain.
We'll clarify that in the documentation for the next release.
Later...
SHAYDE
================================================================

View file

@ -0,0 +1,9 @@
Copy opal.cpp here to make this emulator available.
This is not distributed with the software while the license is undetermined.
The opal.cpp file is part of REALITY ADLIB TRACKER V2.
SHA1: c034dd637887d81770cee92da309fc547ef5fe18
Configure the software with the option USE_OPAL_EMULATOR=ON.
To enable soft panning, it is needed to patch with the file `opal-pan.diff` provided.
for example: `patch -p0 -i opal-pan.diff`

View file

@ -0,0 +1,131 @@
--- opal.cpp.orig 2019-02-23 20:32:00.477658789 +0100
+++ opal.cpp 2019-02-23 20:46:36.745113958 +0100
@@ -13,6 +13,7 @@
- Percussion mode
*/
+#define OPAL_HAVE_SOFT_PANNING 1 /* libADLMIDI */
@@ -133,6 +134,7 @@
void SetOctave(uint16_t oct);
void SetLeftEnable(bool on);
void SetRightEnable(bool on);
+ void SetPan(uint8_t pan);
void SetFeedback(uint16_t val);
void SetModulationType(uint16_t type);
@@ -158,6 +160,7 @@
Channel * ChannelPair;
bool Enable;
bool LeftEnable, RightEnable;
+ uint16_t LeftPan, RightPan;
};
public:
@@ -166,6 +169,7 @@
void SetSampleRate(int sample_rate);
void Port(uint16_t reg_num, uint8_t val);
+ void Pan(uint16_t reg_num, uint8_t pan);
void Sample(int16_t *left, int16_t *right);
protected:
@@ -191,6 +195,7 @@
static const uint16_t RateTables[4][8];
static const uint16_t ExpTable[256];
static const uint16_t LogSinTable[256];
+ static const uint16_t PanLawTable[128];
};
//--------------------------------------------------------------------------------------------------
const uint16_t Opal::RateTables[4][8] = {
@@ -237,6 +242,28 @@
7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2,
2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
};
+//--------------------------------------------------------------------------------------------------
+const uint16_t Opal::PanLawTable[128] =
+{
+ 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289,
+ 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410,
+ 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901,
+ 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776,
+ 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057,
+ 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770,
+ 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947,
+ 50433, 49912, 49383, 48846, 48302, 47750, 47191,
+ 46340, // Center left
+ 46340, // Center right
+ 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221,
+ 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986,
+ 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400,
+ 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516,
+ 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392,
+ 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088,
+ 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666,
+ 4858, 4050, 3240, 2431, 1620, 810, 0
+};
@@ -342,6 +369,10 @@
for (int i = 0; i < NumOperators; i++)
Op[i].ComputeRates();
+ // Initialise channel panning at center.
+ for (int i = 0; i < NumChannels; i++)
+ Chan[i].SetPan(64);
+
SetSampleRate(sample_rate);
}
@@ -533,6 +564,19 @@
//==================================================================================================
+// Set panning on the channel designated by the register number.
+// This is extended functionality.
+//==================================================================================================
+void Opal::Pan(uint16_t reg_num, uint8_t pan)
+{
+ uint8_t high = (reg_num >> 8) & 1;
+ uint8_t regm = reg_num & 0xff;
+ Chan[9 * high + (regm & 0x0f)].SetPan(pan);
+}
+
+
+
+//==================================================================================================
// Generate sample. Every time you call this you will get two signed 16-bit samples (one for each
// stereo channel) which will sound correct when played back at the sample rate given when the
// class was constructed.
@@ -727,6 +771,9 @@
left = LeftEnable ? out : 0;
right = RightEnable ? out : 0;
+
+ left = left * LeftPan / 65536;
+ right = right * RightPan / 65536;
}
@@ -793,6 +840,18 @@
}
+
+//==================================================================================================
+// Pan the channel to the position given.
+//==================================================================================================
+void Opal::Channel::SetPan(uint8_t pan)
+{
+ pan &= 127;
+ LeftPan = PanLawTable[pan];
+ RightPan = PanLawTable[127 - pan];
+}
+
+
//==================================================================================================
// Set the channel feedback amount.

1398
thirdparty/adlmidi/chips/opal/opal.hpp vendored Normal file

File diff suppressed because it is too large Load diff

81
thirdparty/adlmidi/chips/opal_opl3.cpp vendored Normal file
View file

@ -0,0 +1,81 @@
/*
* Interfaces over Yamaha OPL3 (YMF262) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* 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 GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "opal_opl3.h"
#include "opal/opal.hpp"
#include <new>
#include <cstring>
OpalOPL3::OpalOPL3() :
OPLChipBaseT()
{
m_chip = new Opal(m_rate);
setRate(m_rate);
}
OpalOPL3::~OpalOPL3()
{
Opal *chip_r = reinterpret_cast<Opal *>(m_chip);
delete chip_r;
}
void OpalOPL3::setRate(uint32_t rate)
{
OPLChipBaseT::setRate(rate);
Opal *chip_r = reinterpret_cast<Opal *>(m_chip);
chip_r->~Opal();
new (chip_r) Opal(effectiveRate());
}
void OpalOPL3::reset()
{
OPLChipBaseT::reset();
Opal *chip_r = reinterpret_cast<Opal *>(m_chip);
chip_r->~Opal();
new (chip_r) Opal(effectiveRate());
}
void OpalOPL3::writeReg(uint16_t addr, uint8_t data)
{
Opal *chip_r = reinterpret_cast<Opal *>(m_chip);
chip_r->Port(addr, data);
}
void OpalOPL3::writePan(uint16_t addr, uint8_t data)
{
#ifdef OPAL_HAVE_SOFT_PANNING
Opal *chip_r = reinterpret_cast<Opal *>(m_chip);
chip_r->Pan(addr, data);
#else
(void)addr;
(void)data;
#endif
}
void OpalOPL3::nativeGenerate(int16_t *frame)
{
Opal *chip_r = reinterpret_cast<Opal *>(m_chip);
chip_r->Sample(&frame[0], &frame[1]);
}
const char *OpalOPL3::emulatorName()
{
return "Opal OPL3";
}

44
thirdparty/adlmidi/chips/opal_opl3.h vendored Normal file
View file

@ -0,0 +1,44 @@
/*
* Interfaces over Yamaha OPL3 (YMF262) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* 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 GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef OPAL_OPL3_H
#define OPAL_OPL3_H
#include "opl_chip_base.h"
class OpalOPL3 final : public OPLChipBaseT<OpalOPL3>
{
void *m_chip;
public:
OpalOPL3();
~OpalOPL3() 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 writePan(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

@ -1,5 +1,5 @@
/*
* Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public

View file

@ -236,8 +236,8 @@ void OPLChipBaseT<T>::resampledGenerate(int32_t *output)
rsm->out_count = 1;
rsm->out_data = f_out;
}
output[0] = static_cast<int32_t>(std::lround(f_out[0]));
output[1] = static_cast<int32_t>(std::lround(f_out[1]));
output[0] = static_cast<int32_t>(lround(f_out[0]));
output[1] = static_cast<int32_t>(lround(f_out[1]));
}
#else
template <class T>

View file

@ -1,7 +1,7 @@
/*
* FileAndMemoryReader - a tiny helper to utify file reading from a disk and memory block
*
* Copyright (c) 2015-2018 Vitaly Novichkov <admin@wohlnet.ru>
* Copyright (c) 2015-2020 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"),
@ -110,15 +110,15 @@ public:
/**
* @brief Open file from memory block
* @param mem Pointer to the memory block
* @param lenght Size of given block
* @param length Size of given block
*/
void openData(const void *mem, size_t lenght)
void openData(const void *mem, size_t length)
{
if(m_fp)
this->close();//Close previously opened file first!
m_fp = NULL;
m_mp = mem;
m_mp_size = lenght;
m_mp_size = length;
m_mp_tell = 0;
}
@ -140,6 +140,7 @@ public:
{
switch(rel_to)
{
default:
case SET:
m_mp_tell = static_cast<size_t>(pos);
break;

220
thirdparty/adlmidi/fraction.hpp vendored Normal file
View file

@ -0,0 +1,220 @@
/*
* Fraction number handling.
* Copyright (C) 1992,2001 Bisqwit (http://iki.fi/bisqwit/)
*
* The license of this file is in Public Domain:
* https://bisqwit.iki.fi/src/index.html
*
* "... and orphan source code files are copyrighted public domain."
*/
#ifndef bqw_fraction_h
#define bqw_fraction_h
#include <cmath>
#include <limits>
template<typename inttype=int>
class fraction
{
inttype num1, num2;
typedef fraction<inttype> self;
void Optim();
#if 1
inline void Debug(char, const self &) { }
#else
inline void Debug(char op, const self &b)
{
cerr << nom() << '/' << denom() << ' ' << op
<< ' ' << b.nom() << '/' << denom()
<< ":\n";
}
#endif
public:
void set(inttype n, inttype d) { num1=n; num2=d; Optim(); }
fraction() : num1(0), num2(1) { }
fraction(inttype value) : num1(value), num2(1) { }
fraction(inttype n, inttype d) : num1(n), num2(d) { }
fraction(int value) : num1(value), num2(1) { }
template<typename floattype>
explicit fraction(const floattype value) { operator= (value); }
inline double value() const {return nom() / (double)denom(); }
inline long double valuel() const {return nom() / (long double)denom(); }
self &operator+= (const inttype &value) { num1+=value*denom(); Optim(); return *this; }
self &operator-= (const inttype &value) { num1-=value*denom(); Optim(); return *this; }
self &operator*= (const inttype &value) { num1*=value; Optim(); return *this; }
self &operator/= (const inttype &value) { num2*=value; Optim(); return *this; }
self &operator+= (const self &b);
self &operator-= (const self &b);
self &operator*= (const self &b) { Debug('*',b);num1*=b.nom(); num2*=b.denom(); Optim(); return *this; }
self &operator/= (const self &b) { Debug('/',b);num1*=b.denom(); num2*=b.nom(); Optim(); return *this; }
self operator- () const { return self(-num1, num2); }
#define fraction_blah_func(op1, op2) \
self operator op1 (const self &b) const { self tmp(*this); tmp op2 b; return tmp; }
fraction_blah_func( +, += )
fraction_blah_func( -, -= )
fraction_blah_func( /, /= )
fraction_blah_func( *, *= )
#undef fraction_blah_func
#define fraction_blah_func(op) \
bool operator op(const self &b) const { return value() op b.value(); } \
bool operator op(inttype b) const { return value() op b; }
fraction_blah_func( < )
fraction_blah_func( > )
fraction_blah_func( <= )
fraction_blah_func( >= )
#undef fraction_blah_func
const inttype &nom() const { return num1; }
const inttype &denom() const { return num2; }
inline bool operator == (inttype b) const { return denom() == 1 && nom() == b; }
inline bool operator != (inttype b) const { return denom() != 1 || nom() != b; }
inline bool operator == (const self &b) const { return denom()==b.denom() && nom()==b.nom(); }
inline bool operator != (const self &b) const { return denom()!=b.denom() || nom()!=b.nom(); }
//operator bool () const { return nom() != 0; }
inline bool negative() const { return (nom() < 0) ^ (denom() < 0); }
self &operator= (const inttype value) { num2=1; num1=value; return *this; }
//self &operator= (int value) { num2=1; num1=value; return *this; }
self &operator= (double orig) { return *this = (long double)orig; }
self &operator= (long double orig);
};
#ifdef _MSC_VER
#pragma warning(disable:4146)
#endif
template<typename inttype>
void fraction<inttype>::Optim()
{
/* Euclidean algorithm */
inttype n1, n2, nn1, nn2;
nn1 = std::numeric_limits<inttype>::is_signed ? (num1 >= 0 ? num1 : -num1) : num1;
nn2 = std::numeric_limits<inttype>::is_signed ? (num2 >= 0 ? num2 : -num2) : num2;
if(nn1 < nn2)
n1 = num1, n2 = num2;
else
n1 = num2, n2 = num1;
if(!num1) { num2 = 1; return; }
for(;;)
{
//fprintf(stderr, "%d/%d: n1=%d,n2=%d\n", nom(),denom(),n1,n2);
inttype tmp = n2 % n1;
if(!tmp)break;
n2 = n1;
n1 = tmp;
}
num1 /= n1;
num2 /= n1;
//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)
{
return fraction<inttype>(abs(f.nom()), abs(f.denom()));
}
#define fraction_blah_func(op) \
template<typename inttype> \
fraction<inttype> operator op \
(const inttype bla, const fraction<inttype> &b) \
{ return fraction<inttype> (bla) op b; }
fraction_blah_func( + )
fraction_blah_func( - )
fraction_blah_func( * )
fraction_blah_func( / )
#undef fraction_blah_func
#define fraction_blah_func(op1, op2) \
template<typename inttype> \
fraction<inttype> &fraction<inttype>::operator op2 (const fraction<inttype> &b) \
{ \
inttype newnom = nom()*b.denom() op1 denom()*b.nom(); \
num2 *= b.denom(); \
num1 = newnom; \
Optim(); \
return *this; \
}
fraction_blah_func( +, += )
fraction_blah_func( -, -= )
#undef fraction_blah_func
template<typename inttype>
fraction<inttype> &fraction<inttype>::operator= (long double orig)
{
if(orig == 0.0)
{
set(0, 0);
return *this;
}
inttype cf[25];
for(int maxdepth=1; maxdepth<25; ++maxdepth)
{
inttype u,v;
long double virhe, a=orig;
int i, viim;
for(i = 0; i < maxdepth; ++i)
{
cf[i] = (inttype)a;
if(cf[i]-1 > cf[i])break;
a = 1.0 / (a - cf[i]);
}
for(viim=i-1; i < maxdepth; ++i)
cf[i] = 0;
u = cf[viim];
v = 1;
for(i = viim-1; i >= 0; --i)
{
inttype w = cf[i] * u + v;
v = u;
u = w;
}
virhe = (orig - (u / (long double)v)) / orig;
set(u, v);
//if(verbose > 4)
// cerr << "Guess: " << *this << " - error = " << virhe*100 << "%\n";
if(virhe < 1e-8 && virhe > -1e-8)break;
}
//if(verbose > 4)
//{
// cerr << "Fraction=" << orig << ": " << *this << endl;
//}
return *this;
}
/*
template<typename inttype>
ostream &operator << (ostream &dest, const fraction<inttype> &m)
{
if(m.denom() == (inttype)1) return dest << m.nom();
return dest << m.nom() << '/' << m.denom();
}
*/
#endif

6474
thirdparty/adlmidi/inst_db.cpp vendored Normal file

File diff suppressed because it is too large Load diff

121
thirdparty/adlmidi/oplinst.h vendored Normal file
View file

@ -0,0 +1,121 @@
/*
* libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation
*
* Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* ADLMIDI Library API: Copyright (c) 2015-2020 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 OPLINST_H
#define OPLINST_H
#include <string.h>
#include <stdint.h>
#include <cstring>
#pragma pack(push, 1)
#define OPLINST_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); }
/**
* @brief OPL3 Operator data for single tumbre
*/
struct OplTimbre
{
//! WaveForm, Sustain/Release, AttackDecay, and AM/VIB/EG/KSR/F-Mult settings
uint32_t modulator_E862, carrier_E862;
//! KSL/attenuation settings
uint8_t modulator_40, carrier_40;
//! Feedback/connection bits for the channel
uint8_t feedconn;
//! Semi-tone offset
int8_t noteOffset;
};
OPLINST_BYTE_COMPARABLE(struct OplTimbre)
enum { OPLNoteOnMaxTime = 40000 };
/**
* @brief Instrument data with operators included
*/
struct OplInstMeta
{
enum
{
Flag_Pseudo4op = 0x01,
Flag_NoSound = 0x02,
Flag_Real4op = 0x04
};
enum
{
Flag_RM_BassDrum = 0x08,
Flag_RM_Snare = 0x10,
Flag_RM_TomTom = 0x18,
Flag_RM_Cymbal = 0x20,
Flag_RM_HiHat = 0x28,
Mask_RhythmMode = 0x38
};
//! Operator data
OplTimbre op[2];
//! Fixed note for drum instruments
uint8_t drumTone;
//! Instrument flags
uint8_t flags;
//! Number of milliseconds it produces sound while key on
uint16_t soundKeyOnMs;
//! Number of milliseconds it produces sound while releasing after key off
uint16_t soundKeyOffMs;
//! MIDI velocity offset
int8_t midiVelocityOffset;
//! Second voice detune
double voice2_fine_tune;
};
OPLINST_BYTE_COMPARABLE(struct OplInstMeta)
#undef OPLINST_BYTE_COMPARABLE
#pragma pack(pop)
/**
* @brief Bank global setup
*/
struct OplBankSetup
{
int volumeModel;
bool deepTremolo;
bool deepVibrato;
bool scaleModulators;
};
/**
* @brief Convert external instrument to internal instrument
*/
void cvt_ADLI_to_FMIns(OplInstMeta &dst, const struct ADL_Instrument &src);
/**
* @brief Convert internal instrument to external instrument
*/
void cvt_FMIns_to_ADLI(struct ADL_Instrument &dst, const OplInstMeta &src);
#endif // OPLINST_H

View file

@ -0,0 +1,133 @@
// Copyright Jean Pierre Cimalando 2018.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef PL_LIST_HPP
#define PL_LIST_HPP
#include <iterator>
#include <cstddef>
/*
pl_cell: the linked list cell
*/
template <class T>
struct pl_cell;
template <class T>
struct pl_basic_cell
{
pl_cell<T> *prev, *next;
};
template <class T>
struct pl_cell : pl_basic_cell<T>
{
T value;
};
/*
pl_iterator: the linked list iterator
*/
template <class Cell>
class pl_iterator
{
public:
typedef std::bidirectional_iterator_tag iterator_category;
typedef Cell value_type;
typedef Cell &reference;
typedef Cell *pointer;
typedef std::ptrdiff_t difference_type;
pl_iterator(Cell *cell = NULL);
bool is_end() const;
Cell &operator*() const;
Cell *operator->() const;
bool operator==(const pl_iterator &i) const;
bool operator!=(const pl_iterator &i) const;
pl_iterator &operator++();
pl_iterator operator++(int);
pl_iterator &operator--();
pl_iterator operator--(int);
private:
Cell *cell_;
};
/*
pl_list: the preallocated linked list
*/
template <class T>
class pl_list
{
public:
typedef pl_cell<T> value_type;
typedef value_type *pointer;
typedef value_type &reference;
typedef const value_type *const_pointer;
typedef const value_type &const_reference;
typedef pl_iterator< pl_cell<T> > iterator;
typedef pl_iterator< const pl_cell<T> > const_iterator;
pl_list(std::size_t capacity = 0);
~pl_list();
struct external_storage_policy {};
pl_list(pl_cell<T> *cells, std::size_t ncells, external_storage_policy);
pl_list(const pl_list &other);
pl_list &operator=(const pl_list &other);
std::size_t size() const;
std::size_t capacity() const;
bool empty() const;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
void clear();
pl_cell<T> &front();
const pl_cell<T> &front() const;
pl_cell<T> &back();
const pl_cell<T> &back() const;
iterator insert(iterator pos, const T &x);
iterator erase(iterator pos);
void push_front(const T &x);
void push_back(const T &x);
void pop_front();
void pop_back();
iterator find(const T &x);
const_iterator find(const T &x) const;
template <class Pred> iterator find_if(const Pred &p);
template <class Pred> const_iterator find_if(const Pred &p) const;
private:
// number of cells in the list
std::size_t size_;
// number of cells allocated
std::size_t capacity_;
// array of cells allocated
pl_cell<T> *cells_;
// pointer to the head cell
pl_cell<T> *first_;
// pointer to the next free cell
pl_cell<T> *free_;
// value-less cell which terminates the linked list
pl_basic_cell<T> endcell_;
// whether cell storage is allocated
bool cells_allocd_;
void initialize(std::size_t capacity, pl_cell<T> *extcells = NULL);
pl_cell<T> *allocate(pl_cell<T> *pos);
void deallocate(pl_cell<T> *cell);
};
#include "pl_list.tcc"
#endif // PL_LIST_HPP

View file

@ -0,0 +1,338 @@
// Copyright Jean Pierre Cimalando 2018.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include "pl_list.hpp"
template <class Cell>
pl_iterator<Cell>::pl_iterator(Cell *cell)
: cell_(cell)
{
}
template <class Cell>
bool pl_iterator<Cell>::is_end() const
{
return cell_->next == NULL;
}
template <class Cell>
Cell &pl_iterator<Cell>::operator*() const
{
return *cell_;
}
template <class Cell>
Cell *pl_iterator<Cell>::operator->() const
{
return cell_;
}
template <class T>
bool pl_iterator<T>::operator==(const pl_iterator &i) const
{
return cell_ == i.cell_;
}
template <class T>
bool pl_iterator<T>::operator!=(const pl_iterator &i) const
{
return cell_ != i.cell_;
}
template <class T>
pl_iterator<T> &pl_iterator<T>::operator++()
{
cell_ = cell_->next;
return *this;
}
template <class T>
pl_iterator<T> pl_iterator<T>::operator++(int)
{
pl_iterator i(cell_);
cell_ = cell_->next;
return i;
}
template <class T>
pl_iterator<T> &pl_iterator<T>::operator--()
{
cell_ = cell_->prev;
return *this;
}
template <class T>
pl_iterator<T> pl_iterator<T>::operator--(int)
{
pl_iterator i(cell_);
cell_ = cell_->prev;
return i;
}
template <class T>
pl_list<T>::pl_list(std::size_t capacity)
{
initialize(capacity);
}
template <class T>
pl_list<T>::~pl_list()
{
if (cells_allocd_)
delete[] cells_;
}
template <class T>
pl_list<T>::pl_list(pl_cell<T> *cells, std::size_t ncells, external_storage_policy)
{
initialize(ncells, cells);
}
template <class T>
pl_list<T>::pl_list(const pl_list &other)
{
initialize(other.capacity());
for(const_iterator i = other.end(), b = other.begin(); i-- != b;)
push_front(i->value);
}
template <class T>
pl_list<T> &pl_list<T>::operator=(const pl_list &other)
{
if(this != &other)
{
std::size_t size = other.size();
if(size > capacity())
{
pl_cell<T> *oldcells = cells_;
bool allocd = cells_allocd_;
initialize(other.capacity());
if (allocd)
delete[] oldcells;
}
clear();
for(const_iterator i = other.end(), b = other.begin(); i-- != b;)
push_front(i->value);
}
return *this;
}
template <class T>
std::size_t pl_list<T>::size() const
{
return size_;
}
template <class T>
std::size_t pl_list<T>::capacity() const
{
return capacity_;
}
template <class T>
bool pl_list<T>::empty() const
{
return size_ == 0;
}
template <class T>
typename pl_list<T>::iterator pl_list<T>::begin()
{
return iterator(first_);
}
template <class T>
typename pl_list<T>::iterator pl_list<T>::end()
{
return iterator(reinterpret_cast<pl_cell<T> *>(&endcell_));
}
template <class T>
typename pl_list<T>::const_iterator pl_list<T>::begin() const
{
return const_iterator(first_);
}
template <class T>
typename pl_list<T>::const_iterator pl_list<T>::end() const
{
return const_iterator(reinterpret_cast<const pl_cell<T> *>(&endcell_));
}
template <class T>
void pl_list<T>::clear()
{
std::size_t capacity = capacity_;
pl_cell<T> *cells = cells_;
pl_cell<T> *endcell = &*end();
size_ = 0;
first_ = endcell;
free_ = cells;
endcell->prev = NULL;
for(std::size_t i = 0; i < capacity; ++i)
{
cells[i].prev = (i > 0) ? &cells[i - 1] : NULL;
cells[i].next = (i + 1 < capacity) ? &cells[i + 1] : NULL;
cells[i].value = T();
}
}
template <class T>
pl_cell<T> &pl_list<T>::front()
{
return *first_;
}
template <class T>
const pl_cell<T> &pl_list<T>::front() const
{
return *first_;
}
template <class T>
pl_cell<T> &pl_list<T>::back()
{
iterator i = end();
return *--i;
}
template <class T>
const pl_cell<T> &pl_list<T>::back() const
{
const_iterator i = end();
return *--i;
}
template <class T>
typename pl_list<T>::iterator pl_list<T>::insert(iterator pos, const T &x)
{
pl_cell<T> *cell = allocate(&*pos);
if (!cell)
throw std::bad_alloc();
cell->value = x;
return iterator(cell);
}
template <class T>
typename pl_list<T>::iterator pl_list<T>::erase(iterator pos)
{
deallocate(&*(pos++));
return pos;
}
template <class T>
void pl_list<T>::push_front(const T &x)
{
insert(begin(), x);
}
template <class T>
void pl_list<T>::push_back(const T &x)
{
insert(end(), x);
}
template <class T>
void pl_list<T>::pop_front()
{
deallocate(first_);
}
template <class T>
void pl_list<T>::pop_back()
{
iterator i(&*end());
deallocate(&*--i);
}
template <class T>
typename pl_list<T>::iterator pl_list<T>::find(const T &x)
{
const_iterator i = const_cast<const pl_list<T> *>(this)->find(x);
return iterator(&const_cast<reference>(*i));
}
template <class T>
typename pl_list<T>::const_iterator pl_list<T>::find(const T &x) const
{
const_iterator e = end();
for (const_iterator i = begin(); i != e; ++i)
{
if(i->value == x)
return i;
}
return e;
}
template <class T>
template <class Pred>
typename pl_list<T>::iterator pl_list<T>::find_if(const Pred &p)
{
const_iterator i = const_cast<const pl_list<T> *>(this)->find_if(p);
return iterator(&const_cast<reference>(*i));
}
template <class T>
template <class Pred>
typename pl_list<T>::const_iterator pl_list<T>::find_if(const Pred &p) const
{
const_iterator e = end();
for (const_iterator i = begin(); i != e; ++i)
{
if(p(i->value))
return i;
}
return e;
}
template <class T>
void pl_list<T>::initialize(std::size_t capacity, pl_cell<T> *extcells)
{
cells_ = extcells ? extcells : new pl_cell<T>[capacity];
cells_allocd_ = extcells ? false : true;
capacity_ = capacity;
endcell_.next = NULL;
clear();
}
template <class T>
pl_cell<T> *pl_list<T>::allocate(pl_cell<T> *pos)
{
// remove free cells front
pl_cell<T> *cell = free_;
if(!cell)
return NULL;
free_ = cell->next;
if(free_)
free_->prev = NULL;
// insert at position
if (pos == first_)
first_ = cell;
cell->prev = pos->prev;
if (cell->prev)
cell->prev->next = cell;
cell->next = pos;
pos->prev = cell;
++size_;
return cell;
}
template <class T>
void pl_list<T>::deallocate(pl_cell<T> *cell)
{
if(cell->prev)
cell->prev->next = cell->next;
if(cell->next)
cell->next->prev = cell->prev;
if(cell == first_)
first_ = cell->next;
cell->prev = NULL;
cell->next = free_;
cell->value = T();
free_ = cell;
--size_;
}

View file

@ -1,7 +1,7 @@
/*
* Wohlstand's OPL3 Bank File - a bank format to store OPL3 timbre data and setup
*
* Copyright (c) 2015-2018 Vitaly Novichkov <admin@wohlnet.ru>
* Copyright (c) 2015-2020 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"),
@ -77,18 +77,30 @@ static void fromSint16BE(int16_t in, uint8_t *arr)
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));
WOPLFile *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 );
file->banks_count_melodic = (melodic_banks != 0) ? melodic_banks : 1;
file->banks_melodic = (WOPLBank*)calloc(file->banks_count_melodic, sizeof(WOPLBank));
if(melodic_banks == 0)
{
unsigned i;
for(i = 0; i < 128; ++i)
file->banks_melodic[0].ins[i].inst_flags = WOPL_Ins_IsBlank;
}
file->banks_count_percussion = (percussive_banks != 0) ? percussive_banks : 1;
file->banks_percussive = (WOPLBank*)calloc(file->banks_count_percussion, sizeof(WOPLBank));
if(percussive_banks == 0)
{
unsigned i;
for(i = 0; i < 128; ++i)
file->banks_percussive[0].ins[i].inst_flags = WOPL_Ins_IsBlank;
}
return file;
}
@ -161,7 +173,8 @@ static void WOPL_parseInstrument(WOPLInstrument *ins, uint8_t *cursor, uint16_t
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);
memcpy((char*)cursor, ins->inst_name, 32);
cursor[32] = '\0';
fromSint16BE(ins->note_offset1, cursor + 32);
fromSint16BE(ins->note_offset2, cursor + 34);
cursor[36] = (uint8_t)ins->midi_velocity_offset;
@ -266,7 +279,7 @@ WOPLFile *WOPL_LoadBankFromMem(void *mem, size_t length, int *error)
outFile->version = version;
outFile->opl_flags = head[4];
outFile->volume_model = head[5];
}
}
bankslots_sizes[0] = count_melodic_banks;
bankslots[0] = outFile->banks_melodic;

View file

@ -1,7 +1,7 @@
/*
* Wohlstand's OPL3 Bank File - a bank format to store OPL3 timbre data and setup
*
* Copyright (c) 2015-2018 Vitaly Novichkov <admin@wohlnet.ru>
* Copyright (c) 2015-2020 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"),
@ -56,7 +56,12 @@ typedef enum WOPL_VolumeModel
WOPL_VM_Native,
WOPL_VM_DMX,
WOPL_VM_Apogee,
WOPL_VM_Win9x
WOPL_VM_Win9x,
WOPL_VM_DMX_Fixed,
WOPL_VM_Apogee_Fixed,
WOPL_VM_AIL,
WOPL_VM_Win9x_GenericFM,
WOPL_VM_HMI
} WOPL_VolumeModel;
typedef enum WOPL_InstrumentFlags
@ -242,7 +247,7 @@ 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 file Pointer to destination 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
@ -252,7 +257,7 @@ 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
* @param version Destination version of the file
* @return Size of the raw WOPL file data
*/
extern size_t WOPL_CalculateBankFileSize(WOPLFile *file, uint16_t version);
@ -260,7 +265,7 @@ 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
* @param version Destination version of the file
* @return Size of the raw WOPI file data
*/
extern size_t WOPL_CalculateInstFileSize(WOPIFile *file, uint16_t version);
@ -268,8 +273,8 @@ 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 dest_mem Destination memory block pointer
* @param length Length of destination memory block
* @param version Wanted WOPL version
* @param force_gm Force GM set in saved bank file
* @return Error code or 0 on success
@ -279,8 +284,8 @@ extern int WOPL_SaveBankToMem(WOPLFile *file, void *dest_mem, size_t length, uin
/**
* @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 dest_mem Destination memory block pointer
* @param length Length of destination memory block
* @param version Wanted WOPI version
* @return Error code or 0 on success
*/