aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/adlmidi.cpp22
-rw-r--r--src/adlmidi_load.cpp2
-rw-r--r--src/adlmidi_midiplay.cpp15
-rw-r--r--src/adlmidi_opl3.cpp17
-rw-r--r--src/adlmidi_private.cpp7
-rw-r--r--src/adlmidi_private.hpp16
-rw-r--r--src/chips/dosbox_opl3.cpp4
-rw-r--r--src/chips/dosbox_opl3.h1
-rw-r--r--src/chips/nuked_opl3.h1
-rw-r--r--src/chips/nuked_opl3_v174.h1
-rw-r--r--src/chips/opl_chip_base.h30
-rw-r--r--src/chips/opl_chip_base.tcc84
12 files changed, 184 insertions, 16 deletions
diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp
index a0c7d8a..9210b5b 100644
--- a/src/adlmidi.cpp
+++ b/src/adlmidi.cpp
@@ -511,11 +511,28 @@ ADLMIDI_EXPORT int adl_switchEmulator(struct ADL_MIDIPlayer *device, int emulato
adl_reset(device);
return 0;
}
- play->setErrorString("OPN2 MIDI: Unknown emulation core!");
+ play->setErrorString("OPL3 MIDI: Unknown emulation core!");
}
return -1;
}
+
+ADLMIDI_EXPORT int adl_setRunAtPcmRate(ADL_MIDIPlayer *device, int enabled)
+{
+ if(device)
+ {
+ MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
+ if(play)
+ {
+ play->m_setup.runAtPcmRate = (enabled != 0);
+ adl_reset(device);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+
ADLMIDI_EXPORT const char *adl_linkedLibraryVersion()
{
#if !defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
@@ -574,7 +591,8 @@ ADLMIDI_EXPORT void adl_reset(struct ADL_MIDIPlayer *device)
return;
MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
play->m_setup.tick_skip_samples_delay = 0;
- play->opl.Reset(play->m_setup.emulator, play->m_setup.PCM_RATE);
+ play->opl.runAtPcmRate = play->m_setup.runAtPcmRate;
+ play->opl.Reset(play->m_setup.emulator, play->m_setup.PCM_RATE, play);
play->ch.clear();
play->ch.resize((size_t)play->opl.NumChannels);
}
diff --git a/src/adlmidi_load.cpp b/src/adlmidi_load.cpp
index 9e19ab6..7f69e25 100644
--- a/src/adlmidi_load.cpp
+++ b/src/adlmidi_load.cpp
@@ -714,7 +714,7 @@ riffskip:
return false;
}
- opl.Reset(m_setup.emulator, m_setup.PCM_RATE); // Reset OPL3 chip
+ opl.Reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPL3 chip
//opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously)
ch.clear();
ch.resize(opl.NumChannels);
diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp
index 0623ce7..89c1a73 100644
--- a/src/adlmidi_midiplay.cpp
+++ b/src/adlmidi_midiplay.cpp
@@ -680,7 +680,8 @@ bool MIDIplay::buildTrackData()
MIDIplay::MIDIplay(unsigned long sampleRate):
cmf_percussion_mode(false),
- m_arpeggioCounter(0)
+ m_arpeggioCounter(0),
+ m_audioTickCounter(0)
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
, fullSongTimeLength(0.0),
postSongWaitDelay(1.0),
@@ -696,6 +697,7 @@ MIDIplay::MIDIplay(unsigned long sampleRate):
devices.clear();
m_setup.emulator = ADLMIDI_EMU_NUKED;
+ m_setup.runAtPcmRate = false;
m_setup.PCM_RATE = sampleRate;
m_setup.mindelay = 1.0 / (double)m_setup.PCM_RATE;
@@ -726,6 +728,8 @@ void MIDIplay::applySetup()
{
m_setup.tick_skip_samples_delay = 0;
+ opl.runAtPcmRate = m_setup.runAtPcmRate;
+
if(opl.AdlBank != ~0u)
opl.dynamic_bank_setup = adlbanksetup[m_setup.AdlBank];
@@ -752,7 +756,7 @@ void MIDIplay::applySetup()
opl.NumFourOps = m_setup.NumFourOps;
cmf_percussion_mode = false;
- opl.Reset(m_setup.emulator, m_setup.PCM_RATE);
+ opl.Reset(m_setup.emulator, m_setup.PCM_RATE, this);
ch.clear();
ch.resize(opl.NumChannels);
@@ -1435,6 +1439,13 @@ void MIDIplay::realTime_panic()
KillSustainingNotes(-1, -1);
}
+void MIDIplay::AudioTick(uint32_t chipId, uint32_t /*rate*/)
+{
+ if(chipId != 0) // do first chip ticks only
+ return;
+
+ /*uint32_t tickNumber = */m_audioTickCounter++;
+}
void MIDIplay::NoteUpdate(uint16_t MidCh,
MIDIplay::MIDIchannel::activenoteiterator i,
diff --git a/src/adlmidi_opl3.cpp b/src/adlmidi_opl3.cpp
index b4db049..ea479c8 100644
--- a/src/adlmidi_opl3.cpp
+++ b/src/adlmidi_opl3.cpp
@@ -484,7 +484,7 @@ void OPL3::ClearChips()
}
#endif
-void OPL3::Reset(int emulator, unsigned long PCM_RATE)
+void OPL3::Reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler)
{
#ifndef ADLMIDI_HW_OPL
ClearChips();
@@ -520,25 +520,32 @@ void OPL3::Reset(int emulator, unsigned long PCM_RATE)
for(size_t i = 0; i < NumCards; ++i)
{
+ OPLChipBase *chip;
+
#ifndef ADLMIDI_HW_OPL
switch(emulator)
{
default:
#ifndef ADLMIDI_DISABLE_NUKED_EMULATOR
case ADLMIDI_EMU_NUKED: /* Latest Nuked OPL3 */
- cardsOP2[i].reset(new NukedOPL3());
+ chip = new NukedOPL3;
break;
case ADLMIDI_EMU_NUKED_174: /* Old Nuked OPL3 1.4.7 modified and optimized */
- cardsOP2[i].reset(new NukedOPL3v174());
+ chip = new NukedOPL3v174;
break;
#endif
#ifndef ADLMIDI_DISABLE_DOSBOX_EMULATOR
case ADLMIDI_EMU_DOSBOX:
- cardsOP2[i].reset(new DosBoxOPL3());
+ chip = new DosBoxOPL3;
break;
#endif
}
- cardsOP2[i]->setRate((uint32_t)PCM_RATE);
+ cardsOP2[i].reset(chip);
+ chip->setChipId(i);
+ chip->setRate((uint32_t)PCM_RATE);
+ if(runAtPcmRate)
+ chip->setRunningAtPcmRate(true);
+ chip->setAudioTickHandlerInstance(audioTickHandler);
#endif // ADLMIDI_HW_OPL
for(unsigned a = 0; a < 18; ++a) Poke(i, 0xB0 + Channels[a], 0x00);
diff --git a/src/adlmidi_private.cpp b/src/adlmidi_private.cpp
index 77319d7..1ee7c4a 100644
--- a/src/adlmidi_private.cpp
+++ b/src/adlmidi_private.cpp
@@ -25,6 +25,13 @@
std::string ADLMIDI_ErrorString;
+// Generator callback on audio rate ticks
+
+void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate)
+{
+ reinterpret_cast<MIDIplay *>(instance)->AudioTick(chipId, rate);
+}
+
int adlRefreshNumCards(ADL_MIDIPlayer *device)
{
unsigned n_fourop[2] = {0, 0}, n_total[2] = {0, 0};
diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp
index 2499bad..0614c7b 100644
--- a/src/adlmidi_private.hpp
+++ b/src/adlmidi_private.hpp
@@ -245,6 +245,8 @@ public:
bool AdlPercussionMode;
//! Carriers-only are scaled by default by volume level. This flag will tell to scale modulators too.
bool ScaleModulators;
+ //! Run emulator at PCM rate if that possible. Reduces sounding accuracy, but decreases CPU usage on lower rates.
+ bool runAtPcmRate;
// ! Required to play CMF files. Can be turned on by using of "CMF" volume model
//bool LogarithmicVolumes; //[REPLACED WITH "m_volumeScale == VOLUME_NATIVE", DEPRECATED!!!]
// ! Required to play EA-MUS files [REPLACED WITH "m_musicMode", DEPRECATED!!!]
@@ -294,7 +296,7 @@ public:
#ifndef ADLMIDI_HW_OPL
void ClearChips();
#endif
- void Reset(int emulator, unsigned long PCM_RATE);
+ void Reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler);
};
@@ -933,6 +935,7 @@ public:
struct Setup
{
int emulator;
+ bool runAtPcmRate;
unsigned int AdlBank;
unsigned int NumFourOps;
unsigned int NumCards;
@@ -984,6 +987,9 @@ private:
//! Counter of arpeggio processing
size_t m_arpeggioCounter;
+ //! Audio tick counter
+ uint32_t m_audioTickCounter;
+
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
std::vector<std::vector<uint8_t> > TrackData;
@@ -1181,6 +1187,9 @@ public:
void realTime_panic();
+ // Audio rate tick handler
+ void AudioTick(uint32_t chipId, uint32_t rate);
+
private:
enum
{
@@ -1249,6 +1258,11 @@ struct FourChars
};
*/
+#if !defined(ADLMIDI_AUDIO_TICK_HANDLER)
+#error The audio tick handler must be enabled!
+#endif
+
+extern void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate);
extern int adlRefreshNumCards(ADL_MIDIPlayer *device);
diff --git a/src/chips/dosbox_opl3.cpp b/src/chips/dosbox_opl3.cpp
index af4cb08..30fa38e 100644
--- a/src/chips/dosbox_opl3.cpp
+++ b/src/chips/dosbox_opl3.cpp
@@ -23,7 +23,7 @@ void DosBoxOPL3::setRate(uint32_t rate)
DBOPL::Handler *chip_r = reinterpret_cast<DBOPL::Handler*>(m_chip);
chip_r->~Handler();
new(chip_r) DBOPL::Handler;
- chip_r->Init(49716);
+ chip_r->Init(effectiveRate());
}
void DosBoxOPL3::reset()
@@ -32,7 +32,7 @@ void DosBoxOPL3::reset()
DBOPL::Handler *chip_r = reinterpret_cast<DBOPL::Handler*>(m_chip);
chip_r->~Handler();
new(chip_r) DBOPL::Handler;
- chip_r->Init(49716);
+ chip_r->Init(effectiveRate());
}
void DosBoxOPL3::writeReg(uint16_t addr, uint8_t data)
diff --git a/src/chips/dosbox_opl3.h b/src/chips/dosbox_opl3.h
index f4c68da..1928026 100644
--- a/src/chips/dosbox_opl3.h
+++ b/src/chips/dosbox_opl3.h
@@ -10,6 +10,7 @@ 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;
diff --git a/src/chips/nuked_opl3.h b/src/chips/nuked_opl3.h
index 25d9ed5..1b34e9a 100644
--- a/src/chips/nuked_opl3.h
+++ b/src/chips/nuked_opl3.h
@@ -10,6 +10,7 @@ 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;
diff --git a/src/chips/nuked_opl3_v174.h b/src/chips/nuked_opl3_v174.h
index b9c5ba6..f14221f 100644
--- a/src/chips/nuked_opl3_v174.h
+++ b/src/chips/nuked_opl3_v174.h
@@ -10,6 +10,7 @@ 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;
diff --git a/src/chips/opl_chip_base.h b/src/chips/opl_chip_base.h
index 5721a81..879d6da 100644
--- a/src/chips/opl_chip_base.h
+++ b/src/chips/opl_chip_base.h
@@ -13,15 +13,33 @@
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;
@@ -49,13 +67,25 @@ 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);
diff --git a/src/chips/opl_chip_base.tcc b/src/chips/opl_chip_base.tcc
index 58145bc..48ad103 100644
--- a/src/chips/opl_chip_base.tcc
+++ b/src/chips/opl_chip_base.tcc
@@ -5,9 +5,22 @@
#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)
{
}
@@ -20,7 +33,12 @@ inline OPLChipBase::~OPLChipBase()
template <class T>
OPLChipBaseT<T>::OPLChipBaseT()
- : OPLChipBase()
+ : OPLChipBase(),
+ m_runningAtPcmRate(false)
+#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
+ ,
+ m_audioTickHandlerInstance(NULL)
+#endif
{
#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER)
m_resampler = new VResampler;
@@ -37,6 +55,33 @@ OPLChipBaseT<T>::~OPLChipBaseT()
}
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;
@@ -48,6 +93,12 @@ void OPLChipBaseT<T>::setRate(uint32_t rate)
}
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();
@@ -119,6 +170,15 @@ void OPLChipBaseT<T>::generateAndMix32(int32_t *output, size_t frames)
}
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)
@@ -147,6 +207,15 @@ void OPLChipBaseT<T>::resetResampler()
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;
@@ -159,7 +228,7 @@ void OPLChipBaseT<T>::resampledGenerate(int32_t *output)
while(rsm->process(), rsm->out_count != 0)
{
int16_t in[2];
- static_cast<T *>(this)->nativeGenerate(in);
+ static_cast<T *>(this)->nativeTick(in);
f_in[0] = scale * (float)in[0];
f_in[1] = scale * (float)in[1];
rsm->inp_count = 1;
@@ -174,6 +243,15 @@ void OPLChipBaseT<T>::resampledGenerate(int32_t *output)
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)
@@ -181,7 +259,7 @@ void OPLChipBaseT<T>::resampledGenerate(int32_t *output)
m_oldsamples[0] = m_samples[0];
m_oldsamples[1] = m_samples[1];
int16_t buffer[2];
- static_cast<T *>(this)->nativeGenerate(buffer);
+ static_cast<T *>(this)->nativeTick(buffer);
m_samples[0] = buffer[0] * T::resamplerPreAmplify;
m_samples[1] = buffer[1] * T::resamplerPreAmplify;
samplecnt -= rateratio;