- made libadl its own library subproject.
This is to improve compile times because the MSVC compiler tends to become slow with large lists of source files in a single project. This new project is still our stripped down copy of libadl, not the original, because that project contains a large amount of baggage we do not need.
This commit is contained in:
parent
2a642c66be
commit
84cc7cbdd2
35 changed files with 33 additions and 17 deletions
|
|
@ -468,7 +468,7 @@ set( ZDOOM_LIBS ${ZDOOM_LIBS} "${ZLIB_LIBRARIES}" "${JPEG_LIBRARIES}" "${BZIP2_L
|
|||
if (HAVE_VULKAN)
|
||||
set( ZDOOM_LIBS ${ZDOOM_LIBS} "glslang" "SPIRV" "OGLCompiler")
|
||||
endif()
|
||||
include_directories( "${ZLIB_INCLUDE_DIR}" "${BZIP2_INCLUDE_DIR}" "${LZMA_INCLUDE_DIR}" "${JPEG_INCLUDE_DIR}" "${GME_INCLUDE_DIR}" )
|
||||
include_directories( "${ZLIB_INCLUDE_DIR}" "${BZIP2_INCLUDE_DIR}" "${LZMA_INCLUDE_DIR}" "${JPEG_INCLUDE_DIR}" "${GME_INCLUDE_DIR}" "${ADL_INCLUDE_DIR}" )
|
||||
|
||||
if( ${HAVE_VM_JIT} )
|
||||
add_definitions( -DHAVE_VM_JIT )
|
||||
|
|
@ -677,7 +677,6 @@ file( GLOB HEADER_FILES
|
|||
maploader/*.h
|
||||
menu/*.h
|
||||
sound/*.h
|
||||
sound/adlmidi/*.h*
|
||||
sound/backend/*.h*
|
||||
sound/music/*.h*
|
||||
sound/opnmidi/*.h*
|
||||
|
|
@ -885,19 +884,6 @@ set( FASTMATH_SOURCES
|
|||
rendering/hwrenderer/scene/hw_weapon.cpp
|
||||
r_data/models/models.cpp
|
||||
utility/matrix.cpp
|
||||
sound/adlmidi/adldata.cpp
|
||||
sound/adlmidi/adlmidi.cpp
|
||||
sound/adlmidi/adlmidi_load.cpp
|
||||
sound/adlmidi/adlmidi_midiplay.cpp
|
||||
sound/adlmidi/adlmidi_opl3.cpp
|
||||
sound/adlmidi/adlmidi_private.cpp
|
||||
sound/adlmidi/chips/dosbox/dbopl.cpp
|
||||
sound/adlmidi/chips/dosbox_opl3.cpp
|
||||
sound/adlmidi/chips/nuked/nukedopl3_174.c
|
||||
sound/adlmidi/chips/nuked/nukedopl3.c
|
||||
sound/adlmidi/chips/nuked_opl3.cpp
|
||||
sound/adlmidi/chips/nuked_opl3_v174.cpp
|
||||
sound/adlmidi/wopl/wopl_file.c
|
||||
sound/opnmidi/chips/gens_opn2.cpp
|
||||
sound/opnmidi/chips/gens/Ym2612_Emu.cpp
|
||||
sound/opnmidi/chips/mame/mame_ym2612fm.c
|
||||
|
|
@ -1385,7 +1371,7 @@ if( UNIX )
|
|||
endif()
|
||||
endif()
|
||||
|
||||
target_link_libraries( zdoom ${ZDOOM_LIBS} gdtoa dumb lzma )
|
||||
target_link_libraries( zdoom ${ZDOOM_LIBS} gdtoa dumb lzma adl )
|
||||
|
||||
include_directories( .
|
||||
g_statusbar
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||
|
||||
|
|
@ -1,127 +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_BANKMAP_H
|
||||
#define ADLMIDI_BANKMAP_H
|
||||
|
||||
#include <list>
|
||||
#include <utility>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "adlmidi_ptr.hpp"
|
||||
|
||||
/**
|
||||
* A simple hash map which accepts bank numbers as keys, can be reserved to a
|
||||
* fixed size, offers O(1) search and insertion, has a hash function to
|
||||
* optimize for the worst case, and has some good cache locality properties.
|
||||
*/
|
||||
template <class T>
|
||||
class BasicBankMap
|
||||
{
|
||||
public:
|
||||
typedef size_t key_type; /* the bank identifier */
|
||||
typedef T mapped_type;
|
||||
typedef std::pair<key_type, T> value_type;
|
||||
|
||||
BasicBankMap();
|
||||
void reserve(size_t capacity);
|
||||
|
||||
size_t size() const
|
||||
{ return m_size; }
|
||||
size_t capacity() const
|
||||
{ return m_capacity; }
|
||||
bool empty() const
|
||||
{ return m_size == 0; }
|
||||
|
||||
class iterator;
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
|
||||
struct do_not_expand_t {};
|
||||
|
||||
iterator find(key_type key);
|
||||
void erase(iterator it);
|
||||
std::pair<iterator, bool> insert(const value_type &value);
|
||||
std::pair<iterator, bool> insert(const value_type &value, do_not_expand_t);
|
||||
void clear();
|
||||
|
||||
T &operator[](key_type key);
|
||||
|
||||
private:
|
||||
struct Slot;
|
||||
enum { minimum_allocation = 4 };
|
||||
enum
|
||||
{
|
||||
hash_bits = 8, /* worst case # of collisions: 128^2/2^hash_bits */
|
||||
hash_buckets = 1 << hash_bits
|
||||
};
|
||||
|
||||
public:
|
||||
class iterator
|
||||
{
|
||||
public:
|
||||
iterator();
|
||||
value_type &operator*() const { return slot->value; }
|
||||
value_type *operator->() const { return &slot->value; }
|
||||
iterator &operator++();
|
||||
bool operator==(const iterator &o) const;
|
||||
bool operator!=(const iterator &o) const;
|
||||
void to_ptrs(void *ptrs[3]);
|
||||
static iterator from_ptrs(void *const ptrs[3]);
|
||||
private:
|
||||
Slot **buckets;
|
||||
Slot *slot;
|
||||
size_t index;
|
||||
iterator(Slot **buckets, Slot *slot, size_t index);
|
||||
#ifdef _MSC_VER
|
||||
template<class _T>
|
||||
friend class BasicBankMap;
|
||||
#else
|
||||
friend class BasicBankMap<T>;
|
||||
#endif
|
||||
};
|
||||
|
||||
private:
|
||||
struct Slot {
|
||||
Slot *next, *prev;
|
||||
value_type value;
|
||||
Slot() : next(NULL), prev(NULL) {}
|
||||
};
|
||||
AdlMIDI_SPtrArray<Slot *> m_buckets;
|
||||
std::list< AdlMIDI_SPtrArray<Slot> > m_allocations;
|
||||
Slot *m_freeslots;
|
||||
size_t m_size;
|
||||
size_t m_capacity;
|
||||
static size_t hash(key_type key);
|
||||
Slot *allocate_slot();
|
||||
Slot *ensure_allocate_slot();
|
||||
void free_slot(Slot *slot);
|
||||
Slot *bucket_find(size_t index, key_type key);
|
||||
void bucket_add(size_t index, Slot *slot);
|
||||
void bucket_remove(size_t index, Slot *slot);
|
||||
};
|
||||
|
||||
#include "adlmidi_bankmap.tcc"
|
||||
|
||||
#endif // ADLMIDI_BANKMAP_H
|
||||
|
|
@ -1,283 +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/>.
|
||||
*/
|
||||
|
||||
#include "adlmidi_bankmap.h"
|
||||
#include <cassert>
|
||||
|
||||
template <class T>
|
||||
inline BasicBankMap<T>::BasicBankMap()
|
||||
: m_freeslots(NULL),
|
||||
m_size(0),
|
||||
m_capacity(0)
|
||||
{
|
||||
m_buckets.reset(new Slot *[hash_buckets]());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline size_t BasicBankMap<T>::hash(key_type key)
|
||||
{
|
||||
// disregard the 0 high bit in LSB
|
||||
key = key_type(key & 127) | key_type((key >> 8) << 7);
|
||||
// take low part as hash value
|
||||
return key & (hash_buckets - 1);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void BasicBankMap<T>::reserve(size_t capacity)
|
||||
{
|
||||
if(m_capacity >= capacity)
|
||||
return;
|
||||
|
||||
size_t need = capacity - m_capacity;
|
||||
const size_t minalloc = static_cast<size_t>(minimum_allocation);
|
||||
need = (need < minalloc) ? minalloc : need;
|
||||
|
||||
AdlMIDI_SPtrArray<Slot> slotz;
|
||||
slotz.reset(new Slot[need]);
|
||||
m_allocations.push_back(slotz);
|
||||
m_capacity += need;
|
||||
|
||||
for(size_t i = need; i-- > 0;)
|
||||
free_slot(&slotz[i]);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
typename BasicBankMap<T>::iterator
|
||||
BasicBankMap<T>::begin() const
|
||||
{
|
||||
iterator it(m_buckets.get(), NULL, 0);
|
||||
while(it.index < hash_buckets && !(it.slot = m_buckets[it.index]))
|
||||
++it.index;
|
||||
return it;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
typename BasicBankMap<T>::iterator
|
||||
BasicBankMap<T>::end() const
|
||||
{
|
||||
iterator it(m_buckets.get(), NULL, hash_buckets);
|
||||
return it;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
typename BasicBankMap<T>::iterator BasicBankMap<T>::find(key_type key)
|
||||
{
|
||||
size_t index = hash(key);
|
||||
Slot *slot = bucket_find(index, key);
|
||||
if(!slot)
|
||||
return end();
|
||||
return iterator(m_buckets.get(), slot, index);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void BasicBankMap<T>::erase(iterator it)
|
||||
{
|
||||
bucket_remove(it.index, it.slot);
|
||||
free_slot(it.slot);
|
||||
--m_size;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline BasicBankMap<T>::iterator::iterator()
|
||||
: buckets(NULL), slot(NULL), index(0)
|
||||
{
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline BasicBankMap<T>::iterator::iterator(Slot **buckets, Slot *slot, size_t index)
|
||||
: buckets(buckets), slot(slot), index(index)
|
||||
{
|
||||
}
|
||||
|
||||
template <class T>
|
||||
typename BasicBankMap<T>::iterator &
|
||||
BasicBankMap<T>::iterator::operator++()
|
||||
{
|
||||
if(slot->next)
|
||||
slot = slot->next;
|
||||
else {
|
||||
Slot *slot = NULL;
|
||||
++index;
|
||||
while(index < hash_buckets && !(slot = buckets[index]))
|
||||
++index;
|
||||
this->slot = slot;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool BasicBankMap<T>::iterator::operator==(const iterator &o) const
|
||||
{
|
||||
return buckets == o.buckets && slot == o.slot && index == o.index;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool BasicBankMap<T>::iterator::operator!=(const iterator &o) const
|
||||
{
|
||||
return !operator==(o);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void BasicBankMap<T>::iterator::to_ptrs(void *ptrs[3])
|
||||
{
|
||||
ptrs[0] = buckets;
|
||||
ptrs[1] = slot;
|
||||
ptrs[2] = (void *)index;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
typename BasicBankMap<T>::iterator
|
||||
BasicBankMap<T>::iterator::from_ptrs(void *const ptrs[3])
|
||||
{
|
||||
iterator it;
|
||||
it.buckets = (Slot **)ptrs[0];
|
||||
it.slot = (Slot *)ptrs[1];
|
||||
it.index = (size_t)ptrs[2];
|
||||
return it;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::pair<typename BasicBankMap<T>::iterator, bool>
|
||||
BasicBankMap<T>::insert(const value_type &value)
|
||||
{
|
||||
size_t index = hash(value.first);
|
||||
Slot *slot = bucket_find(index, value.first);
|
||||
if(slot)
|
||||
return std::make_pair(iterator(m_buckets.get(), slot, index), false);
|
||||
slot = allocate_slot();
|
||||
if(!slot) {
|
||||
reserve(m_capacity + minimum_allocation);
|
||||
slot = ensure_allocate_slot();
|
||||
}
|
||||
slot->value = value;
|
||||
bucket_add(index, slot);
|
||||
++m_size;
|
||||
return std::make_pair(iterator(m_buckets.get(), slot, index), true);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::pair<typename BasicBankMap<T>::iterator, bool>
|
||||
BasicBankMap<T>::insert(const value_type &value, do_not_expand_t)
|
||||
{
|
||||
size_t index = hash(value.first);
|
||||
Slot *slot = bucket_find(index, value.first);
|
||||
if(slot)
|
||||
return std::make_pair(iterator(m_buckets.get(), slot, index), false);
|
||||
slot = allocate_slot();
|
||||
if(!slot)
|
||||
return std::make_pair(end(), false);
|
||||
slot->value = value;
|
||||
bucket_add(index, slot);
|
||||
++m_size;
|
||||
return std::make_pair(iterator(m_buckets.get(), slot, index), true);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void BasicBankMap<T>::clear()
|
||||
{
|
||||
for(size_t i = 0; i < hash_buckets; ++i) {
|
||||
Slot *slot = m_buckets[i];
|
||||
while (Slot *cur = slot) {
|
||||
slot = slot->next;
|
||||
free_slot(cur);
|
||||
}
|
||||
m_buckets[i] = NULL;
|
||||
}
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T &BasicBankMap<T>::operator[](key_type key)
|
||||
{
|
||||
return insert(value_type(key, T())).first->second;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
typename BasicBankMap<T>::Slot *
|
||||
BasicBankMap<T>::allocate_slot()
|
||||
{
|
||||
Slot *slot = m_freeslots;
|
||||
if(!slot)
|
||||
return NULL;
|
||||
Slot *next = slot->next;
|
||||
if(next)
|
||||
next->prev = NULL;
|
||||
m_freeslots = next;
|
||||
return slot;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline typename BasicBankMap<T>::Slot *
|
||||
BasicBankMap<T>::ensure_allocate_slot()
|
||||
{
|
||||
Slot *slot = allocate_slot();
|
||||
assert(slot);
|
||||
return slot;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void BasicBankMap<T>::free_slot(Slot *slot)
|
||||
{
|
||||
Slot *next = m_freeslots;
|
||||
if(next)
|
||||
next->prev = slot;
|
||||
slot->prev = NULL;
|
||||
slot->next = next;
|
||||
m_freeslots = slot;
|
||||
m_freeslots->value.second = T();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
typename BasicBankMap<T>::Slot *
|
||||
BasicBankMap<T>::bucket_find(size_t index, key_type key)
|
||||
{
|
||||
Slot *slot = m_buckets[index];
|
||||
while(slot && slot->value.first != key)
|
||||
slot = slot->next;
|
||||
return slot;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void BasicBankMap<T>::bucket_add(size_t index, Slot *slot)
|
||||
{
|
||||
assert(slot);
|
||||
Slot *next = m_buckets[index];
|
||||
if(next)
|
||||
next->prev = slot;
|
||||
slot->next = next;
|
||||
m_buckets[index] = slot;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void BasicBankMap<T>::bucket_remove(size_t index, Slot *slot)
|
||||
{
|
||||
assert(slot);
|
||||
Slot *prev = slot->prev;
|
||||
Slot *next = slot->next;
|
||||
if(!prev)
|
||||
m_buckets[index] = next;
|
||||
else
|
||||
prev->next = next;
|
||||
if(next)
|
||||
next->prev = prev;
|
||||
}
|
||||
|
|
@ -1,124 +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/>.
|
||||
*/
|
||||
|
||||
#include "adldata.hh"
|
||||
#include "wopl/wopl_file.h"
|
||||
#include <cmath>
|
||||
|
||||
template <class WOPLI>
|
||||
static void cvt_generic_to_FMIns(adlinsdata2 &ins, const WOPLI &in)
|
||||
{
|
||||
ins.voice2_fine_tune = 0.0;
|
||||
int8_t voice2_fine_tune = in.second_voice_detune;
|
||||
if(voice2_fine_tune != 0)
|
||||
{
|
||||
if(voice2_fine_tune == 1)
|
||||
ins.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);
|
||||
}
|
||||
|
||||
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.flags|= in.inst_flags & WOPL_RhythmModeMask;
|
||||
|
||||
for(size_t op = 0, slt = 0; op < 4; op++, slt++)
|
||||
{
|
||||
ins.adl[slt].carrier_E862 =
|
||||
((static_cast<uint32_t>(in.operators[op].waveform_E0) << 24) & 0xFF000000) //WaveForm
|
||||
| ((static_cast<uint32_t>(in.operators[op].susrel_80) << 16) & 0x00FF0000) //SusRel
|
||||
| ((static_cast<uint32_t>(in.operators[op].atdec_60) << 8) & 0x0000FF00) //AtDec
|
||||
| ((static_cast<uint32_t>(in.operators[op].avekf_20) << 0) & 0x000000FF); //AVEKM
|
||||
ins.adl[slt].carrier_40 = in.operators[op].ksl_l_40;//KSLL
|
||||
|
||||
op++;
|
||||
ins.adl[slt].modulator_E862 =
|
||||
((static_cast<uint32_t>(in.operators[op].waveform_E0) << 24) & 0xFF000000) //WaveForm
|
||||
| ((static_cast<uint32_t>(in.operators[op].susrel_80) << 16) & 0x00FF0000) //SusRel
|
||||
| ((static_cast<uint32_t>(in.operators[op].atdec_60) << 8) & 0x0000FF00) //AtDec
|
||||
| ((static_cast<uint32_t>(in.operators[op].avekf_20) << 0) & 0x000000FF); //AVEKM
|
||||
ins.adl[slt].modulator_40 = in.operators[op].ksl_l_40;//KSLL
|
||||
}
|
||||
|
||||
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.ms_sound_kon = in.delay_on_ms;
|
||||
ins.ms_sound_koff = in.delay_off_ms;
|
||||
}
|
||||
|
||||
template <class WOPLI>
|
||||
static void cvt_FMIns_to_generic(WOPLI &ins, const adlinsdata2 &in)
|
||||
{
|
||||
ins.second_voice_detune = 0;
|
||||
double voice2_fine_tune = in.voice2_fine_tune;
|
||||
if(voice2_fine_tune != 0)
|
||||
{
|
||||
if(voice2_fine_tune > 0 && voice2_fine_tune <= 0.000025)
|
||||
ins.second_voice_detune = 1;
|
||||
else if(voice2_fine_tune < 0 && voice2_fine_tune >= -0.000025)
|
||||
ins.second_voice_detune = -1;
|
||||
else
|
||||
{
|
||||
long value = 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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
for(size_t op = 0; op < 4; op++)
|
||||
{
|
||||
const adldata &in2op = in.adl[(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;
|
||||
|
||||
ins.operators[op].waveform_E0 = static_cast<uint8_t>(regE862 >> 24);
|
||||
ins.operators[op].susrel_80 = static_cast<uint8_t>(regE862 >> 16);
|
||||
ins.operators[op].atdec_60 = static_cast<uint8_t>(regE862 >> 8);
|
||||
ins.operators[op].avekf_20 = static_cast<uint8_t>(regE862 >> 0);
|
||||
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.delay_on_ms = in.ms_sound_kon;
|
||||
ins.delay_off_ms = in.ms_sound_koff;
|
||||
}
|
||||
|
|
@ -1,287 +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/>.
|
||||
*/
|
||||
|
||||
#include "adlmidi_private.hpp"
|
||||
#include "adlmidi_cvt.hpp"
|
||||
#include "wopl/wopl_file.h"
|
||||
|
||||
bool MIDIplay::LoadBank(const std::string &filename)
|
||||
{
|
||||
FileAndMemReader file;
|
||||
file.openFile(filename.c_str());
|
||||
return LoadBank(file);
|
||||
}
|
||||
|
||||
bool MIDIplay::LoadBank(const void *data, size_t size)
|
||||
{
|
||||
FileAndMemReader file;
|
||||
file.openData(data, size);
|
||||
return LoadBank(file);
|
||||
}
|
||||
|
||||
void cvt_ADLI_to_FMIns(adlinsdata2 &ins, const ADL_Instrument &in)
|
||||
{
|
||||
return cvt_generic_to_FMIns(ins, in);
|
||||
}
|
||||
|
||||
void cvt_FMIns_to_ADLI(ADL_Instrument &ins, const adlinsdata2 &in)
|
||||
{
|
||||
cvt_FMIns_to_generic(ins, in);
|
||||
}
|
||||
|
||||
bool MIDIplay::LoadBank(FileAndMemReader &fr)
|
||||
{
|
||||
int err = 0;
|
||||
WOPLFile *wopl = NULL;
|
||||
char *raw_file_data = NULL;
|
||||
size_t fsize;
|
||||
if(!fr.isValid())
|
||||
{
|
||||
errorStringOut = "Custom bank: Invalid data stream!";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read complete bank file into the memory
|
||||
fsize = fr.fileSize();
|
||||
fr.seek(0, FileAndMemReader::SET);
|
||||
// Allocate necessary memory block
|
||||
raw_file_data = (char*)malloc(fsize);
|
||||
if(!raw_file_data)
|
||||
{
|
||||
errorStringOut = "Custom bank: Out of memory before of read!";
|
||||
return false;
|
||||
}
|
||||
fr.read(raw_file_data, 1, fsize);
|
||||
|
||||
// Parse bank file from the memory
|
||||
wopl = WOPL_LoadBankFromMem((void*)raw_file_data, fsize, &err);
|
||||
//Free the buffer no more needed
|
||||
free(raw_file_data);
|
||||
|
||||
// Check for any erros
|
||||
if(!wopl)
|
||||
{
|
||||
switch(err)
|
||||
{
|
||||
case WOPL_ERR_BAD_MAGIC:
|
||||
errorStringOut = "Custom bank: Invalid magic!";
|
||||
return false;
|
||||
case WOPL_ERR_UNEXPECTED_ENDING:
|
||||
errorStringOut = "Custom bank: Unexpected ending!";
|
||||
return false;
|
||||
case WOPL_ERR_INVALID_BANKS_COUNT:
|
||||
errorStringOut = "Custom bank: Invalid banks count!";
|
||||
return false;
|
||||
case WOPL_ERR_NEWER_VERSION:
|
||||
errorStringOut = "Custom bank: Version is newer than supported by this library!";
|
||||
return false;
|
||||
case WOPL_ERR_OUT_OF_MEMORY:
|
||||
errorStringOut = "Custom bank: Out of memory!";
|
||||
return false;
|
||||
default:
|
||||
errorStringOut = "Custom bank: Unknown error!";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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 };
|
||||
|
||||
for(size_t ss = 0; ss < 2; ss++)
|
||||
{
|
||||
for(size_t i = 0; i < slots_counts[ss]; i++)
|
||||
{
|
||||
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];
|
||||
for(int j = 0; j < 128; j++)
|
||||
{
|
||||
adlinsdata2 &ins = bank.ins[j];
|
||||
std::memset(&ins, 0, sizeof(adlinsdata2));
|
||||
WOPLInstrument &inIns = slots_src_ins[ss][i].ins[j];
|
||||
cvt_generic_to_FMIns(ins, inIns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_synth.m_embeddedBank = OPL3::CustomBankTag; // Use dynamic banks!
|
||||
//Percussion offset is count of instruments multipled to count of melodic banks
|
||||
applySetup();
|
||||
|
||||
WOPL_Free(wopl);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
|
||||
|
||||
bool MIDIplay::LoadMIDI_pre()
|
||||
{
|
||||
#ifdef DISABLE_EMBEDDED_BANKS
|
||||
if((m_synth.m_embeddedBank != OPL3::CustomBankTag) || m_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;
|
||||
}
|
||||
#endif
|
||||
/**** Set all properties BEFORE starting of actial file reading! ****/
|
||||
resetMIDI();
|
||||
applySetup();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MIDIplay::LoadMIDI_post()
|
||||
{
|
||||
MidiSequencer::FileFormat format = m_sequencer.getFormat();
|
||||
if(format == MidiSequencer::Format_CMF)
|
||||
{
|
||||
const std::vector<MidiSequencer::CmfInstrument> &instruments = m_sequencer.getRawCmfInstruments();
|
||||
m_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;
|
||||
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);
|
||||
|
||||
/*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;
|
||||
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
|
||||
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;
|
||||
adlins.flags = 0;
|
||||
adlins.voice2_fine_tune = 0.0;
|
||||
}
|
||||
|
||||
m_synth.m_embeddedBank = OPL3::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;
|
||||
|
||||
m_synth.m_numChips = 1;
|
||||
m_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;
|
||||
|
||||
m_synth.m_numChips = 1;
|
||||
m_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;
|
||||
|
||||
m_synth.m_numChips = 1;
|
||||
m_synth.m_numFourOps = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_synth.m_numChips = m_setup.numChips;
|
||||
if(m_setup.numFourOps < 0)
|
||||
adlCalculateFourOpChannels(this, true);
|
||||
}
|
||||
|
||||
m_setup.tick_skip_samples_delay = 0;
|
||||
m_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);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MIDIplay::LoadMIDI(const std::string &filename)
|
||||
{
|
||||
FileAndMemReader file;
|
||||
file.openFile(filename.c_str());
|
||||
if(!LoadMIDI_pre())
|
||||
return false;
|
||||
if(!m_sequencer.loadMIDI(file))
|
||||
{
|
||||
errorStringOut = m_sequencer.getErrorString();
|
||||
return false;
|
||||
}
|
||||
if(!LoadMIDI_post())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MIDIplay::LoadMIDI(const void *data, size_t size)
|
||||
{
|
||||
FileAndMemReader file;
|
||||
file.openData(data, size);
|
||||
if(!LoadMIDI_pre())
|
||||
return false;
|
||||
if(!m_sequencer.loadMIDI(file))
|
||||
{
|
||||
errorStringOut = m_sequencer.getErrorString();
|
||||
return false;
|
||||
}
|
||||
if(!LoadMIDI_post())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,753 +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/>.
|
||||
*/
|
||||
|
||||
#include "adlmidi_private.hpp"
|
||||
#include <stdlib.h>
|
||||
#include <cassert>
|
||||
|
||||
#ifdef ADLMIDI_HW_OPL
|
||||
static const unsigned OPLBase = 0x388;
|
||||
#else
|
||||
# if defined(ADLMIDI_DISABLE_NUKED_EMULATOR) && defined(ADLMIDI_DISABLE_DOSBOX_EMULATOR)
|
||||
# error "No emulators enabled. You must enable at least one emulator to use this library!"
|
||||
# endif
|
||||
|
||||
// Nuked OPL3 emulator, Most accurate, but requires the powerful CPU
|
||||
# ifndef ADLMIDI_DISABLE_NUKED_EMULATOR
|
||||
# include "chips/nuked_opl3.h"
|
||||
# include "chips/nuked_opl3_v174.h"
|
||||
# endif
|
||||
|
||||
// DosBox 0.74 OPL3 emulator, Well-accurate and fast
|
||||
# ifndef ADLMIDI_DISABLE_DOSBOX_EMULATOR
|
||||
# include "chips/dosbox_opl3.h"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static const unsigned adl_emulatorSupport = 0
|
||||
#ifndef ADLMIDI_HW_OPL
|
||||
# ifndef ADLMIDI_DISABLE_NUKED_EMULATOR
|
||||
| (1u << ADLMIDI_EMU_NUKED) | (1u << ADLMIDI_EMU_NUKED_174)
|
||||
# endif
|
||||
|
||||
# ifndef ADLMIDI_DISABLE_DOSBOX_EMULATOR
|
||||
| (1u << ADLMIDI_EMU_DOSBOX)
|
||||
# endif
|
||||
#endif
|
||||
;
|
||||
|
||||
//! Check emulator availability
|
||||
bool adl_isEmulatorAvailable(int emulator)
|
||||
{
|
||||
return (adl_emulatorSupport & (1u << (unsigned)emulator)) != 0;
|
||||
}
|
||||
|
||||
//! Find highest emulator
|
||||
int adl_getHighestEmulator()
|
||||
{
|
||||
int emu = -1;
|
||||
for(unsigned m = adl_emulatorSupport; m > 0; m >>= 1)
|
||||
++emu;
|
||||
return emu;
|
||||
}
|
||||
|
||||
//! Find lowest emulator
|
||||
int adl_getLowestEmulator()
|
||||
{
|
||||
int emu = -1;
|
||||
unsigned m = adl_emulatorSupport;
|
||||
if(m > 0)
|
||||
{
|
||||
for(emu = 0; (m & 1) == 0; m >>= 1)
|
||||
++emu;
|
||||
}
|
||||
return emu;
|
||||
}
|
||||
|
||||
//! Per-channel and per-operator registers map
|
||||
static const uint16_t g_operatorsMap[23 * 2] =
|
||||
{
|
||||
// Channels 0-2
|
||||
0x000, 0x003, 0x001, 0x004, 0x002, 0x005, // operators 0, 3, 1, 4, 2, 5
|
||||
// Channels 3-5
|
||||
0x008, 0x00B, 0x009, 0x00C, 0x00A, 0x00D, // operators 6, 9, 7,10, 8,11
|
||||
// Channels 6-8
|
||||
0x010, 0x013, 0x011, 0x014, 0x012, 0x015, // operators 12,15, 13,16, 14,17
|
||||
// Same for second card
|
||||
0x100, 0x103, 0x101, 0x104, 0x102, 0x105, // operators 18,21, 19,22, 20,23
|
||||
0x108, 0x10B, 0x109, 0x10C, 0x10A, 0x10D, // operators 24,27, 25,28, 26,29
|
||||
0x110, 0x113, 0x111, 0x114, 0x112, 0x115, // operators 30,33, 31,34, 32,35
|
||||
// Channel 18
|
||||
0x010, 0x013, // operators 12,15
|
||||
// Channel 19
|
||||
0x014, 0xFFF, // operator 16
|
||||
// Channel 19
|
||||
0x012, 0xFFF, // operator 14
|
||||
// Channel 19
|
||||
0x015, 0xFFF, // operator 17
|
||||
// Channel 19
|
||||
0x011, 0xFFF
|
||||
}; // operator 13
|
||||
|
||||
//! Channel map to regoster offsets
|
||||
static const uint16_t g_channelsMap[23] =
|
||||
{
|
||||
0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008, // 0..8
|
||||
0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, // 9..17 (secondary set)
|
||||
0x006, 0x007, 0x008, 0xFFF, 0xFFF
|
||||
}; // <- hw percussions, 0xFFF = no support for pitch/pan
|
||||
|
||||
/*
|
||||
In OPL3 mode:
|
||||
0 1 2 6 7 8 9 10 11 16 17 18
|
||||
op0 op1 op2 op12 op13 op14 op18 op19 op20 op30 op31 op32
|
||||
op3 op4 op5 op15 op16 op17 op21 op22 op23 op33 op34 op35
|
||||
3 4 5 13 14 15
|
||||
op6 op7 op8 op24 op25 op26
|
||||
op9 op10 op11 op27 op28 op29
|
||||
Ports:
|
||||
+0 +1 +2 +10 +11 +12 +100 +101 +102 +110 +111 +112
|
||||
+3 +4 +5 +13 +14 +15 +103 +104 +105 +113 +114 +115
|
||||
+8 +9 +A +108 +109 +10A
|
||||
+B +C +D +10B +10C +10D
|
||||
|
||||
Percussion:
|
||||
bassdrum = op(0): 0xBD bit 0x10, operators 12 (0x10) and 15 (0x13) / channels 6, 6b
|
||||
snare = op(3): 0xBD bit 0x08, operators 16 (0x14) / channels 7b
|
||||
tomtom = op(4): 0xBD bit 0x04, operators 14 (0x12) / channels 8
|
||||
cym = op(5): 0xBD bit 0x02, operators 17 (0x17) / channels 8b
|
||||
hihat = op(2): 0xBD bit 0x01, operators 13 (0x11) / channels 7
|
||||
|
||||
|
||||
In OPTi mode ("extended FM" in 82C924, 82C925, 82C931 chips):
|
||||
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
||||
op0 op4 op6 op10 op12 op16 op18 op22 op24 op28 op30 op34 op36 op38 op40 op42 op44 op46
|
||||
op1 op5 op7 op11 op13 op17 op19 op23 op25 op29 op31 op35 op37 op39 op41 op43 op45 op47
|
||||
op2 op8 op14 op20 op26 op32
|
||||
op3 op9 op15 op21 op27 op33 for a total of 6 quad + 12 dual
|
||||
Ports: ???
|
||||
*/
|
||||
|
||||
static adlinsdata2 makeEmptyInstrument()
|
||||
{
|
||||
adlinsdata2 ins;
|
||||
memset(&ins, 0, sizeof(adlinsdata2));
|
||||
ins.flags = adlinsdata::Flag_NoSound;
|
||||
return ins;
|
||||
}
|
||||
|
||||
const adlinsdata2 OPL3::m_emptyInstrument = makeEmptyInstrument();
|
||||
|
||||
OPL3::OPL3() :
|
||||
m_numChips(1),
|
||||
m_numFourOps(0),
|
||||
m_deepTremoloMode(false),
|
||||
m_deepVibratoMode(false),
|
||||
m_rhythmMode(false),
|
||||
m_softPanning(false),
|
||||
m_musicMode(MODE_MIDI),
|
||||
m_volumeScale(VOLUME_Generic)
|
||||
{
|
||||
m_insBankSetup.volumeModel = OPL3::VOLUME_Generic;
|
||||
m_insBankSetup.deepTremolo = false;
|
||||
m_insBankSetup.deepVibrato = false;
|
||||
m_insBankSetup.adLibPercussions = false;
|
||||
m_insBankSetup.scaleModulators = false;
|
||||
|
||||
#ifdef DISABLE_EMBEDDED_BANKS
|
||||
m_embeddedBank = CustomBankTag;
|
||||
#else
|
||||
setEmbeddedBank(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool OPL3::setupLocked()
|
||||
{
|
||||
return (m_musicMode == MODE_CMF ||
|
||||
m_musicMode == MODE_IMF ||
|
||||
m_musicMode == MODE_RSXX);
|
||||
}
|
||||
|
||||
void OPL3::setEmbeddedBank(uint32_t bank)
|
||||
{
|
||||
#ifndef DISABLE_EMBEDDED_BANKS
|
||||
m_embeddedBank = bank;
|
||||
//Embedded banks are supports 128:128 GM set only
|
||||
m_insBanks.clear();
|
||||
|
||||
if(bank >= static_cast<unsigned int>(maxAdlBanks()))
|
||||
return;
|
||||
|
||||
Bank *bank_pair[2] =
|
||||
{
|
||||
&m_insBanks[0],
|
||||
&m_insBanks[PercussionTag]
|
||||
};
|
||||
|
||||
for(unsigned i = 0; i < 256; ++i)
|
||||
{
|
||||
size_t meta = banks[bank][i];
|
||||
adlinsdata2 &ins = bank_pair[i / 128]->ins[i % 128];
|
||||
ins = adlinsdata2::from_adldata(::adlins[meta]);
|
||||
}
|
||||
#else
|
||||
ADL_UNUSED(bank);
|
||||
#endif
|
||||
}
|
||||
|
||||
void OPL3::writeReg(size_t chip, uint16_t address, uint8_t value)
|
||||
{
|
||||
#ifdef ADLMIDI_HW_OPL
|
||||
ADL_UNUSED(chip);
|
||||
unsigned o = address >> 8;
|
||||
unsigned port = OPLBase + o * 2;
|
||||
|
||||
#ifdef __DJGPP__
|
||||
outportb(port, address);
|
||||
for(unsigned c = 0; c < 6; ++c) inportb(port);
|
||||
outportb(port + 1, value);
|
||||
for(unsigned c = 0; c < 35; ++c) inportb(port);
|
||||
#endif
|
||||
|
||||
#ifdef __WATCOMC__
|
||||
outp(port, address);
|
||||
for(uint16_t c = 0; c < 6; ++c) inp(port);
|
||||
outp(port + 1, value);
|
||||
for(uint16_t c = 0; c < 35; ++c) inp(port);
|
||||
#endif//__WATCOMC__
|
||||
|
||||
#else//ADLMIDI_HW_OPL
|
||||
m_chips[chip]->writeReg(address, value);
|
||||
#endif
|
||||
}
|
||||
|
||||
void OPL3::writeRegI(size_t chip, uint32_t address, uint32_t value)
|
||||
{
|
||||
#ifdef ADLMIDI_HW_OPL
|
||||
writeReg(chip, static_cast<uint16_t>(address), static_cast<uint8_t>(value));
|
||||
#else//ADLMIDI_HW_OPL
|
||||
m_chips[chip]->writeReg(static_cast<uint16_t>(address), static_cast<uint8_t>(value));
|
||||
#endif
|
||||
}
|
||||
|
||||
void OPL3::writePan(size_t chip, uint32_t address, uint32_t value)
|
||||
{
|
||||
#ifndef ADLMIDI_HW_OPL
|
||||
m_chips[chip]->writePan(static_cast<uint16_t>(address), static_cast<uint8_t>(value));
|
||||
#else
|
||||
ADL_UNUSED(chip);
|
||||
ADL_UNUSED(address);
|
||||
ADL_UNUSED(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void OPL3::noteOff(size_t c)
|
||||
{
|
||||
size_t chip = c / 23, cc = c % 23;
|
||||
|
||||
if(cc >= 18)
|
||||
{
|
||||
m_regBD[chip] &= ~(0x10 >> (cc - 18));
|
||||
writeRegI(chip, 0xBD, m_regBD[chip]);
|
||||
return;
|
||||
}
|
||||
|
||||
writeRegI(chip, 0xB0 + g_channelsMap[cc], m_keyBlockFNumCache[c] & 0xDF);
|
||||
}
|
||||
|
||||
void OPL3::noteOn(size_t c1, size_t c2, double hertz) // Hertz range: 0..131071
|
||||
{
|
||||
size_t chip = c1 / 23, cc1 = c1 % 23, cc2 = c2 % 23;
|
||||
uint32_t octave = 0, ftone = 0, mul_offset = 0;
|
||||
|
||||
if(hertz < 0)
|
||||
return;
|
||||
|
||||
//Basic range until max of octaves reaching
|
||||
while((hertz >= 1023.5) && (octave < 0x1C00))
|
||||
{
|
||||
hertz /= 2.0; // Calculate octave
|
||||
octave += 0x400;
|
||||
}
|
||||
//Extended range, rely on frequency multiplication increment
|
||||
while(hertz >= 1022.75)
|
||||
{
|
||||
hertz /= 2.0; // Calculate octave
|
||||
mul_offset++;
|
||||
}
|
||||
|
||||
ftone = octave + static_cast<uint32_t>(hertz + 0.5);
|
||||
uint32_t chn = g_channelsMap[cc1];
|
||||
const adldata &patch1 = m_insCache[c1];
|
||||
const adldata &patch2 = m_insCache[c2 < m_insCache.size() ? c2 : 0];
|
||||
|
||||
if(cc1 < 18)
|
||||
{
|
||||
ftone += 0x2000u; /* Key-ON [KON] */
|
||||
|
||||
const bool natural_4op = (m_channelCategory[c1] == ChanCat_4op_Master);
|
||||
const size_t opsCount = natural_4op ? 4 : 2;
|
||||
const uint16_t op_addr[4] =
|
||||
{
|
||||
g_operatorsMap[cc1 * 2 + 0], g_operatorsMap[cc1 * 2 + 1],
|
||||
g_operatorsMap[cc2 * 2 + 0], g_operatorsMap[cc2 * 2 + 1]
|
||||
};
|
||||
const uint32_t ops[4] =
|
||||
{
|
||||
patch1.modulator_E862 & 0xFF,
|
||||
patch1.carrier_E862 & 0xFF,
|
||||
patch2.modulator_E862 & 0xFF,
|
||||
patch2.carrier_E862 & 0xFF
|
||||
};
|
||||
|
||||
for(size_t op = 0; op < opsCount; op++)
|
||||
{
|
||||
if((op > 0) && (op_addr[op] == 0xFFF))
|
||||
break;
|
||||
if(mul_offset > 0)
|
||||
{
|
||||
uint32_t dt = ops[op] & 0xF0;
|
||||
uint32_t mul = ops[op] & 0x0F;
|
||||
if((mul + mul_offset) > 0x0F)
|
||||
{
|
||||
mul_offset = 0;
|
||||
mul = 0x0F;
|
||||
}
|
||||
writeRegI(chip, 0x20 + op_addr[op], (dt | (mul + mul_offset)) & 0xFF);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeRegI(chip, 0x20 + op_addr[op], ops[op] & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(chn != 0xFFF)
|
||||
{
|
||||
writeRegI(chip , 0xA0 + chn, (ftone & 0xFF));
|
||||
writeRegI(chip , 0xB0 + chn, (ftone >> 8));
|
||||
m_keyBlockFNumCache[c1] = (ftone >> 8);
|
||||
}
|
||||
|
||||
if(cc1 >= 18)
|
||||
{
|
||||
m_regBD[chip ] |= (0x10 >> (cc1 - 18));
|
||||
writeRegI(chip , 0x0BD, m_regBD[chip ]);
|
||||
//x |= 0x800; // for test
|
||||
}
|
||||
}
|
||||
|
||||
void OPL3::touchNote(size_t c, uint8_t volume, uint8_t brightness)
|
||||
{
|
||||
if(volume > 63)
|
||||
volume = 63;
|
||||
|
||||
size_t chip = c / 23, cc = c % 23;
|
||||
const adldata &adli = m_insCache[c];
|
||||
uint16_t o1 = g_operatorsMap[cc * 2 + 0];
|
||||
uint16_t o2 = g_operatorsMap[cc * 2 + 1];
|
||||
uint8_t x = adli.modulator_40, y = adli.carrier_40;
|
||||
uint32_t mode = 1; // 2-op AM
|
||||
|
||||
if(m_channelCategory[c] == ChanCat_Regular ||
|
||||
m_channelCategory[c] == ChanCat_Rhythm_Bass)
|
||||
{
|
||||
mode = adli.feedconn & 1; // 2-op FM or 2-op AM
|
||||
}
|
||||
else if(m_channelCategory[c] == ChanCat_4op_Master ||
|
||||
m_channelCategory[c] == ChanCat_4op_Slave)
|
||||
{
|
||||
const adldata *i0, *i1;
|
||||
|
||||
if(m_channelCategory[c] == ChanCat_4op_Master)
|
||||
{
|
||||
i0 = &adli;
|
||||
i1 = &m_insCache[c + 3];
|
||||
mode = 2; // 4-op xx-xx ops 1&2
|
||||
}
|
||||
else
|
||||
{
|
||||
i0 = &m_insCache[c - 3];
|
||||
i1 = &adli;
|
||||
mode = 6; // 4-op xx-xx ops 3&4
|
||||
}
|
||||
|
||||
mode += (i0->feedconn & 1) + (i1->feedconn & 1) * 2;
|
||||
}
|
||||
|
||||
static const bool do_ops[10][2] =
|
||||
{
|
||||
{ false, true }, /* 2 op FM */
|
||||
{ true, true }, /* 2 op AM */
|
||||
{ false, false }, /* 4 op FM-FM ops 1&2 */
|
||||
{ true, false }, /* 4 op AM-FM ops 1&2 */
|
||||
{ false, true }, /* 4 op FM-AM ops 1&2 */
|
||||
{ true, false }, /* 4 op AM-AM ops 1&2 */
|
||||
{ false, true }, /* 4 op FM-FM ops 3&4 */
|
||||
{ false, true }, /* 4 op AM-FM ops 3&4 */
|
||||
{ false, true }, /* 4 op FM-AM ops 3&4 */
|
||||
{ true, true } /* 4 op AM-AM ops 3&4 */
|
||||
};
|
||||
|
||||
if(m_musicMode == MODE_RSXX)
|
||||
{
|
||||
writeRegI(chip, 0x40 + o1, x);
|
||||
if(o2 != 0xFFF)
|
||||
writeRegI(chip, 0x40 + o2, y - volume / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool do_modulator = do_ops[ mode ][ 0 ] || m_scaleModulators;
|
||||
bool do_carrier = do_ops[ mode ][ 1 ] || m_scaleModulators;
|
||||
|
||||
uint32_t modulator = do_modulator ? (x | 63) - volume + volume * (x & 63) / 63 : x;
|
||||
uint32_t carrier = do_carrier ? (y | 63) - volume + volume * (y & 63) / 63 : y;
|
||||
|
||||
if(brightness != 127)
|
||||
{
|
||||
brightness = static_cast<uint8_t>(::round(127.0 * ::sqrt((static_cast<double>(brightness)) * (1.0 / 127.0))) / 2.0);
|
||||
if(!do_modulator)
|
||||
modulator = (modulator | 63) - brightness + brightness * (modulator & 63) / 63;
|
||||
if(!do_carrier)
|
||||
carrier = (carrier | 63) - brightness + brightness * (carrier & 63) / 63;
|
||||
}
|
||||
|
||||
writeRegI(chip, 0x40 + o1, modulator);
|
||||
if(o2 != 0xFFF)
|
||||
writeRegI(chip, 0x40 + o2, carrier);
|
||||
}
|
||||
|
||||
// Correct formula (ST3, AdPlug):
|
||||
// 63-((63-(instrvol))/63)*chanvol
|
||||
// Reduces to (tested identical):
|
||||
// 63 - chanvol + chanvol*instrvol/63
|
||||
// Also (slower, floats):
|
||||
// 63 + chanvol * (instrvol / 63.0 - 1)
|
||||
}
|
||||
|
||||
/*
|
||||
void OPL3::Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127
|
||||
{
|
||||
if(LogarithmicVolumes)
|
||||
Touch_Real(c, volume * 127 / (127 * 127 * 127) / 2);
|
||||
else
|
||||
{
|
||||
// The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A)
|
||||
Touch_Real(c, volume > 8725 ? static_cast<unsigned int>(std::log(volume) * 11.541561 + (0.5 - 104.22845)) : 0);
|
||||
// The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A)
|
||||
//Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0);
|
||||
}
|
||||
}*/
|
||||
|
||||
void OPL3::setPatch(size_t c, const adldata &instrument)
|
||||
{
|
||||
size_t chip = c / 23, cc = c % 23;
|
||||
static const uint8_t data[4] = {0x20, 0x60, 0x80, 0xE0};
|
||||
m_insCache[c] = instrument;
|
||||
uint16_t o1 = g_operatorsMap[cc * 2 + 0];
|
||||
uint16_t o2 = g_operatorsMap[cc * 2 + 1];
|
||||
unsigned x = instrument.modulator_E862, y = instrument.carrier_E862;
|
||||
|
||||
for(size_t a = 0; a < 4; ++a, x >>= 8, y >>= 8)
|
||||
{
|
||||
writeRegI(chip, data[a] + o1, x & 0xFF);
|
||||
if(o2 != 0xFFF)
|
||||
writeRegI(chip, data[a] + o2, y & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
void OPL3::setPan(size_t c, uint8_t value)
|
||||
{
|
||||
size_t chip = c / 23, cc = c % 23;
|
||||
if(g_channelsMap[cc] != 0xFFF)
|
||||
{
|
||||
#ifndef ADLMIDI_HW_OPL
|
||||
if (m_softPanning)
|
||||
{
|
||||
writePan(chip, g_channelsMap[cc], value);
|
||||
writeRegI(chip, 0xC0 + g_channelsMap[cc], m_insCache[c].feedconn | OPL_PANNING_BOTH);
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
int panning = 0;
|
||||
if(value < 64 + 32) panning |= OPL_PANNING_LEFT;
|
||||
if(value >= 64 - 32) panning |= OPL_PANNING_RIGHT;
|
||||
writePan(chip, g_channelsMap[cc], 64);
|
||||
writeRegI(chip, 0xC0 + g_channelsMap[cc], m_insCache[c].feedconn | panning);
|
||||
#ifndef ADLMIDI_HW_OPL
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void OPL3::silenceAll() // Silence all OPL channels.
|
||||
{
|
||||
for(size_t c = 0; c < m_numChannels; ++c)
|
||||
{
|
||||
noteOff(c);
|
||||
touchNote(c, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void OPL3::updateChannelCategories()
|
||||
{
|
||||
const uint32_t fours = m_numFourOps;
|
||||
|
||||
for(uint32_t chip = 0, fours_left = fours; chip < m_numChips; ++chip)
|
||||
{
|
||||
m_regBD[chip] = (m_deepTremoloMode * 0x80 + m_deepVibratoMode * 0x40 + m_rhythmMode * 0x20);
|
||||
writeRegI(chip, 0x0BD, m_regBD[chip]);
|
||||
uint32_t fours_this_chip = std::min(fours_left, static_cast<uint32_t>(6u));
|
||||
writeRegI(chip, 0x104, (1 << fours_this_chip) - 1);
|
||||
fours_left -= fours_this_chip;
|
||||
}
|
||||
|
||||
if(!m_rhythmMode)
|
||||
{
|
||||
for(size_t a = 0, n = m_numChips; a < n; ++a)
|
||||
{
|
||||
for(size_t b = 0; b < 23; ++b)
|
||||
{
|
||||
m_channelCategory[a * 23 + b] =
|
||||
(b >= 18) ? ChanCat_Rhythm_Slave : ChanCat_Regular;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(size_t a = 0, n = m_numChips; a < n; ++a)
|
||||
{
|
||||
for(size_t b = 0; b < 23; ++b)
|
||||
{
|
||||
m_channelCategory[a * 23 + b] =
|
||||
(b >= 18) ? static_cast<ChanCat>(ChanCat_Rhythm_Bass + (b - 18)) :
|
||||
(b >= 6 && b < 9) ? ChanCat_Rhythm_Slave : ChanCat_Regular;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t nextfour = 0;
|
||||
for(uint32_t a = 0; a < fours; ++a)
|
||||
{
|
||||
m_channelCategory[nextfour] = ChanCat_4op_Master;
|
||||
m_channelCategory[nextfour + 3] = ChanCat_4op_Slave;
|
||||
|
||||
switch(a % 6)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
nextfour += 1;
|
||||
break;
|
||||
case 2:
|
||||
nextfour += 9 - 2;
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
nextfour += 1;
|
||||
break;
|
||||
case 5:
|
||||
nextfour += 23 - 9 - 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**/
|
||||
/*
|
||||
In two-op mode, channels 0..8 go as follows:
|
||||
Op1[port] Op2[port]
|
||||
Channel 0: 00 00 03 03
|
||||
Channel 1: 01 01 04 04
|
||||
Channel 2: 02 02 05 05
|
||||
Channel 3: 06 08 09 0B
|
||||
Channel 4: 07 09 10 0C
|
||||
Channel 5: 08 0A 11 0D
|
||||
Channel 6: 12 10 15 13
|
||||
Channel 7: 13 11 16 14
|
||||
Channel 8: 14 12 17 15
|
||||
In four-op mode, channels 0..8 go as follows:
|
||||
Op1[port] Op2[port] Op3[port] Op4[port]
|
||||
Channel 0: 00 00 03 03 06 08 09 0B
|
||||
Channel 1: 01 01 04 04 07 09 10 0C
|
||||
Channel 2: 02 02 05 05 08 0A 11 0D
|
||||
Channel 3: CHANNEL 0 SLAVE
|
||||
Channel 4: CHANNEL 1 SLAVE
|
||||
Channel 5: CHANNEL 2 SLAVE
|
||||
Channel 6: 12 10 15 13
|
||||
Channel 7: 13 11 16 14
|
||||
Channel 8: 14 12 17 15
|
||||
Same goes principally for channels 9-17 respectively.
|
||||
*/
|
||||
}
|
||||
|
||||
void OPL3::commitDeepFlags()
|
||||
{
|
||||
for(size_t chip = 0; chip < m_numChips; ++chip)
|
||||
{
|
||||
m_regBD[chip] = (m_deepTremoloMode * 0x80 + m_deepVibratoMode * 0x40 + m_rhythmMode * 0x20);
|
||||
writeRegI(chip, 0x0BD, m_regBD[chip]);
|
||||
}
|
||||
}
|
||||
|
||||
void OPL3::setVolumeScaleModel(ADLMIDI_VolumeModels volumeModel)
|
||||
{
|
||||
switch(volumeModel)
|
||||
{
|
||||
case ADLMIDI_VolumeModel_AUTO://Do nothing until restart playing
|
||||
break;
|
||||
|
||||
case ADLMIDI_VolumeModel_Generic:
|
||||
m_volumeScale = OPL3::VOLUME_Generic;
|
||||
break;
|
||||
|
||||
case ADLMIDI_VolumeModel_NativeOPL3:
|
||||
m_volumeScale = OPL3::VOLUME_NATIVE;
|
||||
break;
|
||||
|
||||
case ADLMIDI_VolumeModel_DMX:
|
||||
m_volumeScale = OPL3::VOLUME_DMX;
|
||||
break;
|
||||
|
||||
case ADLMIDI_VolumeModel_APOGEE:
|
||||
m_volumeScale = OPL3::VOLUME_APOGEE;
|
||||
break;
|
||||
|
||||
case ADLMIDI_VolumeModel_9X:
|
||||
m_volumeScale = OPL3::VOLUME_9X;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ADLMIDI_VolumeModels OPL3::getVolumeScaleModel()
|
||||
{
|
||||
switch(m_volumeScale)
|
||||
{
|
||||
default:
|
||||
case OPL3::VOLUME_Generic:
|
||||
return ADLMIDI_VolumeModel_Generic;
|
||||
case OPL3::VOLUME_NATIVE:
|
||||
return ADLMIDI_VolumeModel_NativeOPL3;
|
||||
case OPL3::VOLUME_DMX:
|
||||
return ADLMIDI_VolumeModel_DMX;
|
||||
case OPL3::VOLUME_APOGEE:
|
||||
return ADLMIDI_VolumeModel_APOGEE;
|
||||
case OPL3::VOLUME_9X:
|
||||
return ADLMIDI_VolumeModel_9X;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ADLMIDI_HW_OPL
|
||||
void OPL3::clearChips()
|
||||
{
|
||||
for(size_t i = 0; i < m_chips.size(); i++)
|
||||
m_chips[i].reset(NULL);
|
||||
m_chips.clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
void OPL3::reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler)
|
||||
{
|
||||
#ifndef ADLMIDI_HW_OPL
|
||||
clearChips();
|
||||
#else
|
||||
(void)emulator;
|
||||
(void)PCM_RATE;
|
||||
#endif
|
||||
#if !defined(ADLMIDI_AUDIO_TICK_HANDLER)
|
||||
(void)audioTickHandler;
|
||||
#endif
|
||||
m_insCache.clear();
|
||||
m_keyBlockFNumCache.clear();
|
||||
m_regBD.clear();
|
||||
|
||||
#ifndef ADLMIDI_HW_OPL
|
||||
m_chips.resize(m_numChips, AdlMIDI_SPtr<OPLChipBase>());
|
||||
#endif
|
||||
|
||||
const struct adldata defaultInsCache = { 0x1557403,0x005B381, 0x49,0x80, 0x4, +0 };
|
||||
m_numChannels = m_numChips * 23;
|
||||
m_insCache.resize(m_numChannels, defaultInsCache);
|
||||
m_keyBlockFNumCache.resize(m_numChannels, 0);
|
||||
m_regBD.resize(m_numChips, 0);
|
||||
m_channelCategory.resize(m_numChannels, 0);
|
||||
|
||||
for(size_t p = 0, a = 0; a < m_numChips; ++a)
|
||||
{
|
||||
for(size_t b = 0; b < 18; ++b)
|
||||
m_channelCategory[p++] = 0;
|
||||
for(size_t b = 0; b < 5; ++b)
|
||||
m_channelCategory[p++] = ChanCat_Rhythm_Slave;
|
||||
}
|
||||
|
||||
static const uint16_t data[] =
|
||||
{
|
||||
0x004, 96, 0x004, 128, // Pulse timer
|
||||
0x105, 0, 0x105, 1, 0x105, 0, // Pulse OPL3 enable
|
||||
0x001, 32, 0x105, 1 // Enable wave, OPL3 extensions
|
||||
};
|
||||
// size_t fours = m_numFourOps;
|
||||
|
||||
for(size_t i = 0; i < m_numChips; ++i)
|
||||
{
|
||||
#ifndef ADLMIDI_HW_OPL
|
||||
OPLChipBase *chip;
|
||||
switch(emulator)
|
||||
{
|
||||
default:
|
||||
assert(false);
|
||||
abort();
|
||||
#ifndef ADLMIDI_DISABLE_NUKED_EMULATOR
|
||||
case ADLMIDI_EMU_NUKED: /* Latest Nuked OPL3 */
|
||||
chip = new NukedOPL3;
|
||||
break;
|
||||
case ADLMIDI_EMU_NUKED_174: /* Old Nuked OPL3 1.4.7 modified and optimized */
|
||||
chip = new NukedOPL3v174;
|
||||
break;
|
||||
#endif
|
||||
#ifndef ADLMIDI_DISABLE_DOSBOX_EMULATOR
|
||||
case ADLMIDI_EMU_DOSBOX:
|
||||
chip = new DosBoxOPL3;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
m_chips[i].reset(chip);
|
||||
chip->setChipId((uint32_t)i);
|
||||
chip->setRate((uint32_t)PCM_RATE);
|
||||
if(m_runAtPcmRate)
|
||||
chip->setRunningAtPcmRate(true);
|
||||
# if defined(ADLMIDI_AUDIO_TICK_HANDLER)
|
||||
chip->setAudioTickHandlerInstance(audioTickHandler);
|
||||
# endif
|
||||
#endif // ADLMIDI_HW_OPL
|
||||
|
||||
/* Clean-up channels from any playing junk sounds */
|
||||
for(size_t a = 0; a < 18; ++a)
|
||||
writeRegI(i, 0xB0 + g_channelsMap[a], 0x00);
|
||||
for(size_t a = 0; a < sizeof(data) / sizeof(*data); a += 2)
|
||||
writeRegI(i, data[a], (data[a + 1]));
|
||||
}
|
||||
|
||||
updateChannelCategories();
|
||||
silenceAll();
|
||||
}
|
||||
|
|
@ -1,108 +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/>.
|
||||
*/
|
||||
|
||||
#include "adlmidi_private.hpp"
|
||||
|
||||
std::string ADLMIDI_ErrorString;
|
||||
|
||||
// Generator callback on audio rate ticks
|
||||
|
||||
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
|
||||
void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate)
|
||||
{
|
||||
reinterpret_cast<MIDIplay *>(instance)->AudioTick(chipId, rate);
|
||||
}
|
||||
#endif
|
||||
|
||||
int adlCalculateFourOpChannels(MIDIplay *play, bool silent)
|
||||
{
|
||||
size_t n_fourop[2] = {0, 0}, n_total[2] = {0, 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();
|
||||
for(; it != end; ++it)
|
||||
{
|
||||
size_t bank = it->first;
|
||||
size_t div = (bank & OPL3::PercussionTag) ? 1 : 0;
|
||||
for(size_t i = 0; i < 128; ++i)
|
||||
{
|
||||
adlinsdata2 &ins = it->second.ins[i];
|
||||
if(ins.flags & adlinsdata::Flag_NoSound)
|
||||
continue;
|
||||
if((ins.flags & adlinsdata::Flag_Real4op) != 0)
|
||||
++n_fourop[div];
|
||||
++n_total[div];
|
||||
}
|
||||
}
|
||||
}
|
||||
#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))
|
||||
numFourOps = 0;
|
||||
// All 2op melodics and Some (or All) 4op drums
|
||||
else if((n_fourop[0] == 0) && (n_fourop[1] > 0))
|
||||
numFourOps = 2;
|
||||
// Many 4op melodics
|
||||
else if((n_fourop[0] >= (n_total[0] * 7) / 8))
|
||||
numFourOps = 6;
|
||||
// Few 4op melodics
|
||||
else if(n_fourop[0] > 0)
|
||||
numFourOps = 4;
|
||||
|
||||
/* //Old formula
|
||||
unsigned NumFourOps = ((n_fourop[0] == 0) && (n_fourop[1] == 0)) ? 0
|
||||
: (n_fourop[0] >= (n_total[0] * 7) / 8) ? play->m_setup.NumCards * 6
|
||||
: (play->m_setup.NumCards == 1 ? 1 : play->m_setup.NumCards * 4);
|
||||
*/
|
||||
|
||||
play->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();
|
||||
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,217 +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_PTR_HPP_THING
|
||||
#define ADLMIDI_PTR_HPP_THING
|
||||
|
||||
#include <algorithm> // swap
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
Generic deleters for smart pointers
|
||||
*/
|
||||
template <class T>
|
||||
struct ADLMIDI_DefaultDelete
|
||||
{
|
||||
void operator()(T *x) { delete x; }
|
||||
};
|
||||
template <class T>
|
||||
struct ADLMIDI_DefaultArrayDelete
|
||||
{
|
||||
void operator()(T *x) { delete[] x; }
|
||||
};
|
||||
struct ADLMIDI_CDelete
|
||||
{
|
||||
void operator()(void *x) { free(x); }
|
||||
};
|
||||
|
||||
/*
|
||||
Safe unique pointer for C++98, non-copyable but swappable.
|
||||
*/
|
||||
template< class T, class Deleter = ADLMIDI_DefaultDelete<T> >
|
||||
class AdlMIDI_UPtr
|
||||
{
|
||||
T *m_p;
|
||||
public:
|
||||
explicit AdlMIDI_UPtr(T *p)
|
||||
: m_p(p) {}
|
||||
~AdlMIDI_UPtr()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset(T *p = NULL)
|
||||
{
|
||||
if(p != m_p) {
|
||||
if(m_p) {
|
||||
Deleter del;
|
||||
del(m_p);
|
||||
}
|
||||
m_p = p;
|
||||
}
|
||||
}
|
||||
|
||||
void swap(AdlMIDI_UPtr &other)
|
||||
{
|
||||
std::swap(m_p, other.m_p);
|
||||
}
|
||||
|
||||
T *get() const
|
||||
{
|
||||
return m_p;
|
||||
}
|
||||
T &operator*() const
|
||||
{
|
||||
return *m_p;
|
||||
}
|
||||
T *operator->() const
|
||||
{
|
||||
return m_p;
|
||||
}
|
||||
T &operator[](size_t index) const
|
||||
{
|
||||
return m_p[index];
|
||||
}
|
||||
private:
|
||||
AdlMIDI_UPtr(const AdlMIDI_UPtr &);
|
||||
AdlMIDI_UPtr &operator=(const AdlMIDI_UPtr &);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
void swap(AdlMIDI_UPtr<T> &a, AdlMIDI_UPtr<T> &b)
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
/**
|
||||
Unique pointer for arrays.
|
||||
*/
|
||||
template<class T>
|
||||
class AdlMIDI_UPtrArray :
|
||||
public AdlMIDI_UPtr< T, ADLMIDI_DefaultArrayDelete<T> >
|
||||
{
|
||||
public:
|
||||
explicit AdlMIDI_UPtrArray(T *p = NULL)
|
||||
: AdlMIDI_UPtr< T, ADLMIDI_DefaultArrayDelete<T> >(p) {}
|
||||
};
|
||||
|
||||
/**
|
||||
Unique pointer for C memory.
|
||||
*/
|
||||
template<class T>
|
||||
class AdlMIDI_CPtr :
|
||||
public AdlMIDI_UPtr< T, ADLMIDI_CDelete >
|
||||
{
|
||||
public:
|
||||
explicit AdlMIDI_CPtr(T *p = NULL)
|
||||
: AdlMIDI_UPtr< T, ADLMIDI_CDelete >(p) {}
|
||||
};
|
||||
|
||||
/*
|
||||
Shared pointer with non-atomic counter
|
||||
FAQ: Why not std::shared_ptr? Because of Android NDK now doesn't supports it
|
||||
*/
|
||||
template< class T, class Deleter = ADLMIDI_DefaultDelete<T> >
|
||||
class AdlMIDI_SPtr
|
||||
{
|
||||
T *m_p;
|
||||
size_t *m_counter;
|
||||
public:
|
||||
explicit AdlMIDI_SPtr(T *p = NULL)
|
||||
: m_p(p), m_counter(p ? new size_t(1) : NULL) {}
|
||||
~AdlMIDI_SPtr()
|
||||
{
|
||||
reset(NULL);
|
||||
}
|
||||
|
||||
AdlMIDI_SPtr(const AdlMIDI_SPtr &other)
|
||||
: m_p(other.m_p), m_counter(other.m_counter)
|
||||
{
|
||||
if(m_counter)
|
||||
++*m_counter;
|
||||
}
|
||||
|
||||
AdlMIDI_SPtr &operator=(const AdlMIDI_SPtr &other)
|
||||
{
|
||||
if(this == &other)
|
||||
return *this;
|
||||
reset();
|
||||
m_p = other.m_p;
|
||||
m_counter = other.m_counter;
|
||||
if(m_counter)
|
||||
++*m_counter;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset(T *p = NULL)
|
||||
{
|
||||
if(p != m_p) {
|
||||
if(m_p && --*m_counter == 0) {
|
||||
Deleter del;
|
||||
del(m_p);
|
||||
if(!p) {
|
||||
delete m_counter;
|
||||
m_counter = NULL;
|
||||
}
|
||||
}
|
||||
m_p = p;
|
||||
if(p) {
|
||||
if(!m_counter)
|
||||
m_counter = new size_t;
|
||||
*m_counter = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
T *get() const
|
||||
{
|
||||
return m_p;
|
||||
}
|
||||
T &operator*() const
|
||||
{
|
||||
return *m_p;
|
||||
}
|
||||
T *operator->() const
|
||||
{
|
||||
return m_p;
|
||||
}
|
||||
T &operator[](size_t index) const
|
||||
{
|
||||
return m_p[index];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Shared pointer for arrays.
|
||||
*/
|
||||
template<class T>
|
||||
class AdlMIDI_SPtrArray :
|
||||
public AdlMIDI_SPtr< T, ADLMIDI_DefaultArrayDelete<T> >
|
||||
{
|
||||
public:
|
||||
explicit AdlMIDI_SPtrArray(T *p = NULL)
|
||||
: AdlMIDI_SPtr< T, ADLMIDI_DefaultArrayDelete<T> >(p) {}
|
||||
};
|
||||
|
||||
#endif //ADLMIDI_PTR_HPP_THING
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,290 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2018 The DOSBox Team
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined(__GNUC__) && defined(__i386__)
|
||||
#define DB_FASTCALL __attribute__((fastcall))
|
||||
#elif defined(_MSC_VER)
|
||||
#define DB_FASTCALL __fastcall
|
||||
#else
|
||||
#define DB_FASTCALL
|
||||
#endif
|
||||
|
||||
typedef uintptr_t Bitu;
|
||||
typedef intptr_t Bits;
|
||||
typedef uint64_t Bit64u;
|
||||
typedef int64_t Bit64s;
|
||||
typedef uint32_t Bit32u;
|
||||
typedef int32_t Bit32s;
|
||||
typedef uint16_t Bit16u;
|
||||
typedef int16_t Bit16s;
|
||||
typedef uint8_t Bit8u;
|
||||
typedef int8_t Bit8s;
|
||||
|
||||
//Use 8 handlers based on a small logatirmic wavetabe and an exponential table for volume
|
||||
#define WAVE_HANDLER 10
|
||||
//Use a logarithmic wavetable with an exponential table for volume
|
||||
#define WAVE_TABLELOG 11
|
||||
//Use a linear wavetable with a multiply table for volume
|
||||
#define WAVE_TABLEMUL 12
|
||||
|
||||
//Select the type of wave generator routine
|
||||
#define DBOPL_WAVE WAVE_TABLEMUL
|
||||
|
||||
namespace DBOPL {
|
||||
|
||||
struct Chip;
|
||||
struct Operator;
|
||||
struct Channel;
|
||||
|
||||
#if (DBOPL_WAVE == WAVE_HANDLER)
|
||||
typedef Bits ( DB_FASTCALL *WaveHandler) ( Bitu i, Bitu volume );
|
||||
#endif
|
||||
|
||||
typedef Bits ( DBOPL::Operator::*VolumeHandler) ( );
|
||||
typedef Channel* ( DBOPL::Channel::*SynthHandler) ( Chip* chip, Bit32u samples, Bit32s* output );
|
||||
|
||||
//Different synth modes that can generate blocks of data
|
||||
typedef enum {
|
||||
sm2AM,
|
||||
sm2FM,
|
||||
sm3AM,
|
||||
sm3FM,
|
||||
sm4Start,
|
||||
sm3FMFM,
|
||||
sm3AMFM,
|
||||
sm3FMAM,
|
||||
sm3AMAM,
|
||||
sm6Start,
|
||||
sm2Percussion,
|
||||
sm3Percussion
|
||||
} SynthMode;
|
||||
|
||||
//Shifts for the values contained in chandata variable
|
||||
enum {
|
||||
SHIFT_KSLBASE = 16,
|
||||
SHIFT_KEYCODE = 24
|
||||
};
|
||||
|
||||
struct Operator {
|
||||
public:
|
||||
//Masks for operator 20 values
|
||||
enum {
|
||||
MASK_KSR = 0x10,
|
||||
MASK_SUSTAIN = 0x20,
|
||||
MASK_VIBRATO = 0x40,
|
||||
MASK_TREMOLO = 0x80
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
OFF,
|
||||
RELEASE,
|
||||
SUSTAIN,
|
||||
DECAY,
|
||||
ATTACK
|
||||
} State;
|
||||
|
||||
VolumeHandler volHandler;
|
||||
|
||||
#if (DBOPL_WAVE == WAVE_HANDLER)
|
||||
WaveHandler waveHandler; //Routine that generate a wave
|
||||
#else
|
||||
Bit16s* waveBase;
|
||||
Bit32u waveMask;
|
||||
Bit32u waveStart;
|
||||
#endif
|
||||
Bit32u waveIndex; //WAVE_BITS shifted counter of the frequency index
|
||||
Bit32u waveAdd; //The base frequency without vibrato
|
||||
Bit32u waveCurrent; //waveAdd + vibratao
|
||||
|
||||
Bit32u chanData; //Frequency/octave and derived data coming from whatever channel controls this
|
||||
Bit32u freqMul; //Scale channel frequency with this, TODO maybe remove?
|
||||
Bit32u vibrato; //Scaled up vibrato strength
|
||||
Bit32s sustainLevel; //When stopping at sustain level stop here
|
||||
Bit32s totalLevel; //totalLevel is added to every generated volume
|
||||
Bit32u currentLevel; //totalLevel + tremolo
|
||||
Bit32s volume; //The currently active volume
|
||||
|
||||
Bit32u attackAdd; //Timers for the different states of the envelope
|
||||
Bit32u decayAdd;
|
||||
Bit32u releaseAdd;
|
||||
Bit32u rateIndex; //Current position of the evenlope
|
||||
|
||||
Bit8u rateZero; //Bits for the different states of the envelope having no changes
|
||||
Bit8u keyOn; //Bitmask of different values that can generate keyon
|
||||
//Registers, also used to check for changes
|
||||
Bit8u reg20, reg40, reg60, reg80, regE0;
|
||||
//Active part of the envelope we're in
|
||||
Bit8u state;
|
||||
//0xff when tremolo is enabled
|
||||
Bit8u tremoloMask;
|
||||
//Strength of the vibrato
|
||||
Bit8u vibStrength;
|
||||
//Keep track of the calculated KSR so we can check for changes
|
||||
Bit8u ksr;
|
||||
private:
|
||||
void SetState( Bit8u s );
|
||||
void UpdateAttack( const Chip* chip );
|
||||
void UpdateRelease( const Chip* chip );
|
||||
void UpdateDecay( const Chip* chip );
|
||||
public:
|
||||
void UpdateAttenuation();
|
||||
void UpdateRates( const Chip* chip );
|
||||
void UpdateFrequency( );
|
||||
|
||||
void Write20( const Chip* chip, Bit8u val );
|
||||
void Write40( const Chip* chip, Bit8u val );
|
||||
void Write60( const Chip* chip, Bit8u val );
|
||||
void Write80( const Chip* chip, Bit8u val );
|
||||
void WriteE0( const Chip* chip, Bit8u val );
|
||||
|
||||
bool Silent() const;
|
||||
void Prepare( const Chip* chip );
|
||||
|
||||
void KeyOn( Bit8u mask);
|
||||
void KeyOff( Bit8u mask);
|
||||
|
||||
template< State state>
|
||||
Bits TemplateVolume( );
|
||||
|
||||
Bit32s RateForward( Bit32u add );
|
||||
Bitu ForwardWave();
|
||||
Bitu ForwardVolume();
|
||||
|
||||
Bits GetSample( Bits modulation );
|
||||
Bits GetWave( Bitu index, Bitu vol );
|
||||
public:
|
||||
Operator();
|
||||
};
|
||||
|
||||
struct Channel {
|
||||
Operator op[2];
|
||||
inline Operator* Op( Bitu index ) {
|
||||
return &( ( this + (index >> 1) )->op[ index & 1 ]);
|
||||
}
|
||||
SynthHandler synthHandler;
|
||||
Bit32u chanData; //Frequency/octave and derived values
|
||||
Bit32s old[2]; //Old data for feedback
|
||||
|
||||
Bit8u feedback; //Feedback shift
|
||||
Bit8u regB0; //Register values to check for changes
|
||||
Bit8u regC0;
|
||||
//This should correspond with reg104, bit 6 indicates a Percussion channel, bit 7 indicates a silent channel
|
||||
Bit8u fourMask;
|
||||
Bit8s maskLeft; //Sign extended values for both channel's panning
|
||||
Bit8s maskRight;
|
||||
|
||||
Bit16u panLeft; // Extended behavior, scale values for soft panning
|
||||
Bit16u panRight;
|
||||
|
||||
//Forward the channel data to the operators of the channel
|
||||
void SetChanData( const Chip* chip, Bit32u data );
|
||||
//Change in the chandata, check for new values and if we have to forward to operators
|
||||
void UpdateFrequency( const Chip* chip, Bit8u fourOp );
|
||||
void UpdateSynth(const Chip* chip);
|
||||
void WriteA0( const Chip* chip, Bit8u val );
|
||||
void WriteB0( const Chip* chip, Bit8u val );
|
||||
void WriteC0( const Chip* chip, Bit8u val );
|
||||
|
||||
void WritePan( Bit8u val );
|
||||
|
||||
//call this for the first channel
|
||||
template< bool opl3Mode >
|
||||
void GeneratePercussion( Chip* chip, Bit32s* output );
|
||||
|
||||
//Generate blocks of data in specific modes
|
||||
template<SynthMode mode>
|
||||
Channel* BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output );
|
||||
Channel();
|
||||
};
|
||||
|
||||
struct Chip {
|
||||
//This is used as the base counter for vibrato and tremolo
|
||||
Bit32u lfoCounter;
|
||||
Bit32u lfoAdd;
|
||||
|
||||
|
||||
Bit32u noiseCounter;
|
||||
Bit32u noiseAdd;
|
||||
Bit32u noiseValue;
|
||||
|
||||
//Frequency scales for the different multiplications
|
||||
const Bit32u *freqMul/*[16]*/;
|
||||
//Rates for decay and release for rate of this chip
|
||||
const Bit32u *linearRates/*[76]*/;
|
||||
//Best match attack rates for the rate of this chip
|
||||
const Bit32u *attackRates/*[76]*/;
|
||||
|
||||
//18 channels with 2 operators each
|
||||
Channel chan[18];
|
||||
|
||||
Bit8u reg104;
|
||||
Bit8u reg08;
|
||||
Bit8u reg04;
|
||||
Bit8u regBD;
|
||||
Bit8u vibratoIndex;
|
||||
Bit8u tremoloIndex;
|
||||
Bit8s vibratoSign;
|
||||
Bit8u vibratoShift;
|
||||
Bit8u tremoloValue;
|
||||
Bit8u vibratoStrength;
|
||||
Bit8u tremoloStrength;
|
||||
//Mask for allowed wave forms
|
||||
Bit8u waveFormMask;
|
||||
//0 or -1 when enabled
|
||||
Bit8s opl3Active;
|
||||
|
||||
//Return the maximum amount of samples before and LFO change
|
||||
Bit32u ForwardLFO( Bit32u samples );
|
||||
Bit32u ForwardNoise();
|
||||
|
||||
void WriteBD( Bit8u val );
|
||||
void WriteReg(Bit32u reg, Bit8u val );
|
||||
|
||||
Bit32u WriteAddr( Bit32u port, Bit8u val );
|
||||
|
||||
void GenerateBlock2( Bitu samples, Bit32s* output );
|
||||
void GenerateBlock2_Mix( Bitu samples, Bit32s* output );
|
||||
void GenerateBlock3( Bitu samples, Bit32s* output );
|
||||
void GenerateBlock3_Mix( Bitu samples, Bit32s* output );
|
||||
|
||||
//Update the synth handlers in all channels
|
||||
void UpdateSynths();
|
||||
void Generate( Bit32u samples );
|
||||
void Setup( Bit32u r );
|
||||
|
||||
Chip();
|
||||
};
|
||||
|
||||
struct Handler {
|
||||
DBOPL::Chip chip;
|
||||
void WritePan( Bit32u port, Bit8u val );
|
||||
Bit32u WriteAddr( Bit32u port, Bit8u val );
|
||||
void WriteReg( Bit32u addr, Bit8u val );
|
||||
void GenerateArr(Bit32s *out, Bitu *samples);
|
||||
void GenerateArr(Bit16s *out, Bitu *samples);
|
||||
void GenerateArrMix(Bit32s *out, Bitu *samples);
|
||||
void GenerateArrMix(Bit16s *out, Bitu *samples);
|
||||
void Init( Bitu rate );
|
||||
};
|
||||
|
||||
|
||||
} //Namespace
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Interfaces over Yamaha OPL3 (YMF262) chip emulators
|
||||
*
|
||||
* Copyright (C) 2017-2018 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 "dosbox_opl3.h"
|
||||
#include "dosbox/dbopl.h"
|
||||
#include <new>
|
||||
#include <cstdlib>
|
||||
#include <assert.h>
|
||||
|
||||
DosBoxOPL3::DosBoxOPL3() :
|
||||
OPLChipBaseBufferedT(),
|
||||
m_chip(new DBOPL::Handler)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
DosBoxOPL3::~DosBoxOPL3()
|
||||
{
|
||||
DBOPL::Handler *chip_r = reinterpret_cast<DBOPL::Handler*>(m_chip);
|
||||
delete chip_r;
|
||||
}
|
||||
|
||||
void DosBoxOPL3::setRate(uint32_t rate)
|
||||
{
|
||||
OPLChipBaseBufferedT::setRate(rate);
|
||||
DBOPL::Handler *chip_r = reinterpret_cast<DBOPL::Handler*>(m_chip);
|
||||
chip_r->~Handler();
|
||||
new(chip_r) DBOPL::Handler;
|
||||
chip_r->Init(effectiveRate());
|
||||
}
|
||||
|
||||
void DosBoxOPL3::reset()
|
||||
{
|
||||
OPLChipBaseBufferedT::reset();
|
||||
DBOPL::Handler *chip_r = reinterpret_cast<DBOPL::Handler*>(m_chip);
|
||||
chip_r->~Handler();
|
||||
new(chip_r) DBOPL::Handler;
|
||||
chip_r->Init(effectiveRate());
|
||||
}
|
||||
|
||||
void DosBoxOPL3::writeReg(uint16_t addr, uint8_t data)
|
||||
{
|
||||
DBOPL::Handler *chip_r = reinterpret_cast<DBOPL::Handler*>(m_chip);
|
||||
chip_r->WriteReg(static_cast<Bit32u>(addr), data);
|
||||
}
|
||||
|
||||
void DosBoxOPL3::writePan(uint16_t addr, uint8_t data)
|
||||
{
|
||||
DBOPL::Handler *chip_r = reinterpret_cast<DBOPL::Handler*>(m_chip);
|
||||
chip_r->WritePan(static_cast<Bit32u>(addr), data);
|
||||
}
|
||||
|
||||
void DosBoxOPL3::nativeGenerateN(int16_t *output, size_t frames)
|
||||
{
|
||||
DBOPL::Handler *chip_r = reinterpret_cast<DBOPL::Handler*>(m_chip);
|
||||
Bitu frames_i = frames;
|
||||
chip_r->GenerateArr(output, &frames_i);
|
||||
}
|
||||
|
||||
const char *DosBoxOPL3::emulatorName()
|
||||
{
|
||||
return "DOSBox 0.74-r4111 OPL3";
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Interfaces over Yamaha OPL3 (YMF262) chip emulators
|
||||
*
|
||||
* Copyright (C) 2017-2018 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 DOSBOX_OPL3_H
|
||||
#define DOSBOX_OPL3_H
|
||||
|
||||
#include "opl_chip_base.h"
|
||||
|
||||
class DosBoxOPL3 final : public OPLChipBaseBufferedT<DosBoxOPL3>
|
||||
{
|
||||
void *m_chip;
|
||||
public:
|
||||
DosBoxOPL3();
|
||||
~DosBoxOPL3() override;
|
||||
|
||||
bool canRunAtPcmRate() const override { return true; }
|
||||
void setRate(uint32_t rate) override;
|
||||
void reset() override;
|
||||
void writeReg(uint16_t addr, uint8_t data) override;
|
||||
void 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 // DOSBOX_OPL3_H
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,165 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2018 Alexey Khokholov (Nuke.YKT)
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This 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
|
||||
*
|
||||
* Nuked OPL3 emulator.
|
||||
* Thanks:
|
||||
* MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh):
|
||||
* Feedback and Rhythm part calculation information.
|
||||
* forums.submarine.org.uk(carbon14, opl3):
|
||||
* Tremolo and phase generator calculation information.
|
||||
* OPLx decapsulated(Matthew Gambrell, Olli Niemitalo):
|
||||
* OPL2 ROMs.
|
||||
* siliconpr0n.org(John McMaster, digshadow):
|
||||
* YMF262 and VRC VII decaps and die shots.
|
||||
*
|
||||
* version: 1.8
|
||||
*/
|
||||
|
||||
#ifndef OPL_OPL3_H
|
||||
#define OPL_OPL3_H
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define OPL_WRITEBUF_SIZE 1024
|
||||
#define OPL_WRITEBUF_DELAY 2
|
||||
|
||||
typedef uintptr_t Bitu;
|
||||
typedef intptr_t Bits;
|
||||
typedef uint64_t Bit64u;
|
||||
typedef int64_t Bit64s;
|
||||
typedef uint32_t Bit32u;
|
||||
typedef int32_t Bit32s;
|
||||
typedef uint16_t Bit16u;
|
||||
typedef int16_t Bit16s;
|
||||
typedef uint8_t Bit8u;
|
||||
typedef int8_t Bit8s;
|
||||
|
||||
typedef struct _opl3_slot opl3_slot;
|
||||
typedef struct _opl3_channel opl3_channel;
|
||||
typedef struct _opl3_chip opl3_chip;
|
||||
|
||||
struct _opl3_slot {
|
||||
opl3_channel *channel;
|
||||
opl3_chip *chip;
|
||||
Bit16s out;
|
||||
Bit16s fbmod;
|
||||
Bit16s *mod;
|
||||
Bit16s prout;
|
||||
Bit16s eg_rout;
|
||||
Bit16s eg_out;
|
||||
Bit8u eg_inc;
|
||||
Bit8u eg_gen;
|
||||
Bit8u eg_rate;
|
||||
Bit8u eg_ksl;
|
||||
Bit8u *trem;
|
||||
Bit8u reg_vib;
|
||||
Bit8u reg_type;
|
||||
Bit8u reg_ksr;
|
||||
Bit8u reg_mult;
|
||||
Bit8u reg_ksl;
|
||||
Bit8u reg_tl;
|
||||
Bit8u reg_ar;
|
||||
Bit8u reg_dr;
|
||||
Bit8u reg_sl;
|
||||
Bit8u reg_rr;
|
||||
Bit8u reg_wf;
|
||||
Bit8u key;
|
||||
Bit32u pg_reset;
|
||||
Bit32u pg_phase;
|
||||
Bit16u pg_phase_out;
|
||||
Bit8u slot_num;
|
||||
};
|
||||
|
||||
struct _opl3_channel {
|
||||
opl3_slot *slotz[2];/*Don't use "slots" keyword to avoid conflict with Qt applications*/
|
||||
opl3_channel *pair;
|
||||
opl3_chip *chip;
|
||||
Bit16s *out[4];
|
||||
Bit8u chtype;
|
||||
Bit16u f_num;
|
||||
Bit8u block;
|
||||
Bit8u fb;
|
||||
Bit8u con;
|
||||
Bit8u alg;
|
||||
Bit8u ksv;
|
||||
Bit16u cha, chb;
|
||||
Bit16u chl, chr;
|
||||
Bit8u ch_num;
|
||||
};
|
||||
|
||||
typedef struct _opl3_writebuf {
|
||||
Bit64u time;
|
||||
Bit16u reg;
|
||||
Bit8u data;
|
||||
} opl3_writebuf;
|
||||
|
||||
struct _opl3_chip {
|
||||
opl3_channel channel[18];
|
||||
opl3_slot slot[36];
|
||||
Bit16u timer;
|
||||
Bit64u eg_timer;
|
||||
Bit8u eg_timerrem;
|
||||
Bit8u eg_state;
|
||||
Bit8u eg_add;
|
||||
Bit8u newm;
|
||||
Bit8u nts;
|
||||
Bit8u rhy;
|
||||
Bit8u vibpos;
|
||||
Bit8u vibshift;
|
||||
Bit8u tremolo;
|
||||
Bit8u tremolopos;
|
||||
Bit8u tremoloshift;
|
||||
Bit32u noise;
|
||||
Bit16s zeromod;
|
||||
Bit32s mixbuff[2];
|
||||
Bit8u rm_hh_bit2;
|
||||
Bit8u rm_hh_bit3;
|
||||
Bit8u rm_hh_bit7;
|
||||
Bit8u rm_hh_bit8;
|
||||
Bit8u rm_tc_bit3;
|
||||
Bit8u rm_tc_bit5;
|
||||
/* OPL3L */
|
||||
Bit32s rateratio;
|
||||
Bit32s samplecnt;
|
||||
Bit16s oldsamples[2];
|
||||
Bit16s samples[2];
|
||||
|
||||
Bit64u writebuf_samplecnt;
|
||||
Bit32u writebuf_cur;
|
||||
Bit32u writebuf_last;
|
||||
Bit64u writebuf_lasttime;
|
||||
opl3_writebuf writebuf[OPL_WRITEBUF_SIZE];
|
||||
};
|
||||
|
||||
void OPL3_Generate(opl3_chip *chip, Bit16s *buf);
|
||||
void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf);
|
||||
void OPL3_Reset(opl3_chip *chip, Bit32u samplerate);
|
||||
void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v);
|
||||
void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v);
|
||||
void OPL3_WritePan(opl3_chip *chip, Bit16u reg, Bit8u v);
|
||||
void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples);
|
||||
void OPL3_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,156 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2016 Alexey Khokholov (Nuke.YKT)
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Nuked OPL3 emulator.
|
||||
* Thanks:
|
||||
* MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh):
|
||||
* Feedback and Rhythm part calculation information.
|
||||
* forums.submarine.org.uk(carbon14, opl3):
|
||||
* Tremolo and phase generator calculation information.
|
||||
* OPLx decapsulated(Matthew Gambrell, Olli Niemitalo):
|
||||
* OPL2 ROMs.
|
||||
*
|
||||
* version: 1.7.4
|
||||
*/
|
||||
|
||||
#ifndef OPL_OPL3_H
|
||||
#define OPL_OPL3_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#define OPL_WRITEBUF_SIZE 1024
|
||||
#define OPL_WRITEBUF_DELAY 2
|
||||
|
||||
typedef uintptr_t Bitu;
|
||||
typedef intptr_t Bits;
|
||||
typedef uint64_t Bit64u;
|
||||
typedef int64_t Bit64s;
|
||||
typedef uint32_t Bit32u;
|
||||
typedef int32_t Bit32s;
|
||||
typedef uint16_t Bit16u;
|
||||
typedef int16_t Bit16s;
|
||||
typedef uint8_t Bit8u;
|
||||
typedef int8_t Bit8s;
|
||||
|
||||
typedef struct _opl3_slot opl3_slot;
|
||||
typedef struct _opl3_channel opl3_channel;
|
||||
typedef struct _opl3_chip opl3_chip;
|
||||
|
||||
struct _opl3_slot {
|
||||
opl3_channel *channel;
|
||||
opl3_chip *chip;
|
||||
Bit16s out;
|
||||
Bit16s fbmod;
|
||||
Bit16s *mod;
|
||||
Bit16s prout;
|
||||
Bit16s eg_rout;
|
||||
Bit16s eg_out;
|
||||
Bit8u eg_inc;
|
||||
Bit8u eg_gen;
|
||||
Bit8u eg_rate;
|
||||
Bit8u eg_ksl;
|
||||
Bit8u *trem;
|
||||
Bit8u reg_vib;
|
||||
Bit8u reg_type;
|
||||
Bit8u reg_ksr;
|
||||
Bit8u reg_mult;
|
||||
Bit8u reg_ksl;
|
||||
Bit8u reg_tl;
|
||||
Bit8u reg_ar;
|
||||
Bit8u reg_dr;
|
||||
Bit8u reg_sl;
|
||||
Bit8u reg_rr;
|
||||
Bit8u reg_wf;
|
||||
Bit8u key;
|
||||
Bit32u pg_phase;
|
||||
Bit32u timer;
|
||||
|
||||
Bit16u maskzero;
|
||||
Bit8u signpos;
|
||||
Bit8u phaseshift;
|
||||
};
|
||||
|
||||
struct _opl3_channel {
|
||||
opl3_slot *slotz[2];/*Don't use "slots" keyword to avoid conflict with Qt applications*/
|
||||
opl3_channel *pair;
|
||||
opl3_chip *chip;
|
||||
Bit16s *out[4];
|
||||
Bit8u chtype;
|
||||
Bit16u f_num;
|
||||
Bit8u block;
|
||||
Bit8u fb;
|
||||
Bit8u con;
|
||||
Bit8u alg;
|
||||
Bit8u ksv;
|
||||
Bit16u cha, chb;
|
||||
Bit16u chl, chr;
|
||||
};
|
||||
|
||||
typedef struct _opl3_writebuf {
|
||||
Bit64u time;
|
||||
Bit16u reg;
|
||||
Bit8u data;
|
||||
} opl3_writebuf;
|
||||
|
||||
struct _opl3_chip {
|
||||
opl3_channel channel[18];
|
||||
opl3_slot chipslot[36];
|
||||
Bit16u timer;
|
||||
Bit8u newm;
|
||||
Bit8u nts;
|
||||
Bit8u rhy;
|
||||
Bit8u vibpos;
|
||||
Bit8u vibshift;
|
||||
Bit8u tremolo;
|
||||
Bit8u tremolopos;
|
||||
Bit8u tremoloshift;
|
||||
Bit32u noise;
|
||||
Bit16s zeromod;
|
||||
Bit32s mixbuff[2];
|
||||
/* OPL3L */
|
||||
Bit32s rateratio;
|
||||
Bit32s samplecnt;
|
||||
Bit16s oldsamples[2];
|
||||
Bit16s samples[2];
|
||||
|
||||
Bit64u writebuf_samplecnt;
|
||||
Bit32u writebuf_cur;
|
||||
Bit32u writebuf_last;
|
||||
Bit64u writebuf_lasttime;
|
||||
opl3_writebuf writebuf[OPL_WRITEBUF_SIZE];
|
||||
};
|
||||
|
||||
void OPL3v17_Generate(opl3_chip *chip, Bit16s *buf);
|
||||
void OPL3v17_GenerateResampled(opl3_chip *chip, Bit16s *buf);
|
||||
void OPL3v17_Reset(opl3_chip *chip, Bit32u samplerate);
|
||||
void OPL3v17_WritePan(opl3_chip *chip, Bit16u reg, Bit8u v);
|
||||
void OPL3v17_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v);
|
||||
void OPL3v17_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v);
|
||||
void OPL3v17_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples);
|
||||
void OPL3v17_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* Interfaces over Yamaha OPL3 (YMF262) chip emulators
|
||||
*
|
||||
* Copyright (C) 2017-2018 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 "nuked_opl3.h"
|
||||
#include "nuked/nukedopl3.h"
|
||||
#include <cstring>
|
||||
|
||||
NukedOPL3::NukedOPL3() :
|
||||
OPLChipBaseT()
|
||||
{
|
||||
m_chip = new opl3_chip;
|
||||
setRate(m_rate);
|
||||
}
|
||||
|
||||
NukedOPL3::~NukedOPL3()
|
||||
{
|
||||
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
|
||||
delete chip_r;
|
||||
}
|
||||
|
||||
void NukedOPL3::setRate(uint32_t rate)
|
||||
{
|
||||
OPLChipBaseT::setRate(rate);
|
||||
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
|
||||
std::memset(chip_r, 0, sizeof(opl3_chip));
|
||||
OPL3_Reset(chip_r, rate);
|
||||
}
|
||||
|
||||
void NukedOPL3::reset()
|
||||
{
|
||||
OPLChipBaseT::reset();
|
||||
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
|
||||
std::memset(chip_r, 0, sizeof(opl3_chip));
|
||||
OPL3_Reset(chip_r, m_rate);
|
||||
}
|
||||
|
||||
void NukedOPL3::writeReg(uint16_t addr, uint8_t data)
|
||||
{
|
||||
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
|
||||
OPL3_WriteRegBuffered(chip_r, addr, data);
|
||||
}
|
||||
|
||||
void NukedOPL3::writePan(uint16_t addr, uint8_t data)
|
||||
{
|
||||
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
|
||||
OPL3_WritePan(chip_r, addr, data);
|
||||
}
|
||||
|
||||
void NukedOPL3::nativeGenerate(int16_t *frame)
|
||||
{
|
||||
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
|
||||
OPL3_Generate(chip_r, frame);
|
||||
}
|
||||
|
||||
const char *NukedOPL3::emulatorName()
|
||||
{
|
||||
return "Nuked OPL3 (v 1.8)";
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Interfaces over Yamaha OPL3 (YMF262) chip emulators
|
||||
*
|
||||
* Copyright (C) 2017-2018 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 NUKED_OPL3_H
|
||||
#define NUKED_OPL3_H
|
||||
|
||||
#include "opl_chip_base.h"
|
||||
|
||||
class NukedOPL3 final : public OPLChipBaseT<NukedOPL3>
|
||||
{
|
||||
void *m_chip;
|
||||
public:
|
||||
NukedOPL3();
|
||||
~NukedOPL3() override;
|
||||
|
||||
bool canRunAtPcmRate() const override { return false; }
|
||||
void setRate(uint32_t rate) override;
|
||||
void reset() override;
|
||||
void writeReg(uint16_t addr, uint8_t data) override;
|
||||
void 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
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* Interfaces over Yamaha OPL3 (YMF262) chip emulators
|
||||
*
|
||||
* Copyright (C) 2017-2018 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 "nuked_opl3_v174.h"
|
||||
#include "nuked/nukedopl3_174.h"
|
||||
#include <cstring>
|
||||
|
||||
NukedOPL3v174::NukedOPL3v174() :
|
||||
OPLChipBaseT()
|
||||
{
|
||||
m_chip = new opl3_chip;
|
||||
setRate(m_rate);
|
||||
}
|
||||
|
||||
NukedOPL3v174::~NukedOPL3v174()
|
||||
{
|
||||
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
|
||||
delete chip_r;
|
||||
}
|
||||
|
||||
void NukedOPL3v174::setRate(uint32_t rate)
|
||||
{
|
||||
OPLChipBaseT::setRate(rate);
|
||||
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
|
||||
std::memset(chip_r, 0, sizeof(opl3_chip));
|
||||
OPL3v17_Reset(chip_r, rate);
|
||||
}
|
||||
|
||||
void NukedOPL3v174::reset()
|
||||
{
|
||||
OPLChipBaseT::reset();
|
||||
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
|
||||
std::memset(chip_r, 0, sizeof(opl3_chip));
|
||||
OPL3v17_Reset(chip_r, m_rate);
|
||||
}
|
||||
|
||||
void NukedOPL3v174::writeReg(uint16_t addr, uint8_t data)
|
||||
{
|
||||
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
|
||||
OPL3v17_WriteReg(chip_r, addr, data);
|
||||
}
|
||||
|
||||
void NukedOPL3v174::writePan(uint16_t addr, uint8_t data)
|
||||
{
|
||||
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
|
||||
OPL3v17_WritePan(chip_r, addr, data);
|
||||
}
|
||||
|
||||
void NukedOPL3v174::nativeGenerate(int16_t *frame)
|
||||
{
|
||||
opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip);
|
||||
OPL3v17_Generate(chip_r, frame);
|
||||
}
|
||||
|
||||
const char *NukedOPL3v174::emulatorName()
|
||||
{
|
||||
return "Nuked OPL3 (v 1.7.4)";
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Interfaces over Yamaha OPL3 (YMF262) chip emulators
|
||||
*
|
||||
* Copyright (C) 2017-2018 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 NUKED_OPL3174_H
|
||||
#define NUKED_OPL3174_H
|
||||
|
||||
#include "opl_chip_base.h"
|
||||
|
||||
class NukedOPL3v174 final : public OPLChipBaseT<NukedOPL3v174>
|
||||
{
|
||||
void *m_chip;
|
||||
public:
|
||||
NukedOPL3v174();
|
||||
~NukedOPL3v174() override;
|
||||
|
||||
bool canRunAtPcmRate() const override { return false; }
|
||||
void setRate(uint32_t rate) override;
|
||||
void reset() override;
|
||||
void writeReg(uint16_t addr, uint8_t data) override;
|
||||
void 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_OPL3174_H
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017-2018 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 ONP_CHIP_BASE_H
|
||||
#define ONP_CHIP_BASE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#if !defined(_MSC_VER) && (__cplusplus <= 199711L)
|
||||
#define final
|
||||
#define override
|
||||
#endif
|
||||
|
||||
#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
|
||||
class VResampler;
|
||||
#endif
|
||||
|
||||
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
|
||||
extern void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate);
|
||||
#endif
|
||||
|
||||
class OPLChipBase
|
||||
{
|
||||
public:
|
||||
enum { nativeRate = 49716 };
|
||||
protected:
|
||||
uint32_t m_id;
|
||||
uint32_t m_rate;
|
||||
public:
|
||||
OPLChipBase();
|
||||
virtual ~OPLChipBase();
|
||||
|
||||
uint32_t chipId() const { return m_id; }
|
||||
void setChipId(uint32_t id) { m_id = id; }
|
||||
|
||||
virtual bool canRunAtPcmRate() const = 0;
|
||||
virtual bool isRunningAtPcmRate() const = 0;
|
||||
virtual bool setRunningAtPcmRate(bool r) = 0;
|
||||
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
|
||||
virtual void setAudioTickHandlerInstance(void *instance) = 0;
|
||||
#endif
|
||||
|
||||
virtual void setRate(uint32_t rate) = 0;
|
||||
virtual uint32_t effectiveRate() const = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual void writeReg(uint16_t addr, uint8_t data) = 0;
|
||||
|
||||
// extended
|
||||
virtual void writePan(uint16_t addr, uint8_t data) { (void)addr; (void)data; }
|
||||
|
||||
virtual void nativePreGenerate() = 0;
|
||||
virtual void nativePostGenerate() = 0;
|
||||
virtual void nativeGenerate(int16_t *frame) = 0;
|
||||
|
||||
virtual void generate(int16_t *output, size_t frames) = 0;
|
||||
virtual void generateAndMix(int16_t *output, size_t frames) = 0;
|
||||
virtual void generate32(int32_t *output, size_t frames) = 0;
|
||||
virtual void generateAndMix32(int32_t *output, size_t frames) = 0;
|
||||
|
||||
virtual const char* emulatorName() = 0;
|
||||
private:
|
||||
OPLChipBase(const OPLChipBase &c);
|
||||
OPLChipBase &operator=(const OPLChipBase &c);
|
||||
};
|
||||
|
||||
// A base class providing F-bounded generic and efficient implementations,
|
||||
// supporting resampling of chip outputs
|
||||
template <class T>
|
||||
class OPLChipBaseT : public OPLChipBase
|
||||
{
|
||||
public:
|
||||
OPLChipBaseT();
|
||||
virtual ~OPLChipBaseT();
|
||||
|
||||
bool isRunningAtPcmRate() const override;
|
||||
bool setRunningAtPcmRate(bool r) override;
|
||||
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
|
||||
void setAudioTickHandlerInstance(void *instance);
|
||||
#endif
|
||||
|
||||
virtual void setRate(uint32_t rate) override;
|
||||
uint32_t effectiveRate() const override;
|
||||
virtual void reset() override;
|
||||
void generate(int16_t *output, size_t frames) override;
|
||||
void generateAndMix(int16_t *output, size_t frames) override;
|
||||
void generate32(int32_t *output, size_t frames) override;
|
||||
void generateAndMix32(int32_t *output, size_t frames) override;
|
||||
private:
|
||||
bool m_runningAtPcmRate;
|
||||
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
|
||||
void *m_audioTickHandlerInstance;
|
||||
#endif
|
||||
void nativeTick(int16_t *frame);
|
||||
void setupResampler(uint32_t rate);
|
||||
void resetResampler();
|
||||
void resampledGenerate(int32_t *output);
|
||||
#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
|
||||
VResampler *m_resampler;
|
||||
#else
|
||||
int32_t m_oldsamples[2];
|
||||
int32_t m_samples[2];
|
||||
int32_t m_samplecnt;
|
||||
int32_t m_rateratio;
|
||||
enum { rsm_frac = 10 };
|
||||
#endif
|
||||
// amplitude scale factors in and out of resampler, varying for chips;
|
||||
// values are OK to "redefine", the static polymorphism will accept it.
|
||||
enum { resamplerPreAmplify = 1, resamplerPostAttenuate = 1 };
|
||||
};
|
||||
|
||||
// A base class which provides frame-by-frame interfaces on emulations which
|
||||
// don't have a routine for it. It produces outputs in fixed size buffers.
|
||||
// Fast register updates will suffer some latency because of buffering.
|
||||
template <class T, unsigned Buffer = 256>
|
||||
class OPLChipBaseBufferedT : public OPLChipBaseT<T>
|
||||
{
|
||||
public:
|
||||
OPLChipBaseBufferedT()
|
||||
: OPLChipBaseT<T>(), m_bufferIndex(0) {}
|
||||
virtual ~OPLChipBaseBufferedT()
|
||||
{}
|
||||
public:
|
||||
void reset() override;
|
||||
void nativeGenerate(int16_t *frame) override;
|
||||
protected:
|
||||
virtual void nativeGenerateN(int16_t *output, size_t frames) = 0;
|
||||
private:
|
||||
unsigned m_bufferIndex;
|
||||
int16_t m_buffer[2 * Buffer];
|
||||
};
|
||||
|
||||
#include "opl_chip_base.tcc"
|
||||
|
||||
#endif // ONP_CHIP_BASE_H
|
||||
|
|
@ -1,294 +0,0 @@
|
|||
#include "opl_chip_base.h"
|
||||
#include <cmath>
|
||||
|
||||
#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
|
||||
#include <zita-resampler/vresampler.h>
|
||||
#endif
|
||||
|
||||
#if !defined(LIKELY) && defined(__GNUC__)
|
||||
#define LIKELY(x) __builtin_expect((x), 1)
|
||||
#elif !defined(LIKELY)
|
||||
#define LIKELY(x) (x)
|
||||
#endif
|
||||
|
||||
#if !defined(UNLIKELY) && defined(__GNUC__)
|
||||
#define UNLIKELY(x) __builtin_expect((x), 0)
|
||||
#elif !defined(UNLIKELY)
|
||||
#define UNLIKELY(x) (x)
|
||||
#endif
|
||||
|
||||
/* OPLChipBase */
|
||||
|
||||
inline OPLChipBase::OPLChipBase() :
|
||||
m_id(0),
|
||||
m_rate(44100)
|
||||
{
|
||||
}
|
||||
|
||||
inline OPLChipBase::~OPLChipBase()
|
||||
{
|
||||
}
|
||||
|
||||
/* OPLChipBaseT */
|
||||
|
||||
template <class T>
|
||||
OPLChipBaseT<T>::OPLChipBaseT()
|
||||
: OPLChipBase(),
|
||||
m_runningAtPcmRate(false)
|
||||
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
|
||||
,
|
||||
m_audioTickHandlerInstance(NULL)
|
||||
#endif
|
||||
{
|
||||
#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
|
||||
m_resampler = new VResampler;
|
||||
#endif
|
||||
setupResampler(m_rate);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
OPLChipBaseT<T>::~OPLChipBaseT()
|
||||
{
|
||||
#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
|
||||
delete m_resampler;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool OPLChipBaseT<T>::isRunningAtPcmRate() const
|
||||
{
|
||||
return m_runningAtPcmRate;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool OPLChipBaseT<T>::setRunningAtPcmRate(bool r)
|
||||
{
|
||||
if(r != m_runningAtPcmRate)
|
||||
{
|
||||
if(r && !static_cast<T *>(this)->canRunAtPcmRate())
|
||||
return false;
|
||||
m_runningAtPcmRate = r;
|
||||
static_cast<T *>(this)->setRate(m_rate);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
|
||||
template <class T>
|
||||
void OPLChipBaseT<T>::setAudioTickHandlerInstance(void *instance)
|
||||
{
|
||||
m_audioTickHandlerInstance = instance;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
void OPLChipBaseT<T>::setRate(uint32_t rate)
|
||||
{
|
||||
uint32_t oldRate = m_rate;
|
||||
m_rate = rate;
|
||||
if(rate != oldRate)
|
||||
setupResampler(rate);
|
||||
else
|
||||
resetResampler();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
uint32_t OPLChipBaseT<T>::effectiveRate() const
|
||||
{
|
||||
return m_runningAtPcmRate ? m_rate : (uint32_t)nativeRate;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void OPLChipBaseT<T>::reset()
|
||||
{
|
||||
resetResampler();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void OPLChipBaseT<T>::generate(int16_t *output, size_t frames)
|
||||
{
|
||||
static_cast<T *>(this)->nativePreGenerate();
|
||||
for(size_t i = 0; i < frames; ++i)
|
||||
{
|
||||
int32_t frame[2];
|
||||
static_cast<T *>(this)->resampledGenerate(frame);
|
||||
for (unsigned c = 0; c < 2; ++c) {
|
||||
int32_t temp = frame[c];
|
||||
temp = (temp > -32768) ? temp : -32768;
|
||||
temp = (temp < 32767) ? temp : 32767;
|
||||
output[c] = (int16_t)temp;
|
||||
}
|
||||
output += 2;
|
||||
}
|
||||
static_cast<T *>(this)->nativePostGenerate();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void OPLChipBaseT<T>::generateAndMix(int16_t *output, size_t frames)
|
||||
{
|
||||
static_cast<T *>(this)->nativePreGenerate();
|
||||
for(size_t i = 0; i < frames; ++i)
|
||||
{
|
||||
int32_t frame[2];
|
||||
static_cast<T *>(this)->resampledGenerate(frame);
|
||||
for (unsigned c = 0; c < 2; ++c) {
|
||||
int32_t temp = (int32_t)output[c] + frame[c];
|
||||
temp = (temp > -32768) ? temp : -32768;
|
||||
temp = (temp < 32767) ? temp : 32767;
|
||||
output[c] = (int16_t)temp;
|
||||
}
|
||||
output += 2;
|
||||
}
|
||||
static_cast<T *>(this)->nativePostGenerate();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void OPLChipBaseT<T>::generate32(int32_t *output, size_t frames)
|
||||
{
|
||||
static_cast<T *>(this)->nativePreGenerate();
|
||||
for(size_t i = 0; i < frames; ++i)
|
||||
{
|
||||
static_cast<T *>(this)->resampledGenerate(output);
|
||||
output += 2;
|
||||
}
|
||||
static_cast<T *>(this)->nativePostGenerate();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void OPLChipBaseT<T>::generateAndMix32(int32_t *output, size_t frames)
|
||||
{
|
||||
static_cast<T *>(this)->nativePreGenerate();
|
||||
for(size_t i = 0; i < frames; ++i)
|
||||
{
|
||||
int32_t frame[2];
|
||||
static_cast<T *>(this)->resampledGenerate(frame);
|
||||
output[0] += frame[0];
|
||||
output[1] += frame[1];
|
||||
output += 2;
|
||||
}
|
||||
static_cast<T *>(this)->nativePostGenerate();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void OPLChipBaseT<T>::nativeTick(int16_t *frame)
|
||||
{
|
||||
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
|
||||
adl_audioTickHandler(m_audioTickHandlerInstance, m_id, effectiveRate());
|
||||
#endif
|
||||
static_cast<T *>(this)->nativeGenerate(frame);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void OPLChipBaseT<T>::setupResampler(uint32_t rate)
|
||||
{
|
||||
#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
|
||||
m_resampler->setup(rate * (1.0 / 49716), 2, 48);
|
||||
#else
|
||||
m_oldsamples[0] = m_oldsamples[1] = 0;
|
||||
m_samples[0] = m_samples[1] = 0;
|
||||
m_samplecnt = 0;
|
||||
m_rateratio = (int32_t)((rate << rsm_frac) / 49716);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void OPLChipBaseT<T>::resetResampler()
|
||||
{
|
||||
#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
|
||||
m_resampler->reset();
|
||||
#else
|
||||
m_oldsamples[0] = m_oldsamples[1] = 0;
|
||||
m_samples[0] = m_samples[1] = 0;
|
||||
m_samplecnt = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
|
||||
template <class T>
|
||||
void OPLChipBaseT<T>::resampledGenerate(int32_t *output)
|
||||
{
|
||||
if(UNLIKELY(m_runningAtPcmRate))
|
||||
{
|
||||
int16_t in[2];
|
||||
static_cast<T *>(this)->nativeTick(in);
|
||||
output[0] = (int32_t)in[0] * T::resamplerPreAmplify / T::resamplerPostAttenuate;
|
||||
output[1] = (int32_t)in[1] * T::resamplerPreAmplify / T::resamplerPostAttenuate;
|
||||
return;
|
||||
}
|
||||
|
||||
VResampler *rsm = m_resampler;
|
||||
float scale = (float)T::resamplerPreAmplify /
|
||||
(float)T::resamplerPostAttenuate;
|
||||
float f_in[2];
|
||||
float f_out[2];
|
||||
rsm->inp_count = 0;
|
||||
rsm->inp_data = f_in;
|
||||
rsm->out_count = 1;
|
||||
rsm->out_data = f_out;
|
||||
while(rsm->process(), rsm->out_count != 0)
|
||||
{
|
||||
int16_t in[2];
|
||||
static_cast<T *>(this)->nativeTick(in);
|
||||
f_in[0] = scale * (float)in[0];
|
||||
f_in[1] = scale * (float)in[1];
|
||||
rsm->inp_count = 1;
|
||||
rsm->inp_data = f_in;
|
||||
rsm->out_count = 1;
|
||||
rsm->out_data = f_out;
|
||||
}
|
||||
output[0] = static_cast<int32_t>(std::lround(f_out[0]));
|
||||
output[1] = static_cast<int32_t>(std::lround(f_out[1]));
|
||||
}
|
||||
#else
|
||||
template <class T>
|
||||
void OPLChipBaseT<T>::resampledGenerate(int32_t *output)
|
||||
{
|
||||
if(UNLIKELY(m_runningAtPcmRate))
|
||||
{
|
||||
int16_t in[2];
|
||||
static_cast<T *>(this)->nativeTick(in);
|
||||
output[0] = (int32_t)in[0] * T::resamplerPreAmplify / T::resamplerPostAttenuate;
|
||||
output[1] = (int32_t)in[1] * T::resamplerPreAmplify / T::resamplerPostAttenuate;
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t samplecnt = m_samplecnt;
|
||||
const int32_t rateratio = m_rateratio;
|
||||
while(samplecnt >= rateratio)
|
||||
{
|
||||
m_oldsamples[0] = m_samples[0];
|
||||
m_oldsamples[1] = m_samples[1];
|
||||
int16_t buffer[2];
|
||||
static_cast<T *>(this)->nativeTick(buffer);
|
||||
m_samples[0] = buffer[0] * T::resamplerPreAmplify;
|
||||
m_samples[1] = buffer[1] * T::resamplerPreAmplify;
|
||||
samplecnt -= rateratio;
|
||||
}
|
||||
output[0] = (int32_t)(((m_oldsamples[0] * (rateratio - samplecnt)
|
||||
+ m_samples[0] * samplecnt) / rateratio)/T::resamplerPostAttenuate);
|
||||
output[1] = (int32_t)(((m_oldsamples[1] * (rateratio - samplecnt)
|
||||
+ m_samples[1] * samplecnt) / rateratio)/T::resamplerPostAttenuate);
|
||||
m_samplecnt = samplecnt + (1 << rsm_frac);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* OPLChipBaseBufferedT */
|
||||
|
||||
template <class T, unsigned Buffer>
|
||||
void OPLChipBaseBufferedT<T, Buffer>::reset()
|
||||
{
|
||||
OPLChipBaseT<T>::reset();
|
||||
m_bufferIndex = 0;
|
||||
}
|
||||
|
||||
template <class T, unsigned Buffer>
|
||||
void OPLChipBaseBufferedT<T, Buffer>::nativeGenerate(int16_t *frame)
|
||||
{
|
||||
unsigned bufferIndex = m_bufferIndex;
|
||||
if(bufferIndex == 0)
|
||||
static_cast<T *>(this)->nativeGenerateN(m_buffer, Buffer);
|
||||
frame[0] = m_buffer[2 * bufferIndex];
|
||||
frame[1] = m_buffer[2 * bufferIndex + 1];
|
||||
bufferIndex = (bufferIndex + 1 < Buffer) ? (bufferIndex + 1) : 0;
|
||||
m_bufferIndex = bufferIndex;
|
||||
}
|
||||
|
|
@ -1,300 +0,0 @@
|
|||
/*
|
||||
* FileAndMemoryReader - a tiny helper to utify file reading from a disk and memory block
|
||||
*
|
||||
* Copyright (c) 2015-2018 Vitaly Novichkov <admin@wohlnet.ru>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef FILE_AND_MEM_READER_HHHH
|
||||
#define FILE_AND_MEM_READER_HHHH
|
||||
|
||||
#include <string> // std::string
|
||||
#include <cstdio> // std::fopen, std::fread, std::fseek, std::ftell, std::fclose, std::feof
|
||||
#include <stdint.h> // uint*_t
|
||||
#include <stddef.h> // size_t and friends
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX 1
|
||||
#include <cstring> // std::strlen
|
||||
#include <windows.h> // MultiByteToWideChar
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief A little class gives able to read filedata from disk and also from a memory segment
|
||||
*/
|
||||
class FileAndMemReader
|
||||
{
|
||||
//! Currently loaded filename (empty for a memory blocks)
|
||||
std::string m_file_name;
|
||||
//! File reader descriptor
|
||||
std::FILE *m_fp;
|
||||
|
||||
//! Memory pointer descriptor
|
||||
const void *m_mp;
|
||||
//! Size of memory block
|
||||
size_t m_mp_size;
|
||||
//! Cursor position in the memory block
|
||||
size_t m_mp_tell;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Relation direction
|
||||
*/
|
||||
enum relTo
|
||||
{
|
||||
//! At begin position
|
||||
SET = SEEK_SET,
|
||||
//! At current position
|
||||
CUR = SEEK_CUR,
|
||||
//! At end position
|
||||
END = SEEK_END
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief C.O.: It's a constructor!
|
||||
*/
|
||||
FileAndMemReader() :
|
||||
m_fp(NULL),
|
||||
m_mp(NULL),
|
||||
m_mp_size(0),
|
||||
m_mp_tell(0)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief C.O.: It's a destructor!
|
||||
*/
|
||||
~FileAndMemReader()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Open file from a disk
|
||||
* @param path Path to the file in UTF-8 (even on Windows!)
|
||||
*/
|
||||
void openFile(const char *path)
|
||||
{
|
||||
if(m_fp)
|
||||
this->close();//Close previously opened file first!
|
||||
#if !defined(_WIN32) || defined(__WATCOMC__)
|
||||
m_fp = std::fopen(path, "rb");
|
||||
#else
|
||||
wchar_t widePath[MAX_PATH];
|
||||
int size = MultiByteToWideChar(CP_UTF8, 0, path, static_cast<int>(std::strlen(path)), widePath, MAX_PATH);
|
||||
widePath[size] = '\0';
|
||||
m_fp = _wfopen(widePath, L"rb");
|
||||
#endif
|
||||
m_file_name = path;
|
||||
m_mp = NULL;
|
||||
m_mp_size = 0;
|
||||
m_mp_tell = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Open file from memory block
|
||||
* @param mem Pointer to the memory block
|
||||
* @param lenght Size of given block
|
||||
*/
|
||||
void openData(const void *mem, size_t lenght)
|
||||
{
|
||||
if(m_fp)
|
||||
this->close();//Close previously opened file first!
|
||||
m_fp = NULL;
|
||||
m_mp = mem;
|
||||
m_mp_size = lenght;
|
||||
m_mp_tell = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Seek to given position
|
||||
* @param pos Offset or position
|
||||
* @param rel_to Relation (at begin, at current, or at end)
|
||||
*/
|
||||
void seek(long pos, int rel_to)
|
||||
{
|
||||
if(!this->isValid())
|
||||
return;
|
||||
|
||||
if(m_fp)//If a file
|
||||
{
|
||||
std::fseek(m_fp, pos, rel_to);
|
||||
}
|
||||
else//If a memory block
|
||||
{
|
||||
switch(rel_to)
|
||||
{
|
||||
case SET:
|
||||
m_mp_tell = static_cast<size_t>(pos);
|
||||
break;
|
||||
|
||||
case END:
|
||||
m_mp_tell = m_mp_size - static_cast<size_t>(pos);
|
||||
break;
|
||||
|
||||
case CUR:
|
||||
m_mp_tell = m_mp_tell + static_cast<size_t>(pos);
|
||||
break;
|
||||
}
|
||||
|
||||
if(m_mp_tell > m_mp_size)
|
||||
m_mp_tell = m_mp_size;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Seek to given position (unsigned integer 64 as relation. Negative values not supported)
|
||||
* @param pos Offset or position
|
||||
* @param rel_to Relation (at begin, at current, or at end)
|
||||
*/
|
||||
inline void seeku(uint64_t pos, int rel_to)
|
||||
{
|
||||
this->seek(static_cast<long>(pos), rel_to);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read the buffer from a file
|
||||
* @param buf Pointer to the destination memory block
|
||||
* @param num Number of elements
|
||||
* @param size Size of one element
|
||||
* @return Size
|
||||
*/
|
||||
size_t read(void *buf, size_t num, size_t size)
|
||||
{
|
||||
if(!this->isValid())
|
||||
return 0;
|
||||
if(m_fp)
|
||||
return std::fread(buf, num, size, m_fp);
|
||||
else
|
||||
{
|
||||
size_t pos = 0;
|
||||
size_t maxSize = static_cast<size_t>(size * num);
|
||||
|
||||
while((pos < maxSize) && (m_mp_tell < m_mp_size))
|
||||
{
|
||||
reinterpret_cast<uint8_t *>(buf)[pos] = reinterpret_cast<const uint8_t *>(m_mp)[m_mp_tell];
|
||||
m_mp_tell++;
|
||||
pos++;
|
||||
}
|
||||
|
||||
return pos / num;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get one byte and seek forward
|
||||
* @return Readed byte or EOF (a.k.a. -1)
|
||||
*/
|
||||
int getc()
|
||||
{
|
||||
if(!this->isValid())
|
||||
return -1;
|
||||
if(m_fp)//If a file
|
||||
{
|
||||
return std::getc(m_fp);
|
||||
}
|
||||
else //If a memory block
|
||||
{
|
||||
if(m_mp_tell >= m_mp_size)
|
||||
return -1;
|
||||
int x = reinterpret_cast<const uint8_t *>(m_mp)[m_mp_tell];
|
||||
m_mp_tell++;
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns current offset of cursor in a file
|
||||
* @return Offset position
|
||||
*/
|
||||
size_t tell()
|
||||
{
|
||||
if(!this->isValid())
|
||||
return 0;
|
||||
if(m_fp)//If a file
|
||||
return static_cast<size_t>(std::ftell(m_fp));
|
||||
else//If a memory block
|
||||
return m_mp_tell;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Close the file
|
||||
*/
|
||||
void close()
|
||||
{
|
||||
if(m_fp)
|
||||
std::fclose(m_fp);
|
||||
|
||||
m_fp = NULL;
|
||||
m_mp = NULL;
|
||||
m_mp_size = 0;
|
||||
m_mp_tell = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Is file instance valid
|
||||
* @return true if vaild
|
||||
*/
|
||||
bool isValid()
|
||||
{
|
||||
return (m_fp) || (m_mp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Is End Of File?
|
||||
* @return true if end of file was reached
|
||||
*/
|
||||
bool eof()
|
||||
{
|
||||
if(!this->isValid())
|
||||
return true;
|
||||
if(m_fp)
|
||||
return (std::feof(m_fp) != 0);
|
||||
else
|
||||
return m_mp_tell >= m_mp_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a current file name
|
||||
* @return File name of currently loaded file
|
||||
*/
|
||||
const std::string &fileName()
|
||||
{
|
||||
return m_file_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve file size
|
||||
* @return Size of file in bytes
|
||||
*/
|
||||
size_t fileSize()
|
||||
{
|
||||
if(!this->isValid())
|
||||
return 0;
|
||||
if(!m_fp)
|
||||
return m_mp_size; //Size of memory block is well known
|
||||
size_t old_pos = this->tell();
|
||||
seek(0l, FileAndMemReader::END);
|
||||
size_t file_size = this->tell();
|
||||
seek(static_cast<long>(old_pos), FileAndMemReader::SET);
|
||||
return file_size;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* FILE_AND_MEM_READER_HHHH */
|
||||
|
|
@ -1,588 +0,0 @@
|
|||
/*
|
||||
* Wohlstand's OPL3 Bank File - a bank format to store OPL3 timbre data and setup
|
||||
*
|
||||
* Copyright (c) 2015-2018 Vitaly Novichkov <admin@wohlnet.ru>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "wopl_file.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static const char *wopl3_magic = "WOPL3-BANK\0";
|
||||
static const char *wopli_magic = "WOPL3-INST\0";
|
||||
|
||||
static const uint16_t wopl_latest_version = 3;
|
||||
|
||||
#define WOPL_INST_SIZE_V2 62
|
||||
#define WOPL_INST_SIZE_V3 66
|
||||
|
||||
static uint16_t toUint16LE(const uint8_t *arr)
|
||||
{
|
||||
uint16_t num = arr[0];
|
||||
num |= ((arr[1] << 8) & 0xFF00);
|
||||
return num;
|
||||
}
|
||||
|
||||
static uint16_t toUint16BE(const uint8_t *arr)
|
||||
{
|
||||
uint16_t num = arr[1];
|
||||
num |= ((arr[0] << 8) & 0xFF00);
|
||||
return num;
|
||||
}
|
||||
|
||||
static int16_t toSint16BE(const uint8_t *arr)
|
||||
{
|
||||
int16_t num = *(const int8_t *)(&arr[0]);
|
||||
num *= 1 << 8;
|
||||
num |= arr[1];
|
||||
return num;
|
||||
}
|
||||
|
||||
static void fromUint16LE(uint16_t in, uint8_t *arr)
|
||||
{
|
||||
arr[0] = in & 0x00FF;
|
||||
arr[1] = (in >> 8) & 0x00FF;
|
||||
}
|
||||
|
||||
static void fromUint16BE(uint16_t in, uint8_t *arr)
|
||||
{
|
||||
arr[1] = in & 0x00FF;
|
||||
arr[0] = (in >> 8) & 0x00FF;
|
||||
}
|
||||
|
||||
static void fromSint16BE(int16_t in, uint8_t *arr)
|
||||
{
|
||||
arr[1] = in & 0x00FF;
|
||||
arr[0] = ((uint16_t)in >> 8) & 0x00FF;
|
||||
}
|
||||
|
||||
|
||||
WOPLFile *WOPL_Init(uint16_t melodic_banks, uint16_t percussive_banks)
|
||||
{
|
||||
WOPLFile *file = NULL;
|
||||
if(melodic_banks == 0)
|
||||
return NULL;
|
||||
if(percussive_banks == 0)
|
||||
return NULL;
|
||||
file = (WOPLFile*)calloc(1, sizeof(WOPLFile));
|
||||
if(!file)
|
||||
return NULL;
|
||||
file->banks_count_melodic = melodic_banks;
|
||||
file->banks_count_percussion = percussive_banks;
|
||||
file->banks_melodic = (WOPLBank*)calloc(1, sizeof(WOPLBank) * melodic_banks );
|
||||
file->banks_percussive = (WOPLBank*)calloc(1, sizeof(WOPLBank) * percussive_banks );
|
||||
return file;
|
||||
}
|
||||
|
||||
void WOPL_Free(WOPLFile *file)
|
||||
{
|
||||
if(file)
|
||||
{
|
||||
if(file->banks_melodic)
|
||||
free(file->banks_melodic);
|
||||
if(file->banks_percussive)
|
||||
free(file->banks_percussive);
|
||||
free(file);
|
||||
}
|
||||
}
|
||||
|
||||
int WOPL_BanksCmp(const WOPLFile *bank1, const WOPLFile *bank2)
|
||||
{
|
||||
int res = 1;
|
||||
|
||||
res &= (bank1->version == bank2->version);
|
||||
res &= (bank1->opl_flags == bank2->opl_flags);
|
||||
res &= (bank1->volume_model == bank2->volume_model);
|
||||
res &= (bank1->banks_count_melodic == bank2->banks_count_melodic);
|
||||
res &= (bank1->banks_count_percussion == bank2->banks_count_percussion);
|
||||
|
||||
if(res)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < bank1->banks_count_melodic; i++)
|
||||
res &= (memcmp(&bank1->banks_melodic[i], &bank2->banks_melodic[i], sizeof(WOPLBank)) == 0);
|
||||
if(res)
|
||||
{
|
||||
for(i = 0; i < bank1->banks_count_percussion; i++)
|
||||
res &= (memcmp(&bank1->banks_percussive[i], &bank2->banks_percussive[i], sizeof(WOPLBank)) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void WOPL_parseInstrument(WOPLInstrument *ins, uint8_t *cursor, uint16_t version, uint8_t has_sounding_delays)
|
||||
{
|
||||
int l;
|
||||
strncpy(ins->inst_name, (const char*)cursor, 32);
|
||||
ins->inst_name[32] = '\0';
|
||||
ins->note_offset1 = toSint16BE(cursor + 32);
|
||||
ins->note_offset2 = toSint16BE(cursor + 34);
|
||||
ins->midi_velocity_offset = (int8_t)cursor[36];
|
||||
ins->second_voice_detune = (int8_t)cursor[37];
|
||||
ins->percussion_key_number = cursor[38];
|
||||
ins->inst_flags = cursor[39];
|
||||
ins->fb_conn1_C0 = cursor[40];
|
||||
ins->fb_conn2_C0 = cursor[41];
|
||||
for(l = 0; l < 4; l++)
|
||||
{
|
||||
size_t off = 42 + (size_t)(l) * 5;
|
||||
ins->operators[l].avekf_20 = cursor[off + 0];
|
||||
ins->operators[l].ksl_l_40 = cursor[off + 1];
|
||||
ins->operators[l].atdec_60 = cursor[off + 2];
|
||||
ins->operators[l].susrel_80 = cursor[off + 3];
|
||||
ins->operators[l].waveform_E0 = cursor[off + 4];
|
||||
}
|
||||
if((version >= 3) && has_sounding_delays)
|
||||
{
|
||||
ins->delay_on_ms = toUint16BE(cursor + 62);
|
||||
ins->delay_off_ms = toUint16BE(cursor + 64);
|
||||
}
|
||||
}
|
||||
|
||||
static void WOPL_writeInstrument(WOPLInstrument *ins, uint8_t *cursor, uint16_t version, uint8_t has_sounding_delays)
|
||||
{
|
||||
int l;
|
||||
strncpy((char*)cursor, ins->inst_name, 32);
|
||||
fromSint16BE(ins->note_offset1, cursor + 32);
|
||||
fromSint16BE(ins->note_offset2, cursor + 34);
|
||||
cursor[36] = (uint8_t)ins->midi_velocity_offset;
|
||||
cursor[37] = (uint8_t)ins->second_voice_detune;
|
||||
cursor[38] = ins->percussion_key_number;
|
||||
cursor[39] = ins->inst_flags;
|
||||
cursor[40] = ins->fb_conn1_C0;
|
||||
cursor[41] = ins->fb_conn2_C0;
|
||||
for(l = 0; l < 4; l++)
|
||||
{
|
||||
size_t off = 42 + (size_t)(l) * 5;
|
||||
cursor[off + 0] = ins->operators[l].avekf_20;
|
||||
cursor[off + 1] = ins->operators[l].ksl_l_40;
|
||||
cursor[off + 2] = ins->operators[l].atdec_60;
|
||||
cursor[off + 3] = ins->operators[l].susrel_80;
|
||||
cursor[off + 4] = ins->operators[l].waveform_E0;
|
||||
}
|
||||
if((version >= 3) && has_sounding_delays)
|
||||
{
|
||||
fromUint16BE(ins->delay_on_ms, cursor + 62);
|
||||
fromUint16BE(ins->delay_off_ms, cursor + 64);
|
||||
}
|
||||
}
|
||||
|
||||
WOPLFile *WOPL_LoadBankFromMem(void *mem, size_t length, int *error)
|
||||
{
|
||||
WOPLFile *outFile = NULL;
|
||||
uint16_t i = 0, j = 0, k = 0;
|
||||
uint16_t version = 0;
|
||||
uint16_t count_melodic_banks = 1;
|
||||
uint16_t count_percusive_banks = 1;
|
||||
uint8_t *cursor = (uint8_t *)mem;
|
||||
|
||||
WOPLBank *bankslots[2];
|
||||
uint16_t bankslots_sizes[2];
|
||||
|
||||
#define SET_ERROR(err) \
|
||||
{\
|
||||
WOPL_Free(outFile);\
|
||||
if(error)\
|
||||
{\
|
||||
*error = err;\
|
||||
}\
|
||||
}
|
||||
|
||||
#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; }
|
||||
|
||||
if(!cursor)
|
||||
{
|
||||
SET_ERROR(WOPL_ERR_NULL_POINTER);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
{/* Magic number */
|
||||
if(length < 11)
|
||||
{
|
||||
SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING);
|
||||
return NULL;
|
||||
}
|
||||
if(memcmp(cursor, wopl3_magic, 11) != 0)
|
||||
{
|
||||
SET_ERROR(WOPL_ERR_BAD_MAGIC);
|
||||
return NULL;
|
||||
}
|
||||
GO_FORWARD(11);
|
||||
}
|
||||
|
||||
{/* Version code */
|
||||
if(length < 2)
|
||||
{
|
||||
SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING);
|
||||
return NULL;
|
||||
}
|
||||
version = toUint16LE(cursor);
|
||||
if(version > wopl_latest_version)
|
||||
{
|
||||
SET_ERROR(WOPL_ERR_NEWER_VERSION);
|
||||
return NULL;
|
||||
}
|
||||
GO_FORWARD(2);
|
||||
}
|
||||
|
||||
{/* Header of WOPL */
|
||||
uint8_t head[6];
|
||||
if(length < 6)
|
||||
{
|
||||
SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(head, cursor, 6);
|
||||
count_melodic_banks = toUint16BE(head);
|
||||
count_percusive_banks = toUint16BE(head + 2);
|
||||
GO_FORWARD(6);
|
||||
|
||||
outFile = WOPL_Init(count_melodic_banks, count_percusive_banks);
|
||||
if(!outFile)
|
||||
{
|
||||
SET_ERROR(WOPL_ERR_OUT_OF_MEMORY);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
outFile->version = version;
|
||||
outFile->opl_flags = head[4];
|
||||
outFile->volume_model = head[5];
|
||||
}
|
||||
|
||||
bankslots_sizes[0] = count_melodic_banks;
|
||||
bankslots[0] = outFile->banks_melodic;
|
||||
bankslots_sizes[1] = count_percusive_banks;
|
||||
bankslots[1] = outFile->banks_percussive;
|
||||
|
||||
if(version >= 2) /* Bank names and LSB/MSB titles */
|
||||
{
|
||||
for(i = 0; i < 2; i++)
|
||||
{
|
||||
for(j = 0; j < bankslots_sizes[i]; j++)
|
||||
{
|
||||
if(length < 34)
|
||||
{
|
||||
SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING);
|
||||
return NULL;
|
||||
}
|
||||
strncpy(bankslots[i][j].bank_name, (const char*)cursor, 32);
|
||||
bankslots[i][j].bank_name[32] = '\0';
|
||||
bankslots[i][j].bank_midi_lsb = cursor[32];
|
||||
bankslots[i][j].bank_midi_msb = cursor[33];
|
||||
GO_FORWARD(34);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{/* Read instruments data */
|
||||
uint16_t insSize = 0;
|
||||
if(version > 2)
|
||||
insSize = WOPL_INST_SIZE_V3;
|
||||
else
|
||||
insSize = WOPL_INST_SIZE_V2;
|
||||
for(i = 0; i < 2; i++)
|
||||
{
|
||||
if(length < (insSize * 128) * (size_t)bankslots_sizes[i])
|
||||
{
|
||||
SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(j = 0; j < bankslots_sizes[i]; j++)
|
||||
{
|
||||
for(k = 0; k < 128; k++)
|
||||
{
|
||||
WOPLInstrument *ins = &bankslots[i][j].ins[k];
|
||||
WOPL_parseInstrument(ins, cursor, version, 1);
|
||||
GO_FORWARD(insSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef GO_FORWARD
|
||||
#undef SET_ERROR
|
||||
|
||||
return outFile;
|
||||
}
|
||||
|
||||
int WOPL_LoadInstFromMem(WOPIFile *file, void *mem, size_t length)
|
||||
{
|
||||
uint16_t version = 0;
|
||||
uint8_t *cursor = (uint8_t *)mem;
|
||||
uint16_t ins_size;
|
||||
|
||||
if(!cursor)
|
||||
return WOPL_ERR_NULL_POINTER;
|
||||
|
||||
#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; }
|
||||
|
||||
{/* Magic number */
|
||||
if(length < 11)
|
||||
return WOPL_ERR_UNEXPECTED_ENDING;
|
||||
if(memcmp(cursor, wopli_magic, 11) != 0)
|
||||
return WOPL_ERR_BAD_MAGIC;
|
||||
GO_FORWARD(11);
|
||||
}
|
||||
|
||||
{/* Version code */
|
||||
if(length < 2)
|
||||
return WOPL_ERR_UNEXPECTED_ENDING;
|
||||
version = toUint16LE(cursor);
|
||||
if(version > wopl_latest_version)
|
||||
return WOPL_ERR_NEWER_VERSION;
|
||||
GO_FORWARD(2);
|
||||
}
|
||||
|
||||
file->version = version;
|
||||
|
||||
{/* is drum flag */
|
||||
if(length < 1)
|
||||
return WOPL_ERR_UNEXPECTED_ENDING;
|
||||
file->is_drum = *cursor;
|
||||
GO_FORWARD(1);
|
||||
}
|
||||
|
||||
if(version > 2)
|
||||
/* Skip sounding delays are not part of single-instrument file
|
||||
* two sizes of uint16_t will be subtracted */
|
||||
ins_size = WOPL_INST_SIZE_V3 - (sizeof(uint16_t) * 2);
|
||||
else
|
||||
ins_size = WOPL_INST_SIZE_V2;
|
||||
|
||||
if(length < ins_size)
|
||||
return WOPL_ERR_UNEXPECTED_ENDING;
|
||||
|
||||
WOPL_parseInstrument(&file->inst, cursor, version, 0);
|
||||
GO_FORWARD(ins_size);
|
||||
|
||||
return WOPL_ERR_OK;
|
||||
#undef GO_FORWARD
|
||||
}
|
||||
|
||||
size_t WOPL_CalculateBankFileSize(WOPLFile *file, uint16_t version)
|
||||
{
|
||||
size_t final_size = 0;
|
||||
size_t ins_size = 0;
|
||||
|
||||
if(version == 0)
|
||||
version = wopl_latest_version;
|
||||
|
||||
if(!file)
|
||||
return 0;
|
||||
final_size += 11 + 2 + 2 + 2 + 1 + 1;
|
||||
/*
|
||||
* Magic number,
|
||||
* Version,
|
||||
* Count of melodic banks,
|
||||
* Count of percussive banks,
|
||||
* Chip specific flags
|
||||
* Volume Model
|
||||
*/
|
||||
|
||||
if(version >= 2)
|
||||
{
|
||||
/* Melodic banks meta-data */
|
||||
final_size += (32 + 1 + 1) * file->banks_count_melodic;
|
||||
/* Percussive banks meta-data */
|
||||
final_size += (32 + 1 + 1) * file->banks_count_percussion;
|
||||
}
|
||||
|
||||
if(version >= 3)
|
||||
ins_size = WOPL_INST_SIZE_V3;
|
||||
else
|
||||
ins_size = WOPL_INST_SIZE_V2;
|
||||
/* Melodic instruments */
|
||||
final_size += (ins_size * 128) * file->banks_count_melodic;
|
||||
/* Percusive instruments */
|
||||
final_size += (ins_size * 128) * file->banks_count_percussion;
|
||||
|
||||
return final_size;
|
||||
}
|
||||
|
||||
size_t WOPL_CalculateInstFileSize(WOPIFile *file, uint16_t version)
|
||||
{
|
||||
size_t final_size = 0;
|
||||
size_t ins_size = 0;
|
||||
|
||||
if(version == 0)
|
||||
version = wopl_latest_version;
|
||||
|
||||
if(!file)
|
||||
return 0;
|
||||
final_size += 11 + 2 + 1;
|
||||
/*
|
||||
* Magic number,
|
||||
* version,
|
||||
* is percussive instrument
|
||||
*/
|
||||
|
||||
if(version > 2)
|
||||
/* Skip sounding delays are not part of single-instrument file
|
||||
* two sizes of uint16_t will be subtracted */
|
||||
ins_size = WOPL_INST_SIZE_V3 - (sizeof(uint16_t) * 2);
|
||||
else
|
||||
ins_size = WOPL_INST_SIZE_V2;
|
||||
final_size += ins_size;
|
||||
|
||||
return final_size;
|
||||
}
|
||||
|
||||
int WOPL_SaveBankToMem(WOPLFile *file, void *dest_mem, size_t length, uint16_t version, uint16_t force_gm)
|
||||
{
|
||||
uint8_t *cursor = (uint8_t *)dest_mem;
|
||||
uint16_t ins_size = 0;
|
||||
uint16_t i, j, k;
|
||||
uint16_t banks_melodic = force_gm ? 1 : file->banks_count_melodic;
|
||||
uint16_t banks_percusive = force_gm ? 1 : file->banks_count_percussion;
|
||||
|
||||
WOPLBank *bankslots[2];
|
||||
uint16_t bankslots_sizes[2];
|
||||
|
||||
if(version == 0)
|
||||
version = wopl_latest_version;
|
||||
|
||||
#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; }
|
||||
|
||||
if(length < 11)
|
||||
return WOPL_ERR_UNEXPECTED_ENDING;
|
||||
memcpy(cursor, wopl3_magic, 11);
|
||||
GO_FORWARD(11);
|
||||
|
||||
if(length < 2)
|
||||
return WOPL_ERR_UNEXPECTED_ENDING;
|
||||
fromUint16LE(version, cursor);
|
||||
GO_FORWARD(2);
|
||||
|
||||
if(length < 2)
|
||||
return WOPL_ERR_UNEXPECTED_ENDING;
|
||||
fromUint16BE(banks_melodic, cursor);
|
||||
GO_FORWARD(2);
|
||||
|
||||
if(length < 2)
|
||||
return WOPL_ERR_UNEXPECTED_ENDING;
|
||||
fromUint16BE(banks_percusive, cursor);
|
||||
GO_FORWARD(2);
|
||||
|
||||
if(length < 2)
|
||||
return WOPL_ERR_UNEXPECTED_ENDING;
|
||||
cursor[0] = file->opl_flags;
|
||||
cursor[1] = file->volume_model;
|
||||
GO_FORWARD(2);
|
||||
|
||||
bankslots[0] = file->banks_melodic;
|
||||
bankslots_sizes[0] = banks_melodic;
|
||||
bankslots[1] = file->banks_percussive;
|
||||
bankslots_sizes[1] = banks_percusive;
|
||||
|
||||
if(version >= 2)
|
||||
{
|
||||
for(i = 0; i < 2; i++)
|
||||
{
|
||||
for(j = 0; j < bankslots_sizes[i]; j++)
|
||||
{
|
||||
if(length < 34)
|
||||
return WOPL_ERR_UNEXPECTED_ENDING;
|
||||
strncpy((char*)cursor, bankslots[i][j].bank_name, 32);
|
||||
cursor[32] = bankslots[i][j].bank_midi_lsb;
|
||||
cursor[33] = bankslots[i][j].bank_midi_msb;
|
||||
GO_FORWARD(34);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{/* Write instruments data */
|
||||
if(version >= 3)
|
||||
ins_size = WOPL_INST_SIZE_V3;
|
||||
else
|
||||
ins_size = WOPL_INST_SIZE_V2;
|
||||
for(i = 0; i < 2; i++)
|
||||
{
|
||||
if(length < (ins_size * 128) * (size_t)bankslots_sizes[i])
|
||||
return WOPL_ERR_UNEXPECTED_ENDING;
|
||||
|
||||
for(j = 0; j < bankslots_sizes[i]; j++)
|
||||
{
|
||||
for(k = 0; k < 128; k++)
|
||||
{
|
||||
WOPLInstrument *ins = &bankslots[i][j].ins[k];
|
||||
WOPL_writeInstrument(ins, cursor, version, 1);
|
||||
GO_FORWARD(ins_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return WOPL_ERR_OK;
|
||||
#undef GO_FORWARD
|
||||
}
|
||||
|
||||
int WOPL_SaveInstToMem(WOPIFile *file, void *dest_mem, size_t length, uint16_t version)
|
||||
{
|
||||
uint8_t *cursor = (uint8_t *)dest_mem;
|
||||
uint16_t ins_size;
|
||||
|
||||
if(!cursor)
|
||||
return WOPL_ERR_NULL_POINTER;
|
||||
|
||||
if(version == 0)
|
||||
version = wopl_latest_version;
|
||||
|
||||
#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; }
|
||||
|
||||
{/* Magic number */
|
||||
if(length < 11)
|
||||
return WOPL_ERR_UNEXPECTED_ENDING;
|
||||
memcpy(cursor, wopli_magic, 11);
|
||||
GO_FORWARD(11);
|
||||
}
|
||||
|
||||
{/* Version code */
|
||||
if(length < 2)
|
||||
return WOPL_ERR_UNEXPECTED_ENDING;
|
||||
fromUint16LE(version, cursor);
|
||||
GO_FORWARD(2);
|
||||
}
|
||||
|
||||
{/* is drum flag */
|
||||
if(length < 1)
|
||||
return WOPL_ERR_UNEXPECTED_ENDING;
|
||||
*cursor = file->is_drum;
|
||||
GO_FORWARD(1);
|
||||
}
|
||||
|
||||
if(version > 2)
|
||||
/* Skip sounding delays are not part of single-instrument file
|
||||
* two sizes of uint16_t will be subtracted */
|
||||
ins_size = WOPL_INST_SIZE_V3 - (sizeof(uint16_t) * 2);
|
||||
else
|
||||
ins_size = WOPL_INST_SIZE_V2;
|
||||
|
||||
if(length < ins_size)
|
||||
return WOPL_ERR_UNEXPECTED_ENDING;
|
||||
|
||||
WOPL_writeInstrument(&file->inst, cursor, version, 0);
|
||||
GO_FORWARD(ins_size);
|
||||
|
||||
return WOPL_ERR_OK;
|
||||
#undef GO_FORWARD
|
||||
}
|
||||
|
|
@ -1,293 +0,0 @@
|
|||
/*
|
||||
* Wohlstand's OPL3 Bank File - a bank format to store OPL3 timbre data and setup
|
||||
*
|
||||
* Copyright (c) 2015-2018 Vitaly Novichkov <admin@wohlnet.ru>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef WOPL_FILE_H
|
||||
#define WOPL_FILE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if !defined(__STDC_VERSION__) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ < 199901L)) \
|
||||
|| defined(__STRICT_ANSI__) || !defined(__cplusplus)
|
||||
typedef signed char int8_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef signed short int int16_t;
|
||||
typedef unsigned short int uint16_t;
|
||||
#endif
|
||||
|
||||
/* Global OPL flags */
|
||||
typedef enum WOPLFileFlags
|
||||
{
|
||||
/* Enable Deep-Tremolo flag */
|
||||
WOPL_FLAG_DEEP_TREMOLO = 0x01,
|
||||
/* Enable Deep-Vibrato flag */
|
||||
WOPL_FLAG_DEEP_VIBRATO = 0x02
|
||||
} WOPLFileFlags;
|
||||
|
||||
/* Volume scaling model implemented in the libADLMIDI */
|
||||
typedef enum WOPL_VolumeModel
|
||||
{
|
||||
WOPL_VM_Generic = 0,
|
||||
WOPL_VM_Native,
|
||||
WOPL_VM_DMX,
|
||||
WOPL_VM_Apogee,
|
||||
WOPL_VM_Win9x
|
||||
} WOPL_VolumeModel;
|
||||
|
||||
typedef enum WOPL_InstrumentFlags
|
||||
{
|
||||
/* Is two-operator single-voice instrument (no flags) */
|
||||
WOPL_Ins_2op = 0x00,
|
||||
/* Is true four-operator instrument */
|
||||
WOPL_Ins_4op = 0x01,
|
||||
/* Is pseudo four-operator (two 2-operator voices) instrument */
|
||||
WOPL_Ins_Pseudo4op = 0x02,
|
||||
/* Is a blank instrument entry */
|
||||
WOPL_Ins_IsBlank = 0x04,
|
||||
|
||||
/* RythmMode flags mask */
|
||||
WOPL_RhythmModeMask = 0x38,
|
||||
|
||||
/* Mask of the flags range */
|
||||
WOPL_Ins_ALL_MASK = 0x07
|
||||
} WOPL_InstrumentFlags;
|
||||
|
||||
typedef enum WOPL_RhythmMode
|
||||
{
|
||||
/* RythmMode: BassDrum */
|
||||
WOPL_RM_BassDrum = 0x08,
|
||||
/* RythmMode: Snare */
|
||||
WOPL_RM_Snare = 0x10,
|
||||
/* RythmMode: TomTom */
|
||||
WOPL_RM_TomTom = 0x18,
|
||||
/* RythmMode: Cymbell */
|
||||
WOPL_RM_Cymbal = 0x20,
|
||||
/* RythmMode: HiHat */
|
||||
WOPL_RM_HiHat = 0x28
|
||||
} WOPL_RhythmMode;
|
||||
|
||||
/* DEPRECATED: It has typo. Don't use it! */
|
||||
typedef WOPL_RhythmMode WOPL_RythmMode;
|
||||
|
||||
/* Error codes */
|
||||
typedef enum WOPL_ErrorCodes
|
||||
{
|
||||
WOPL_ERR_OK = 0,
|
||||
/* Magic number is not maching */
|
||||
WOPL_ERR_BAD_MAGIC,
|
||||
/* Too short file */
|
||||
WOPL_ERR_UNEXPECTED_ENDING,
|
||||
/* Zero banks count */
|
||||
WOPL_ERR_INVALID_BANKS_COUNT,
|
||||
/* Version of file is newer than supported by current version of library */
|
||||
WOPL_ERR_NEWER_VERSION,
|
||||
/* Out of memory */
|
||||
WOPL_ERR_OUT_OF_MEMORY,
|
||||
/* Given null pointer memory data */
|
||||
WOPL_ERR_NULL_POINTER
|
||||
} WOPL_ErrorCodes;
|
||||
|
||||
/* Operator indeces inside of Instrument Entry */
|
||||
#define WOPL_OP_CARRIER1 0
|
||||
#define WOPL_OP_MODULATOR1 1
|
||||
#define WOPL_OP_CARRIER2 2
|
||||
#define WOPL_OP_MODULATOR2 3
|
||||
|
||||
/* OPL3 Oerators data */
|
||||
typedef struct WOPLOperator
|
||||
{
|
||||
/* AM/Vib/Env/Ksr/FMult characteristics */
|
||||
uint8_t avekf_20;
|
||||
/* Key Scale Level / Total level register data */
|
||||
uint8_t ksl_l_40;
|
||||
/* Attack / Decay */
|
||||
uint8_t atdec_60;
|
||||
/* Systain and Release register data */
|
||||
uint8_t susrel_80;
|
||||
/* Wave form */
|
||||
uint8_t waveform_E0;
|
||||
} WOPLOperator;
|
||||
|
||||
/* Instrument entry */
|
||||
typedef struct WOPLInstrument
|
||||
{
|
||||
/* Title of the instrument */
|
||||
char inst_name[34];
|
||||
/* MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */
|
||||
int16_t note_offset1;
|
||||
/* MIDI note key (half-tone) offset for a second voice in pseudo-4-op mode */
|
||||
int16_t note_offset2;
|
||||
/* MIDI note velocity offset (taken from Apogee TMB format) */
|
||||
int8_t midi_velocity_offset;
|
||||
/* Second voice detune level (taken from DMX OP2) */
|
||||
int8_t second_voice_detune;
|
||||
/* Percussion MIDI base tone number at which this drum will be played */
|
||||
uint8_t percussion_key_number;
|
||||
/* Enum WOPL_InstrumentFlags */
|
||||
uint8_t inst_flags;
|
||||
/* Feedback&Connection register for first and second operators */
|
||||
uint8_t fb_conn1_C0;
|
||||
/* Feedback&Connection register for third and fourth operators */
|
||||
uint8_t fb_conn2_C0;
|
||||
/* Operators register data */
|
||||
WOPLOperator operators[4];
|
||||
/* Millisecond delay of sounding while key is on */
|
||||
uint16_t delay_on_ms;
|
||||
/* Millisecond delay of sounding after key off */
|
||||
uint16_t delay_off_ms;
|
||||
} WOPLInstrument;
|
||||
|
||||
/* Bank entry */
|
||||
typedef struct WOPLBank
|
||||
{
|
||||
/* Name of bank */
|
||||
char bank_name[33];
|
||||
/* MIDI Bank LSB code */
|
||||
uint8_t bank_midi_lsb;
|
||||
/* MIDI Bank MSB code */
|
||||
uint8_t bank_midi_msb;
|
||||
/* Instruments data of this bank */
|
||||
WOPLInstrument ins[128];
|
||||
} WOPLBank;
|
||||
|
||||
/* Instrument data file */
|
||||
typedef struct WOPIFile
|
||||
{
|
||||
/* Version of instrument file */
|
||||
uint16_t version;
|
||||
/* Is this a percussion instrument */
|
||||
uint8_t is_drum;
|
||||
/* Instrument data */
|
||||
WOPLInstrument inst;
|
||||
} WOPIFile;
|
||||
|
||||
/* Bank data file */
|
||||
typedef struct WOPLFile
|
||||
{
|
||||
/* Version of bank file */
|
||||
uint16_t version;
|
||||
/* Count of melodic banks in this file */
|
||||
uint16_t banks_count_melodic;
|
||||
/* Count of percussion banks in this file */
|
||||
uint16_t banks_count_percussion;
|
||||
/* Enum WOPLFileFlags */
|
||||
uint8_t opl_flags;
|
||||
/* Enum WOPL_VolumeModel */
|
||||
uint8_t volume_model;
|
||||
/* dynamically allocated data Melodic banks array */
|
||||
WOPLBank *banks_melodic;
|
||||
/* dynamically allocated data Percussive banks array */
|
||||
WOPLBank *banks_percussive;
|
||||
} WOPLFile;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize blank WOPL data structure with allocated bank data
|
||||
* @param melodic_banks Count of melodic banks
|
||||
* @param percussive_banks Count of percussive banks
|
||||
* @return pointer to heap-allocated WOPL data structure or NULL when out of memory or incorrectly given banks counts
|
||||
*/
|
||||
extern WOPLFile *WOPL_Init(uint16_t melodic_banks, uint16_t percussive_banks);
|
||||
|
||||
/**
|
||||
* @brief Clean up WOPL data file (all allocated bank arrays will be fried too)
|
||||
* @param file pointer to heap-allocated WOPL data structure
|
||||
*/
|
||||
extern void WOPL_Free(WOPLFile *file);
|
||||
|
||||
/**
|
||||
* @brief Compare two bank entries
|
||||
* @param bank1 First bank
|
||||
* @param bank2 Second bank
|
||||
* @return 1 if banks are equal or 0 if there are different
|
||||
*/
|
||||
extern int WOPL_BanksCmp(const WOPLFile *bank1, const WOPLFile *bank2);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Load WOPL bank file from the memory.
|
||||
* WOPL data structure will be allocated. (don't forget to clear it with WOPL_Free() after use!)
|
||||
* @param mem Pointer to memory block contains raw WOPL bank file data
|
||||
* @param length Length of given memory block
|
||||
* @param error pointer to integer to return an error code. Pass NULL if you don't want to use error codes.
|
||||
* @return Heap-allocated WOPL file data structure or NULL if any error has occouped
|
||||
*/
|
||||
extern WOPLFile *WOPL_LoadBankFromMem(void *mem, size_t length, int *error);
|
||||
|
||||
/**
|
||||
* @brief Load WOPI instrument file from the memory.
|
||||
* You must allocate WOPIFile structure by yourself and give the pointer to it.
|
||||
* @param file Pointer to destinition WOPIFile structure to fill it with parsed data.
|
||||
* @param mem Pointer to memory block contains raw WOPI instrument file data
|
||||
* @param length Length of given memory block
|
||||
* @return 0 if no errors occouped, or an error code of WOPL_ErrorCodes enumeration
|
||||
*/
|
||||
extern int WOPL_LoadInstFromMem(WOPIFile *file, void *mem, size_t length);
|
||||
|
||||
/**
|
||||
* @brief Calculate the size of the output memory block
|
||||
* @param file Heap-allocated WOPL file data structure
|
||||
* @param version Destinition version of the file
|
||||
* @return Size of the raw WOPL file data
|
||||
*/
|
||||
extern size_t WOPL_CalculateBankFileSize(WOPLFile *file, uint16_t version);
|
||||
|
||||
/**
|
||||
* @brief Calculate the size of the output memory block
|
||||
* @param file Pointer to WOPI file data structure
|
||||
* @param version Destinition version of the file
|
||||
* @return Size of the raw WOPI file data
|
||||
*/
|
||||
extern size_t WOPL_CalculateInstFileSize(WOPIFile *file, uint16_t version);
|
||||
|
||||
/**
|
||||
* @brief Write raw WOPL into given memory block
|
||||
* @param file Heap-allocated WOPL file data structure
|
||||
* @param dest_mem Destinition memory block pointer
|
||||
* @param length Length of destinition memory block
|
||||
* @param version Wanted WOPL version
|
||||
* @param force_gm Force GM set in saved bank file
|
||||
* @return Error code or 0 on success
|
||||
*/
|
||||
extern int WOPL_SaveBankToMem(WOPLFile *file, void *dest_mem, size_t length, uint16_t version, uint16_t force_gm);
|
||||
|
||||
/**
|
||||
* @brief Write raw WOPI into given memory block
|
||||
* @param file Pointer to WOPI file data structure
|
||||
* @param dest_mem Destinition memory block pointer
|
||||
* @param length Length of destinition memory block
|
||||
* @param version Wanted WOPI version
|
||||
* @return Error code or 0 on success
|
||||
*/
|
||||
extern int WOPL_SaveInstToMem(WOPIFile *file, void *dest_mem, size_t length, uint16_t version);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* WOPL_FILE_H */
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
// HEADER FILES ------------------------------------------------------------
|
||||
|
||||
#include "i_musicinterns.h"
|
||||
#include "adlmidi/adlmidi.h"
|
||||
#include "adlmidi.h"
|
||||
#include "i_soundfont.h"
|
||||
|
||||
class ADLMIDIDevice : public SoftSynthMIDIDevice
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue