diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/adlmidi.cpp | 22 | ||||
-rw-r--r-- | src/adlmidi_load.cpp | 2 | ||||
-rw-r--r-- | src/adlmidi_midiplay.cpp | 15 | ||||
-rw-r--r-- | src/adlmidi_opl3.cpp | 17 | ||||
-rw-r--r-- | src/adlmidi_private.cpp | 7 | ||||
-rw-r--r-- | src/adlmidi_private.hpp | 16 | ||||
-rw-r--r-- | src/chips/dosbox_opl3.cpp | 4 | ||||
-rw-r--r-- | src/chips/dosbox_opl3.h | 1 | ||||
-rw-r--r-- | src/chips/nuked_opl3.h | 1 | ||||
-rw-r--r-- | src/chips/nuked_opl3_v174.h | 1 | ||||
-rw-r--r-- | src/chips/opl_chip_base.h | 30 | ||||
-rw-r--r-- | src/chips/opl_chip_base.tcc | 84 |
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; |