Refactors, bug fixes, lotsa things

This commit is contained in:
Jack Andersen 2016-05-13 18:46:39 -10:00
parent 1fb9ef3239
commit bee8719d4e
26 changed files with 777 additions and 339 deletions

View File

@ -61,6 +61,6 @@ add_library(amuse
${EXTRAS})
if(TARGET boo)
add_executable(amusetool WIN32 driver/main.cpp)
target_link_libraries(amusetool amuse boo ${BOO_SYS_LIBS} logvisor athena-core)
add_executable(amuseplay WIN32 driver/main.cpp)
target_link_libraries(amuseplay amuse boo ${BOO_SYS_LIBS} logvisor athena-core)
endif()

View File

@ -10,7 +10,7 @@
#include <thread>
#include <map>
static logvisor::Module Log("amusetool");
static logvisor::Module Log("amuseplay");
static amuse::IntrusiveAudioGroupData LoadFromArgs(int argc, const boo::SystemChar** argv,
std::string& descOut, bool& good)
@ -161,9 +161,11 @@ struct AppCallback : boo::IApplicationCallback
/* SFX playback selection */
int m_sfxId = -1;
amuse::Voice* m_vox = nullptr;
std::shared_ptr<amuse::Voice> m_vox;
/* Control state */
float m_volume = 1.f;
bool m_updateDisp = false;
bool m_running = true;
bool m_wantsNext = false;
bool m_wantsPrev = false;
@ -180,8 +182,7 @@ struct AppCallback : boo::IApplicationCallback
"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░\n"
"<left/right>: cycle MIDI setup / channel, <up/down>: volume\n"
"<tab>: sustain pedal, <window-Y>: pitch wheel, <window-X>: mod wheel\n"
"<Z/X>: octave, <C/V>: velocity\n"
"<Q>: quit\n");
"<Z/X>: octave, <C/V>: velocity, <Q>: quit\n");
std::map<int, const std::array<amuse::SongGroupIndex::MIDISetup, 16>*> sortEntries
(index.m_midiSetups.cbegin(), index.m_midiSetups.cend());
@ -207,31 +208,79 @@ struct AppCallback : boo::IApplicationCallback
}
}
void UpdateSFXDisplay()
{
bool playing = m_vox && m_vox->state() == amuse::VoiceState::Playing;
printf("\r "
"\r %c SFX %d, VOL: %d%%\r", playing ? '>' : ' ',
m_sfxId, int(std::rint(m_volume * 100)));
fflush(stdout);
}
void SelectSFX(int sfxId)
{
m_sfxId = sfxId;
UpdateSFXDisplay();
}
void SFXLoop(const amuse::AudioGroup& group,
const amuse::SFXGroupIndex& index)
{
printf("<space>: keyon/keyon, <left/right>: cycle SFX, <up/down>: volume\n"
"<Q>: quit\n");
printf("<space>: keyon/keyon, <left/right>: cycle SFX, <up/down>: volume, <Q>: quit\n");
std::map<uint16_t, const amuse::SFXGroupIndex::SFXEntry*> sortEntries
(index.m_sfxEntries.cbegin(), index.m_sfxEntries.cend());
auto sfxIt = sortEntries.cbegin();
if (sfxIt != sortEntries.cend())
{
m_sfxId = sfxIt->first;
m_vox = m_engine->fxStart(m_sfxId, 1.f, 0.f);
}
SelectSFX(sfxIt->first);
while (m_running)
{
m_events.dispatchEvents();
m_engine->pumpEngine();
if (m_wantsNext)
{
m_wantsNext = false;
auto nextIt = sfxIt;
++nextIt;
if (nextIt != sortEntries.cend())
{
++sfxIt;
SelectSFX(sfxIt->first);
m_updateDisp = false;
}
}
if (m_wantsPrev)
{
m_wantsPrev = false;
if (sfxIt != sortEntries.cbegin())
{
--sfxIt;
SelectSFX(sfxIt->first);
m_updateDisp = false;
}
}
if (m_updateDisp)
{
m_updateDisp = false;
UpdateSFXDisplay();
}
m_win->waitForRetrace();
}
}
void charKeyDown(unsigned long charCode)
{
if (charCode == 'q' || charCode == 'Q')
{
m_running = false;
return;
}
if (m_sfxGroup)
{
switch (charCode)
@ -240,7 +289,8 @@ struct AppCallback : boo::IApplicationCallback
if (m_vox && m_vox->state() == amuse::VoiceState::Playing)
m_vox->keyOff();
else if (m_sfxId != -1)
m_vox = m_engine->fxStart(m_sfxId, 1.f, 0.f);
m_vox = m_engine->fxStart(m_sfxId, m_volume, 0.f);
m_updateDisp = true;
default: break;
}
}
@ -256,10 +306,24 @@ struct AppCallback : boo::IApplicationCallback
int appMain(boo::IApplication* app)
{
/* Event window */
m_win = app->newWindow("amusetool", 1);
m_win->setWindowFrame(100, 100, 100, 100);
m_win = app->newWindow("amuseplay", 1);
m_win->setCallback(&m_events);
m_win->setWindowFrame(100, 100, 100, 100);
m_win->setStyle(~boo::EWindowStyle::Resize);
m_win->showWindow();
boo::ITextureR* tex = nullptr;
boo::GraphicsDataToken gfxToken =
m_win->getMainContextDataFactory()->commitTransaction(
[&](boo::IGraphicsDataFactory::Context& ctx) -> bool
{
tex = ctx.newRenderTexture(100, 100, false, false);
return true;
});
boo::IGraphicsCommandQueue* q = m_win->getCommandQueue();
q->setRenderTarget(tex);
q->clearTarget();
q->resolveDisplay(tex);
q->execute();
/* Load data */
std::string desc;
@ -267,7 +331,7 @@ struct AppCallback : boo::IApplicationCallback
amuse::IntrusiveAudioGroupData data = LoadFromArgs(m_argc, m_argv, desc, good);
if (!good)
Log.report(logvisor::Fatal, "incomplete data in args");
printf("Found '%s' Audio Group data\n", desc.c_str());
Log.report(logvisor::Info, "Found '%s' Audio Group data", desc.c_str());
/* Load project to assemble group list */
amuse::AudioGroupProject proj(data.getProj());
@ -376,6 +440,20 @@ void EventCallback::specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods,
case boo::ESpecialKey::Right:
m_app.m_wantsNext = true;
break;
case boo::ESpecialKey::Up:
if (m_app.m_volume < 1.f)
m_app.m_volume = amuse::clamp(0.f, m_app.m_volume + 0.05f, 1.f);
if (m_app.m_vox)
m_app.m_vox->setVolume(m_app.m_volume);
m_app.m_updateDisp = true;
break;
case boo::ESpecialKey::Down:
if (m_app.m_volume > 0.f)
m_app.m_volume = amuse::clamp(0.f, m_app.m_volume - 0.05f, 1.f);
if (m_app.m_vox)
m_app.m_vox->setVolume(m_app.m_volume);
m_app.m_updateDisp = true;
break;
default: break;
}
}
@ -393,7 +471,7 @@ int main(int argc, const boo::SystemChar** argv)
logvisor::RegisterConsoleLogger();
AppCallback app(argc, argv);
int ret = boo::ApplicationRun(boo::IApplication::EPlatformType::Auto,
app, _S("amusetool"), _S("Amuse Tool"), argc, argv);
app, _S("amuseplay"), _S("Amuse Player"), argc, argv);
printf("IM DYING!!\n");
return ret;
}

View File

@ -25,8 +25,6 @@ public:
AudioGroup(int groupId, const AudioGroupData& data);
int groupId() const {return m_groupId;}
bool sfxInGroup(int sfxId) const;
bool songInGroup(int songId) const;
const Sample* getSample(int sfxId) const;
const unsigned char* getSampleData(uint32_t offset) const;

View File

@ -57,6 +57,8 @@ public:
BooBackendSubmix(boo::IAudioSubmix& parent, Submix& clientSmx);
void setChannelGains(const float gains[8]);
std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch);
double getSampleRate() const;
SubmixFormat getSampleFormat() const;
};
/** Backend voice allocator implementation for boo mixer */
@ -68,6 +70,7 @@ public:
std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch);
std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx);
AudioChannelSet getAvailableSet();
void pumpAndMixVoices();
};
}

View File

@ -8,8 +8,14 @@ namespace amuse
{
class ChannelMap;
class EffectBaseTypeless
{
public:
virtual ~EffectBaseTypeless() = default;
};
template <typename T>
class EffectBase
class EffectBase : public EffectBaseTypeless
{
public:
virtual void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)=0;

View File

@ -7,12 +7,51 @@
namespace amuse
{
template <typename T>
class EffectChorusImp;
#define AMUSE_CHORUS_NUM_BLOCKS 3
/** Mixes the audio back into itself after continuously-varying delay */
class EffectChorus
{
uint32_t x90_baseDelay; /**< [5, 15] minimum value (in ms) for computed delay */
uint32_t x94_variation; /**< [0, 5] time error (in ms) to set delay within */
uint32_t x98_period; /**< [500, 10000] time (in ms) of one delay-shift cycle */
bool m_dirty = true; /**< needs update of internal parameter data */
template <typename T>
friend class EffectChorusImp;
EffectChorus(uint32_t baseDelay, uint32_t variation, uint32_t period);
public:
template <typename T>
using ImpType = EffectChorusImp<T>;
void setBaseDelay(uint32_t baseDelay)
{
baseDelay = clamp(5u, baseDelay, 15u);
x90_baseDelay = baseDelay;
m_dirty = true;
}
void setVariation(uint32_t variation)
{
variation = clamp(0u, variation, 5u);
x94_variation = variation;
m_dirty = true;
}
void setPeriod(uint32_t period)
{
period = clamp(500u, period, 10000u);
x98_period = period;
m_dirty = true;
}
};
/** Type-specific implementation of chorus effect */
template <typename T>
class EffectChorus : public EffectBase<T>
class EffectChorusImp : public EffectBase<T>, public EffectChorus
{
T* x0_lastChans[8][AMUSE_CHORUS_NUM_BLOCKS]; /**< Evenly-allocated pointer-table for each channel's delay */
@ -42,41 +81,15 @@ class EffectChorus : public EffectBase<T>
void doSrc2(size_t blockSamples, size_t chanCount);
} x6c_src;
uint32_t x90_baseDelay; /**< [5, 15] minimum value (in ms) for computed delay */
uint32_t x94_variation; /**< [0, 5] time error (in ms) to set delay within */
uint32_t x98_period; /**< [500, 10000] time (in ms) of one delay-shift cycle */
uint32_t m_sampsPerMs; /**< canonical count of samples per ms for the current backend */
uint32_t m_blockSamples; /**< count of samples in a 5ms block */
bool m_dirty = true; /**< needs update of internal parameter data */
void _update();
public:
~EffectChorus();
EffectChorus(uint32_t baseDelay, uint32_t variation, uint32_t period, double sampleRate);
~EffectChorusImp();
EffectChorusImp(uint32_t baseDelay, uint32_t variation, uint32_t period, double sampleRate);
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
void setBaseDelay(uint32_t baseDelay)
{
baseDelay = clamp(5u, baseDelay, 15u);
x90_baseDelay = baseDelay;
m_dirty = true;
}
void setVariation(uint32_t variation)
{
variation = clamp(0u, variation, 5u);
x94_variation = variation;
m_dirty = true;
}
void setPeriod(uint32_t period)
{
period = clamp(500u, period, 10000u);
x98_period = period;
m_dirty = true;
}
};
}

View File

@ -8,29 +8,20 @@
namespace amuse
{
template <typename T>
class EffectDelayImp;
/** Mixes the audio back into itself after specified delay */
template <typename T>
class EffectDelay : public EffectBase<T>
class EffectDelay
{
uint32_t x0_currentSize[8]; /**< per-channel delay-line buffer sizes */
uint32_t xc_currentPos[8]; /**< per-channel block-index */
uint32_t x18_currentFeedback[8]; /**< [0, 128] feedback attenuator */
uint32_t x24_currentOutput[8]; /**< [0, 128] total attenuator */
std::unique_ptr<T[]> x30_chanLines[8]; /**< delay-line buffers for each channel */
protected:
uint32_t x3c_delay[8]; /**< [10, 5000] time in ms of each channel's delay */
uint32_t x48_feedback[8]; /**< [0, 100] percent to mix delayed signal with input signal */
uint32_t x54_output[8]; /**< [0, 100] total output percent */
uint32_t m_sampsPerMs; /**< canonical count of samples per ms for the current backend */
uint32_t m_blockSamples; /**< count of samples in a 5ms block */
bool m_dirty = true; /**< needs update of internal parameter data */
void _update();
public:
EffectDelay(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput, double sampleRate);
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
template <typename T>
using ImpType = EffectDelayImp<T>;
void setDelay(uint32_t delay)
{
@ -75,6 +66,25 @@ public:
}
};
/** Type-specific implementation of chorus effect */
template <typename T>
class EffectDelayImp : public EffectBase<T>, public EffectDelay
{
uint32_t x0_currentSize[8]; /**< per-channel delay-line buffer sizes */
uint32_t xc_currentPos[8]; /**< per-channel block-index */
uint32_t x18_currentFeedback[8]; /**< [0, 128] feedback attenuator */
uint32_t x24_currentOutput[8]; /**< [0, 128] total attenuator */
std::unique_ptr<T[]> x30_chanLines[8]; /**< delay-line buffers for each channel */
uint32_t m_sampsPerMs; /**< canonical count of samples per ms for the current backend */
uint32_t m_blockSamples; /**< count of samples in a 5ms block */
void _update();
public:
EffectDelayImp(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput, double sampleRate);
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
};
}
#endif // __AMUSE_EFFECTDELAY_HPP__

View File

@ -21,46 +21,45 @@ struct ReverbDelayLine
void setdelay(int32_t delay);
};
template <typename T>
class EffectReverbStdImp;
template <typename T>
class EffectReverbHiImp;
/** Reverb effect with configurable reflection filtering */
template <typename T, size_t AP, size_t C>
class EffectReverb : public EffectBase<T>
class EffectReverb
{
protected:
ReverbDelayLine x0_x0_AP[8][AP] = {}; /**< All-pass delay lines */
ReverbDelayLine x78_xb4_C[8][C] = {}; /**< Comb delay lines */
float xf0_x168_allPassCoef = 0.f; /**< All-pass mix coefficient */
float xf4_x16c_combCoef[8][C] = {}; /**< Comb mix coefficients */
float x10c_x190_lpLastout[8] = {}; /**< Last low-pass results */
float x118_x19c_level = 0.f; /**< Internal wet/dry mix factor */
float x11c_x1a0_damping = 0.f; /**< Low-pass damping */
int32_t x120_x1a4_preDelayTime = 0; /**< Sample count of pre-delay */
std::unique_ptr<float[]> x124_x1ac_preDelayLine[8]; /**< Dedicated pre-delay buffers */
float* x130_x1b8_preDelayPtr[8] = {}; /**< Current pre-delay pointers */
float x140_x1c8_coloration; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a room */
float x144_x1cc_mix; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */
float x148_x1d0_time; /**< [0.01, 10.0] time in seconds for reflection decay */
float x14c_x1d4_damping; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */
float x150_x1d8_preDelay; /**< [0.0, 0.1] time in seconds before initial reflection heard */
double m_sampleRate; /**< copy of sample rate */
bool m_dirty = true; /**< needs update of internal parameter data */
void _update();
public:
template <typename T>
friend class EffectReverbStdImp;
template <typename T>
friend class EffectReverbHiImp;
EffectReverb(float coloration, float mix, float time,
float damping, float preDelay, double sampleRate);
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
float damping, float preDelay);
public:
template <typename T>
using ImpType = EffectReverbStdImp<T>;
void setColoration(float coloration)
{
x140_x1c8_coloration = clamp(0.f, coloration, 1.f);
m_dirty = true;
}
void setMix(float mix)
{
x144_x1cc_mix = clamp(0.f, mix, 1.f);
m_dirty = true;
}
void setTime(float time)
{
x148_x1d0_time = clamp(0.01f, time, 10.f);
@ -71,6 +70,7 @@ public:
x14c_x1d4_damping = clamp(0.f, damping, 1.f);
m_dirty = true;
}
void setPreDelay(float preDelay)
{
x150_x1d8_preDelay = clamp(0.f, preDelay, 0.1f);
@ -78,37 +78,76 @@ public:
}
};
/** Standard-quality 2-stage reverb */
template <typename T>
class EffectReverbStd : public EffectReverb<T, 2, 2>
/** Reverb effect with configurable reflection filtering, adds per-channel low-pass and crosstalk */
class EffectReverbHi : public EffectReverb
{
public:
EffectReverbStd(float coloration, float mix, float time,
float damping, float preDelay, double sampleRate);
};
/** High-quality 3-stage reverb with per-channel low-pass and crosstalk */
template <typename T>
class EffectReverbHi : public EffectReverb<T, 2, 3>
{
ReverbDelayLine x78_LP[8] = {}; /**< Per-channel low-pass delay-lines */
float x1a8_internalCrosstalk;
float x1dc_crosstalk; /**< [0.0, 1.0] factor defining how much reflections are allowed to bleed to other channels */
void _update();
void _handleReverb(T* audio, int chanIdx, int chanCount, int sampleCount);
void _doCrosstalk(T* audio, float wet, float dry, int chanCount, int sampleCount);
public:
template <typename T>
friend class EffectReverbHiImp;
EffectReverbHi(float coloration, float mix, float time,
float damping, float preDelay, float crosstalk, double sampleRate);
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
float damping, float preDelay, float crosstalk);
public:
template <typename T>
using ImpType = EffectReverbHiImp<T>;
void setCrosstalk(float crosstalk)
{
x1dc_crosstalk = clamp(0.f, crosstalk, 1.f);
EffectReverb<T, 2, 3>::m_dirty = true;
m_dirty = true;
}
};
/** Standard-quality 2-stage reverb */
template <typename T>
class EffectReverbStdImp : public EffectBase<T>, public EffectReverb
{
ReverbDelayLine x0_AP[8][2] = {}; /**< All-pass delay lines */
ReverbDelayLine x78_C[8][2] = {}; /**< Comb delay lines */
float xf0_allPassCoef = 0.f; /**< All-pass mix coefficient */
float xf4_combCoef[8][2] = {}; /**< Comb mix coefficients */
float x10c_lpLastout[8] = {}; /**< Last low-pass results */
float x118_level = 0.f; /**< Internal wet/dry mix factor */
float x11c_damping = 0.f; /**< Low-pass damping */
int32_t x120_preDelayTime = 0; /**< Sample count of pre-delay */
std::unique_ptr<float[]> x124_preDelayLine[8]; /**< Dedicated pre-delay buffers */
float* x130_preDelayPtr[8] = {}; /**< Current pre-delay pointers */
double m_sampleRate; /**< copy of sample rate */
void _update();
public:
EffectReverbStdImp(float coloration, float mix, float time,
float damping, float preDelay, double sampleRate);
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
};
/** High-quality 3-stage reverb with per-channel low-pass and crosstalk */
template <typename T>
class EffectReverbHiImp : public EffectBase<T>, public EffectReverbHi
{
ReverbDelayLine x0_AP[8][2] = {}; /**< All-pass delay lines */
ReverbDelayLine x78_LP[8] = {}; /**< Per-channel low-pass delay-lines */
ReverbDelayLine xb4_C[8][3] = {}; /**< Comb delay lines */
float x168_allPassCoef = 0.f; /**< All-pass mix coefficient */
float x16c_combCoef[8][3] = {}; /**< Comb mix coefficients */
float x190_lpLastout[8] = {}; /**< Last low-pass results */
float x19c_level = 0.f; /**< Internal wet/dry mix factor */
float x1a0_damping = 0.f; /**< Low-pass damping */
int32_t x1a4_preDelayTime = 0; /**< Sample count of pre-delay */
std::unique_ptr<float[]> x1ac_preDelayLine[8]; /**< Dedicated pre-delay buffers */
float* x1b8_preDelayPtr[8] = {}; /**< Current pre-delay pointers */
float x1a8_internalCrosstalk;
double m_sampleRate; /**< copy of sample rate */
void _update();
void _handleReverb(T* audio, int chanIdx, int chanCount, int sampleCount);
void _doCrosstalk(T* audio, float wet, float dry, int chanCount, int sampleCount);
public:
EffectReverbHiImp(float coloration, float mix, float time,
float damping, float preDelay, float crosstalk, double sampleRate);
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
};
}
#endif // __AMUSE_EFFECTREVERB_HPP__

View File

@ -26,18 +26,19 @@ class Engine
IBackendVoiceAllocator& m_backend;
std::unordered_map<int, std::unique_ptr<AudioGroup>> m_audioGroups;
std::list<Voice> m_activeVoices;
std::list<Emitter> m_activeEmitters;
std::list<Sequencer> m_activeSequencers;
std::list<std::shared_ptr<Voice>> m_activeVoices;
std::list<std::shared_ptr<Emitter>> m_activeEmitters;
std::list<std::shared_ptr<Sequencer>> m_activeSequencers;
std::list<Submix> m_activeSubmixes;
std::unordered_map<uint16_t, std::pair<AudioGroup*, ObjectId>> m_sfxLookup;
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
int m_nextVid = 0;
Voice* _allocateVoice(const AudioGroup& group, double sampleRate,
std::shared_ptr<Voice> _allocateVoice(const AudioGroup& group, double sampleRate,
bool dynamicPitch, bool emitter, Submix* smx);
Submix* _allocateSubmix(Submix* smx);
std::list<Voice>::iterator _destroyVoice(Voice* voice);
std::list<std::shared_ptr<Voice>>::iterator _destroyVoice(Voice* voice);
std::list<Submix>::iterator _destroySubmix(Submix* smx);
void _bringOutYourDead();
public:
Engine(IBackendVoiceAllocator& backend);
@ -60,21 +61,21 @@ public:
void removeSubmix(Submix* smx);
/** Start soundFX playing from loaded audio groups */
Voice* fxStart(int sfxId, float vol, float pan, Submix* smx=nullptr);
std::shared_ptr<Voice> fxStart(int sfxId, float vol, float pan, Submix* smx=nullptr);
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
Emitter* addEmitter(const Vector3f& pos, const Vector3f& dir, float maxDist,
std::shared_ptr<Emitter> addEmitter(const Vector3f& pos, const Vector3f& dir, float maxDist,
float falloff, int sfxId, float minVol, float maxVol,
Submix* smx=nullptr);
/** Start song playing from loaded audio groups */
Sequencer* seqPlay(int groupId, int songId, const unsigned char* arrData);
std::shared_ptr<Sequencer> seqPlay(int groupId, int songId, const unsigned char* arrData);
/** Find voice from VoiceId */
Voice* findVoice(int vid);
std::shared_ptr<Voice> findVoice(int vid);
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `flag` set */
void killKeygroup(uint8_t kg, uint8_t flag);
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */
void killKeygroup(uint8_t kg, bool now);
/** Send all voices using `macroId` the message `val` */
void sendMacroMessage(ObjectId macroId, int32_t val);

View File

@ -26,7 +26,8 @@ using ObjectId = uint16_t;
/** Common 'engine child' class */
class Entity
{
/* Only the Engine will manage Entity lifetimes */
/* Only the Engine will manage Entity lifetimes,
* but shared_ptrs are issued to the client so it can safely track state */
friend class Engine;
friend class SoundMacroState;
bool m_destroyed = false;

View File

@ -8,6 +8,13 @@ namespace amuse
class IBackendVoice;
class Voice;
enum class SubmixFormat
{
Int16,
Int32,
Float
};
/**
* @brief Client-implemented submix instance
*/
@ -21,6 +28,12 @@ public:
/** Amuse obtains a new voice from the platform outputting to this submix */
virtual std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch)=0;
/** Amuse gets fixed sample rate of submix this way */
virtual double getSampleRate() const=0;
/** Amuse gets fixed sample format of submix this way */
virtual SubmixFormat getSampleFormat() const=0;
};
}

View File

@ -38,6 +38,9 @@ public:
/** Amuse obtains speaker-configuration from the platform this way */
virtual AudioChannelSet getAvailableSet()=0;
/** Amuse flushes voice samples to the backend this way */
virtual void pumpAndMixVoices()=0;
};
}

View File

@ -117,7 +117,7 @@ class SoundMacroState
/** 'program counter' stack for the active SoundMacro */
std::vector<std::pair<const unsigned char*, int>> m_pc;
float m_ticksPerSec; /**< ratio for resolving ticks in commands that use them */
double m_ticksPerSec; /**< ratio for resolving ticks in commands that use them */
uint8_t m_initVel; /**< Velocity played for this macro invocation */
uint8_t m_initMod; /**< Modulation played for this macro invocation */
uint8_t m_initKey; /**< Key played for this macro invocation */
@ -125,14 +125,14 @@ class SoundMacroState
uint8_t m_curMod; /**< Current modulation played for this macro invocation */
uint32_t m_curKey; /**< Current key played for this macro invocation (in cents) */
float m_execTime; /**< time in seconds of SoundMacro execution (per-update resolution) */
double m_execTime; /**< time in seconds of SoundMacro execution (per-update resolution) */
bool m_keyoff; /**< keyoff message has been received */
bool m_sampleEnd; /**< sample has finished playback */
bool m_inWait = false; /**< set when timer/keyoff/sampleend wait active */
bool m_keyoffWait = false; /**< set when active wait is a keyoff wait */
bool m_sampleEndWait = false; /**< set when active wait is a sampleend wait */
float m_waitCountdown; /**< countdown timer for active wait */
double m_waitCountdown; /**< countdown timer for active wait */
int m_loopCountdown = -1; /**< countdown for current loop */
int m_lastPlayMacroVid = -1; /**< VoiceId from last PlayMacro command */
@ -180,7 +180,7 @@ class SoundMacroState
Combine combine, VarType varType);
/** Calculate value */
float evaluate(Voice& vox, const SoundMacroState& st);
float evaluate(const Voice& vox, const SoundMacroState& st) const;
/** Determine if able to use */
operator bool() const {return m_comps.size() != 0;}
@ -220,13 +220,13 @@ class SoundMacroState
public:
/** initialize state for SoundMacro data at `ptr` */
void initialize(const unsigned char* ptr, int step);
void initialize(const unsigned char* ptr, int step, float ticksPerSec,
void initialize(const unsigned char* ptr, int step, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod);
/** advances `dt` seconds worth of commands in the SoundMacro
* @return `true` if END reached
*/
bool advance(Voice& vox, float dt);
bool advance(Voice& vox, double dt);
/** keyoff event */
void keyoffNotify(Voice& vox);

View File

@ -5,6 +5,10 @@
#include "SoundMacroState.hpp"
#include "IBackendSubmix.hpp"
#include "IBackendVoice.hpp"
#include "EffectBase.hpp"
#include "EffectChorus.hpp"
#include "EffectDelay.hpp"
#include "EffectReverb.hpp"
#include <unordered_set>
namespace amuse
@ -22,8 +26,10 @@ class Submix
std::unique_ptr<IBackendSubmix> m_backendSubmix; /**< Handle to client-implemented backend submix */
std::unordered_set<Voice*> m_activeVoices; /**< Secondary index of Voices within Submix */
std::unordered_set<Submix*> m_activeSubmixes; /**< Secondary index of Submixes within Submix */
std::vector<std::unique_ptr<EffectBaseTypeless>> m_effectStack; /**< Ordered list of effects to apply to submix */
bool m_destroyed = false;
void _destroy();
public:
Submix(Engine& engine, Submix* smx);
~Submix()
@ -37,13 +43,61 @@ public:
/** Obtain pointer to Submix's parent Submix */
Submix* getParentSubmix() {return m_submix;}
/** Add new effect to effect stack and assume ownership */
template <class T, class ...Args>
T& makeEffect(Args... args)
{
switch (m_backendSubmix->getSampleFormat())
{
case SubmixFormat::Int16:
{
using ImpType = typename T::template ImpType<int16_t>;
m_effectStack.emplace_back(new ImpType(args..., m_backendSubmix->getSampleRate()));
return static_cast<ImpType&>(*m_effectStack.back());
}
case SubmixFormat::Int32:
{
using ImpType = typename T::template ImpType<int32_t>;
m_effectStack.emplace_back(new ImpType(args..., m_backendSubmix->getSampleRate()));
return static_cast<ImpType&>(*m_effectStack.back());
}
case SubmixFormat::Float:
{
using ImpType = typename T::template ImpType<float>;
m_effectStack.emplace_back(new ImpType(args..., m_backendSubmix->getSampleRate()));
return static_cast<ImpType&>(*m_effectStack.back());
}
}
}
/** Add new chorus effect to effect stack and assume ownership */
EffectChorus& makeChorus(uint32_t baseDelay, uint32_t variation, uint32_t period);
/** Add new delay effect to effect stack and assume ownership */
EffectDelay& makeDelay(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput);
/** Add new standard-quality reverb effect to effect stack and assume ownership */
EffectReverb& makeReverbStd(float coloration, float mix, float time,
float damping, float preDelay);
/** Add new high-quality reverb effect to effect stack and assume ownership */
EffectReverbHi& makeReverbHi(float coloration, float mix, float time,
float damping, float preDelay, float crosstalk);
/** Remove and deallocate all effects from effect stack */
void clearEffects() {m_effectStack.clear();}
/** Returns true when an effect callback is bound */
bool canApplyEffect() const;
bool canApplyEffect() const {return m_effectStack.size() != 0;}
/** in/out transformation entry for audio effect */
void applyEffect(int16_t* audio, const ChannelMap& chanMap, double sampleRate) const;
void applyEffect(int32_t* audio, const ChannelMap& chanMap, double sampleRate) const;
void applyEffect(float* audio, const ChannelMap& chanMap, double sampleRate) const;
void applyEffect(int16_t* audio, size_t frameCount, const ChannelMap& chanMap) const;
/** in/out transformation entry for audio effect */
void applyEffect(int32_t* audio, size_t frameCount, const ChannelMap& chanMap) const;
/** in/out transformation entry for audio effect */
void applyEffect(float* audio, size_t frameCount, const ChannelMap& chanMap) const;
Engine& getEngine() {return m_root;}
};

View File

@ -32,15 +32,22 @@ class Voice : public Entity
int m_vid; /**< VoiceID of this voice instance */
bool m_emitter; /**< Voice is part of an Emitter */
Submix* m_submix = nullptr; /**< Submix this voice outputs to (or NULL for the main output mix) */
std::list<Voice>::iterator m_engineIt; /**< Iterator to self within Engine's list for quick deletion */
std::list<std::shared_ptr<Voice>>::iterator m_engineIt; /**< Iterator to self within Engine's list for quick deletion */
std::unique_ptr<IBackendVoice> m_backendVoice; /**< Handle to client-implemented backend voice */
SoundMacroState m_state; /**< State container for SoundMacro playback */
std::list<Voice> m_childVoices; /**< Child voices for PLAYMACRO usage */
std::list<std::shared_ptr<Voice>> m_childVoices; /**< Child voices for PLAYMACRO usage */
uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */
enum class SampleFormat : uint8_t
{
DSP,
IMA,
PCM
};
const Sample* m_curSample = nullptr; /**< Current sample entry playing */
const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */
SampleFormat m_curFormat; /**< Current sample format playing */
uint32_t m_curSamplePos = 0; /**< Current sample position */
uint32_t m_lastSamplePos = 0; /**< Last sample position (or last loop sample) */
int16_t m_prev1 = 0; /**< DSPADPCM prev sample */
@ -97,15 +104,18 @@ class Voice : public Entity
float m_lfoPeriods[2]; /**< time-periods for LFO1 and LFO2 */
bool m_dead = false; /**< sound macro has reached END, voice should be deallocated at end of update cycle */
void _destroy();
void _reset();
bool _checkSamplePos();
void _doKeyOff();
bool _advanceSample(int16_t& samp);
void _setTotalPitch(int32_t cents);
void _bringOutYourDead();
Voice* _allocateVoice(double sampleRate, bool dynamicPitch);
std::list<Voice>::iterator _destroyVoice(Voice* voice);
std::shared_ptr<Voice> _allocateVoice(double sampleRate, bool dynamicPitch);
std::list<std::shared_ptr<Voice>>::iterator _destroyVoice(Voice* voice);
public:
Voice(Engine& engine, const AudioGroup& group, int vid, bool emitter, Submix* smx);
@ -128,10 +138,10 @@ public:
int maxVid() const;
/** Allocate parallel macro and tie to voice for possible emitter influence */
Voice* startChildMacro(int8_t addNote, ObjectId macroId, int macroStep);
std::shared_ptr<Voice> startChildMacro(int8_t addNote, ObjectId macroId, int macroStep);
/** Load specified SoundMacro ID of within group into voice */
bool loadSoundMacro(ObjectId macroId, int macroStep, float ticksPerSec,
bool loadSoundMacro(ObjectId macroId, int macroStep, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod,
bool pushPc=false);
@ -220,11 +230,11 @@ public:
void setKeygroup(uint8_t kg) {m_keygroup = kg;}
uint8_t getLastNote() const {return m_state.m_initKey;}
int8_t getCtrlValue(uint8_t ctrl) const {}
int8_t getCtrlValue(uint8_t ctrl) const {return 0;}
void setCtrlValue(uint8_t ctrl, int8_t val) {}
int8_t getPitchWheel() const {}
int8_t getModWheel() const {}
int8_t getAftertouch() const {}
int8_t getPitchWheel() const {return 0;}
int8_t getModWheel() const {return 0;}
int8_t getAftertouch() const {return 0;}
};

View File

@ -6,20 +6,12 @@ namespace amuse
AudioGroup::AudioGroup(int groupId, const AudioGroupData& data)
: m_groupId(groupId),
m_pool(data.getPool()),
m_proj(data.getProj()),
m_pool(data.getPool()),
m_sdir(data.getSdir()),
m_samp(data.getSamp())
{}
bool AudioGroup::sfxInGroup(int sfxId) const
{
}
bool AudioGroup::songInGroup(int songId) const
{
}
const Sample* AudioGroup::getSample(int sfxId) const
{
for (const auto& ent : m_sdir.m_entries)

View File

@ -91,9 +91,11 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data)
bIdx = &idx;
/* SFX entries */
uint16_t count = SBig(*reinterpret_cast<const uint16_t*>(data + header.pageTableOff));
idx.m_sfxEntries.reserve(count);
const SFXGroupIndex::SFXEntry* entries =
reinterpret_cast<const SFXGroupIndex::SFXEntry*>(data + header.pageTableOff);
while (entries->objId != 0xffff)
reinterpret_cast<const SFXGroupIndex::SFXEntry*>(data + header.pageTableOff + 4);
for (int i=0 ; i<count ; ++i)
{
idx.m_sfxEntries[SBig(entries->defineId)] = entries;
++entries;

View File

@ -6,7 +6,6 @@ namespace amuse
void AudioGroupSampleDirectory::Entry::swapBig()
{
m_sfxId = SBig(m_sfxId);
m_sampleOff = SBig(m_sampleOff);
m_unk = SBig(m_unk);
m_sampleRate = SBig(m_sampleRate);
@ -30,6 +29,27 @@ void AudioGroupSampleDirectory::ADPCMParms::swapBig()
AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data)
{
const unsigned char* cur = data;
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff)
{
const AudioGroupSampleDirectory::Entry* ent =
reinterpret_cast<const AudioGroupSampleDirectory::Entry*>(cur);
std::pair<Entry, ADPCMParms>& store = m_entries[ent->m_sfxId];
store.first = *ent;
store.first.swapBig();
if (store.first.m_adpcmParmOffset)
{
const AudioGroupSampleDirectory::ADPCMParms* adpcm =
reinterpret_cast<const AudioGroupSampleDirectory::ADPCMParms*>(data +
store.first.m_adpcmParmOffset);
store.second = *adpcm;
store.second.swapBig();
}
cur += 32;
}
}
}

View File

@ -54,21 +54,21 @@ bool BooBackendSubmix::SubmixCallback::canApplyEffect() const
}
void BooBackendSubmix::SubmixCallback::applyEffect(int16_t* audio, size_t frameCount,
const boo::ChannelMap& chanMap, double sampleRate) const
const boo::ChannelMap& chanMap, double) const
{
return m_parent.m_clientSmx.applyEffect(audio, reinterpret_cast<const ChannelMap&>(chanMap), sampleRate);
return m_parent.m_clientSmx.applyEffect(audio, frameCount, reinterpret_cast<const ChannelMap&>(chanMap));
}
void BooBackendSubmix::SubmixCallback::applyEffect(int32_t* audio, size_t frameCount,
const boo::ChannelMap& chanMap, double sampleRate) const
const boo::ChannelMap& chanMap, double) const
{
return m_parent.m_clientSmx.applyEffect(audio, reinterpret_cast<const ChannelMap&>(chanMap), sampleRate);
return m_parent.m_clientSmx.applyEffect(audio, frameCount, reinterpret_cast<const ChannelMap&>(chanMap));
}
void BooBackendSubmix::SubmixCallback::applyEffect(float* audio, size_t frameCount,
const boo::ChannelMap& chanMap, double sampleRate) const
const boo::ChannelMap& chanMap, double) const
{
return m_parent.m_clientSmx.applyEffect(audio, reinterpret_cast<const ChannelMap&>(chanMap), sampleRate);
return m_parent.m_clientSmx.applyEffect(audio, frameCount, reinterpret_cast<const ChannelMap&>(chanMap));
}
BooBackendSubmix::BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx)
@ -90,6 +90,16 @@ BooBackendSubmix::allocateVoice(Voice& clientVox, double sampleRate, bool dynami
return std::make_unique<BooBackendVoice>(*m_booSubmix, clientVox, sampleRate, dynamicPitch);
}
double BooBackendSubmix::getSampleRate() const
{
return m_booSubmix->getSampleRate();
}
SubmixFormat BooBackendSubmix::getSampleFormat() const
{
return SubmixFormat(m_booSubmix->getSampleFormat());
}
BooBackendVoiceAllocator::BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine)
: m_booEngine(booEngine)
{}
@ -110,4 +120,9 @@ AudioChannelSet BooBackendVoiceAllocator::getAvailableSet()
return AudioChannelSet(m_booEngine.getAvailableSet());
}
void BooBackendVoiceAllocator::pumpAndMixVoices()
{
m_booEngine.pumpAndMixVoices();
}
}

View File

@ -139,12 +139,16 @@ static const float rsmpTab12khz[] =
-0.000977, 0.101593, 0.802216, 0.097504,
};
template <typename T>
EffectChorus<T>::EffectChorus(uint32_t baseDelay, uint32_t variation,
uint32_t period, double sampleRate)
EffectChorus::EffectChorus(uint32_t baseDelay, uint32_t variation, uint32_t period)
: x90_baseDelay(clamp(5u, baseDelay, 15u)),
x94_variation(clamp(0u, variation, 5u)),
x98_period(clamp(500u, period, 10000u)),
x98_period(clamp(500u, period, 10000u))
{}
template <typename T>
EffectChorusImp<T>::EffectChorusImp(uint32_t baseDelay, uint32_t variation,
uint32_t period, double sampleRate)
: EffectChorus(baseDelay, variation, period),
m_sampsPerMs(std::ceil(sampleRate / 1000.0)),
m_blockSamples(m_sampsPerMs * 5)
{
@ -160,7 +164,7 @@ EffectChorus<T>::EffectChorus(uint32_t baseDelay, uint32_t variation,
}
template <typename T>
void EffectChorus<T>::_update()
void EffectChorusImp<T>::_update()
{
size_t chanPitch = m_blockSamples * AMUSE_CHORUS_NUM_BLOCKS;
size_t fifteenSamps = 15 * m_sampsPerMs;
@ -178,13 +182,13 @@ void EffectChorus<T>::_update()
}
template <typename T>
EffectChorus<T>::~EffectChorus()
EffectChorusImp<T>::~EffectChorusImp()
{
delete[] x0_lastChans[0][0];
}
template <typename T>
void EffectChorus<T>::SrcInfo::doSrc1(size_t blockSamples, size_t chanCount)
void EffectChorusImp<T>::SrcInfo::doSrc1(size_t blockSamples, size_t chanCount)
{
float old1 = x74_old[0];
float old2 = x74_old[1];
@ -225,7 +229,7 @@ void EffectChorus<T>::SrcInfo::doSrc1(size_t blockSamples, size_t chanCount)
}
template <typename T>
void EffectChorus<T>::SrcInfo::doSrc2(size_t blockSamples, size_t chanCount)
void EffectChorusImp<T>::SrcInfo::doSrc2(size_t blockSamples, size_t chanCount)
{
float old1 = x74_old[0];
float old2 = x74_old[1];
@ -284,7 +288,7 @@ void EffectChorus<T>::SrcInfo::doSrc2(size_t blockSamples, size_t chanCount)
}
template <typename T>
void EffectChorus<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)
void EffectChorusImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)
{
if (m_dirty)
_update();
@ -356,8 +360,8 @@ void EffectChorus<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap&
}
}
template class EffectChorus<int16_t>;
template class EffectChorus<int32_t>;
template class EffectChorus<float>;
template class EffectChorusImp<int16_t>;
template class EffectChorusImp<int32_t>;
template class EffectChorusImp<float>;
}

View File

@ -8,7 +8,7 @@ namespace amuse
{
template <typename T>
EffectDelay<T>::EffectDelay(uint32_t initDelay, uint32_t initFeedback,
EffectDelayImp<T>::EffectDelayImp(uint32_t initDelay, uint32_t initFeedback,
uint32_t initOutput, double sampleRate)
: m_sampsPerMs(std::ceil(sampleRate / 1000.0)),
m_blockSamples(m_sampsPerMs * 5)
@ -28,7 +28,7 @@ EffectDelay<T>::EffectDelay(uint32_t initDelay, uint32_t initFeedback,
}
template <typename T>
void EffectDelay<T>::_update()
void EffectDelayImp<T>::_update()
{
for (int i=0 ; i<8 ; ++i)
{
@ -38,14 +38,14 @@ void EffectDelay<T>::_update()
x24_currentOutput[i] = x54_output[i] * 128 / 100;
x30_chanLines[i].reset(new T[m_blockSamples * x0_currentSize[i]]);
memset(x30_chanLines[i], 0, m_blockSamples * x0_currentSize[i] * sizeof(T));
memset(x30_chanLines[i].get(), 0, m_blockSamples * x0_currentSize[i] * sizeof(T));
}
m_dirty = false;
}
template <typename T>
void EffectDelay<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)
void EffectDelayImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)
{
if (m_dirty)
_update();
@ -62,10 +62,14 @@ void EffectDelay<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap&
samp = ClampFull<T>(samp * x18_currentFeedback[c] / 128 + liveSamp);
liveSamp = samp * x24_currentOutput[c] / 128;
}
xc_currentPos = (xc_currentPos[c] + 1) % x0_currentSize[c];
xc_currentPos[c] = (xc_currentPos[c] + 1) % x0_currentSize[c];
}
audio += chanMap.m_channelCount * m_blockSamples;
}
}
template class EffectDelayImp<int16_t>;
template class EffectDelayImp<int32_t>;
template class EffectDelayImp<float>;
}

View File

@ -53,101 +53,112 @@ void ReverbDelayLine::setdelay(int32_t delay)
x4_outPoint += x8_length;
}
template <typename T, size_t AP, size_t C>
EffectReverb<T, AP, C>::EffectReverb(float coloration, float mix, float time,
float damping, float preDelay, double sampleRate)
EffectReverb::EffectReverb(float coloration, float mix, float time,
float damping, float preDelay)
: x140_x1c8_coloration(clamp(0.f, coloration, 1.f)),
x144_x1cc_mix(clamp(0.f, mix, 1.f)),
x148_x1d0_time(clamp(0.01f, time, 10.f)),
x14c_x1d4_damping(clamp(0.f, damping, 1.f)),
x150_x1d8_preDelay(clamp(0.f, preDelay, 0.1f)),
x150_x1d8_preDelay(clamp(0.f, preDelay, 0.1f))
{}
EffectReverbHi::EffectReverbHi(float coloration, float mix, float time,
float damping, float preDelay, float crosstalk)
: EffectReverb(coloration, mix, time, damping, preDelay),
x1dc_crosstalk(clamp(0.f, crosstalk, 1.0f))
{}
template <typename T>
EffectReverbStdImp<T>::EffectReverbStdImp(float coloration, float mix, float time,
float damping, float preDelay, double sampleRate)
: EffectReverb(coloration, mix, time, damping, preDelay),
m_sampleRate(sampleRate)
{}
template <typename T, size_t AP, size_t C>
void EffectReverb<T, AP, C>::_update()
template <typename T>
void EffectReverbStdImp<T>::_update()
{
float timeSamples = x148_x1d0_time * m_sampleRate;
for (int c=0 ; c<8 ; ++c)
{
for (int t=0 ; t<C ; ++t)
for (int t=0 ; t<2 ; ++t)
{
ReverbDelayLine& combLine = x78_xb4_C[c][t];
ReverbDelayLine& combLine = x78_C[c][t];
size_t tapDelay = CTapDelays[t] * m_sampleRate / 32000.0;
combLine.allocate(tapDelay);
combLine.setdelay(tapDelay);
xf4_x16c_combCoef[c][t] = std::pow(10.f, tapDelay * -3 / timeSamples);
xf4_combCoef[c][t] = std::pow(10.f, tapDelay * -3 / timeSamples);
}
for (int t=0 ; t<AP ; ++t)
for (int t=0 ; t<2 ; ++t)
{
ReverbDelayLine& allPassLine = x0_x0_AP[c][t];
ReverbDelayLine& allPassLine = x0_AP[c][t];
size_t tapDelay = APTapDelays[t] * m_sampleRate / 32000.0;
allPassLine.allocate(tapDelay);
allPassLine.setdelay(tapDelay);
}
}
xf0_x168_allPassCoef = x140_x1c8_coloration;
x118_x19c_level = x144_x1cc_mix;
x11c_x1a0_damping = x14c_x1d4_damping;
xf0_allPassCoef = x140_x1c8_coloration;
x118_level = x144_x1cc_mix;
x11c_damping = x14c_x1d4_damping;
if (x11c_x1a0_damping < 0.05f)
x11c_x1a0_damping = 0.05f;
if (x11c_damping < 0.05f)
x11c_damping = 0.05f;
x11c_x1a0_damping = 1.f - (x11c_x1a0_damping * 0.8f + 0.05);
x11c_damping = 1.f - (x11c_damping * 0.8f + 0.05);
if (x150_x1d8_preDelay != 0.f)
{
x120_x1a4_preDelayTime = m_sampleRate * x150_x1d8_preDelay;
x120_preDelayTime = m_sampleRate * x150_x1d8_preDelay;
for (int i=0 ; i<8 ; ++i)
{
x124_x1ac_preDelayLine[i].reset(new float[x120_x1a4_preDelayTime]);
memset(x124_x1ac_preDelayLine[i].get(), 0, x120_x1a4_preDelayTime * sizeof(float));
x130_x1b8_preDelayPtr[i] = x124_x1ac_preDelayLine[i].get();
x124_preDelayLine[i].reset(new float[x120_preDelayTime]);
memset(x124_preDelayLine[i].get(), 0, x120_preDelayTime * sizeof(float));
x130_preDelayPtr[i] = x124_preDelayLine[i].get();
}
}
else
{
x120_x1a4_preDelayTime = 0;
x120_preDelayTime = 0;
for (int i=0 ; i<8 ; ++i)
{
x124_x1ac_preDelayLine[i] = nullptr;
x130_x1b8_preDelayPtr[i] = nullptr;
x124_preDelayLine[i] = nullptr;
x130_preDelayPtr[i] = nullptr;
}
}
m_dirty = false;
}
template <typename T, size_t AP, size_t C>
void EffectReverb<T, AP, C>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)
template <typename T>
void EffectReverbStdImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)
{
if (m_dirty)
_update();
float dampWet = x118_x19c_level * 0.6f;
float dampWet = x118_level * 0.6f;
float dampDry = 0.6f - dampWet;
for (size_t f=0 ; f<frameCount ;)
{
for (int c=0 ; c<chanMap.m_channelCount ; ++c)
{
float* combCoefs = xf4_x16c_combCoef[c];
float& lpLastOut = x10c_x190_lpLastout[c];
float* preDelayLine = x124_x1ac_preDelayLine[c].get();
float* preDelayPtr = x130_x1b8_preDelayPtr[c];
float* lastPreDelaySamp = &preDelayLine[x120_x1a4_preDelayTime - 1];
float* combCoefs = xf4_combCoef[c];
float& lpLastOut = x10c_lpLastout[c];
float* preDelayLine = x124_preDelayLine[c].get();
float* preDelayPtr = x130_preDelayPtr[c];
float* lastPreDelaySamp = &preDelayLine[x120_preDelayTime - 1];
ReverbDelayLine* linesC = x78_xb4_C[c];
ReverbDelayLine* linesAP = x0_x0_AP[c];
ReverbDelayLine* linesC = x78_C[c];
ReverbDelayLine* linesAP = x0_AP[c];
float sample = audio[c];
for (int s=1 ; s<160 && f<frameCount ; ++s, ++f)
{
/* Pre-delay stage */
float sample2 = sample;
if (x120_x1a4_preDelayTime != 0)
if (x120_preDelayTime != 0)
{
sample2 = *preDelayPtr;
*preDelayPtr = sample;
@ -183,8 +194,8 @@ void EffectReverb<T, AP, C>::applyEffect(T* audio, size_t frameCount, const Chan
/* All-pass filter stage */
linesAP[0].xc_inputs[linesAP[0].x0_inPoint] =
xf0_x168_allPassCoef * linesAP[0].x10_lastInput + linesC[0].x10_lastInput + linesC[1].x10_lastInput;
float lowPass = -(xf0_x168_allPassCoef * linesAP[0].xc_inputs[linesAP[0].x0_inPoint] -
xf0_allPassCoef * linesAP[0].x10_lastInput + linesC[0].x10_lastInput + linesC[1].x10_lastInput;
float lowPass = -(xf0_allPassCoef * linesAP[0].xc_inputs[linesAP[0].x0_inPoint] -
linesAP[0].x10_lastInput);
linesAP[0].x0_inPoint += 4;
@ -197,9 +208,9 @@ void EffectReverb<T, AP, C>::applyEffect(T* audio, size_t frameCount, const Chan
if (linesAP[0].x4_outPoint == linesAP[0].x8_length)
linesAP[0].x4_outPoint = 0;
lpLastOut = x11c_x1a0_damping * lpLastOut + lowPass * 0.3f;
linesAP[1].xc_inputs[linesAP[1].x0_inPoint] = xf0_x168_allPassCoef * linesAP[1].x10_lastInput + lpLastOut;
float allPass = -(xf0_x168_allPassCoef * linesAP[1].xc_inputs[linesAP[1].x0_inPoint] -
lpLastOut = x11c_damping * lpLastOut + lowPass * 0.3f;
linesAP[1].xc_inputs[linesAP[1].x0_inPoint] = xf0_allPassCoef * linesAP[1].x10_lastInput + lpLastOut;
float allPass = -(xf0_allPassCoef * linesAP[1].xc_inputs[linesAP[1].x0_inPoint] -
linesAP[1].x10_lastInput);
linesAP[1].x0_inPoint += 4;
@ -216,53 +227,102 @@ void EffectReverb<T, AP, C>::applyEffect(T* audio, size_t frameCount, const Chan
audio[(s-1) * chanMap.m_channelCount + c] = ClampFull<T>(dampWet * allPass + dampDry * sample);
sample = audio[s * chanMap.m_channelCount + c];
}
x130_x1b8_preDelayPtr[c] = preDelayPtr;
x130_preDelayPtr[c] = preDelayPtr;
}
audio += chanMap.m_channelCount * 160;
}
}
template <typename T>
EffectReverbStd<T>::EffectReverbStd(float coloration, float mix, float time,
float damping, float preDelay, double sampleRate)
: EffectReverb<T, 2, 2>(coloration, mix, time, damping, preDelay, sampleRate)
EffectReverbHiImp<T>::EffectReverbHiImp(float coloration, float mix, float time,
float damping, float preDelay, float crosstalk,
double sampleRate)
: EffectReverbHi(coloration, mix, time, damping, preDelay, crosstalk),
m_sampleRate(sampleRate)
{
EffectReverb<T, 2, 2>::_update();
_update();
}
template <typename T>
void EffectReverbHi<T>::_update()
void EffectReverbHiImp<T>::_update()
{
EffectReverb<T, 2, 3>::_update();
float timeSamples = x148_x1d0_time * m_sampleRate;
for (int c=0 ; c<8 ; ++c)
{
for (int t=0 ; t<2 ; ++t)
{
ReverbDelayLine& combLine = xb4_C[c][t];
size_t tapDelay = CTapDelays[t] * m_sampleRate / 32000.0;
combLine.allocate(tapDelay);
combLine.setdelay(tapDelay);
x16c_combCoef[c][t] = std::pow(10.f, tapDelay * -3 / timeSamples);
}
for (int t=0 ; t<2 ; ++t)
{
ReverbDelayLine& allPassLine = x0_AP[c][t];
size_t tapDelay = APTapDelays[t] * m_sampleRate / 32000.0;
allPassLine.allocate(tapDelay);
allPassLine.setdelay(tapDelay);
}
ReverbDelayLine& lpLine = x78_LP[c];
size_t tapDelay = LPTapDelays[c] * EffectReverb<T, 2, 3>::m_sampleRate / 32000.0;
size_t tapDelay = LPTapDelays[c] * m_sampleRate / 32000.0;
lpLine.allocate(tapDelay);
lpLine.setdelay(tapDelay);
}
x168_allPassCoef = x140_x1c8_coloration;
x19c_level = x144_x1cc_mix;
x1a0_damping = x14c_x1d4_damping;
if (x1a0_damping < 0.05f)
x1a0_damping = 0.05f;
x1a0_damping = 1.f - (x1a0_damping * 0.8f + 0.05);
if (x150_x1d8_preDelay != 0.f)
{
x1a4_preDelayTime = m_sampleRate * x150_x1d8_preDelay;
for (int i=0 ; i<8 ; ++i)
{
x1ac_preDelayLine[i].reset(new float[x1a4_preDelayTime]);
memset(x1ac_preDelayLine[i].get(), 0, x1a4_preDelayTime * sizeof(float));
x1b8_preDelayPtr[i] = x1ac_preDelayLine[i].get();
}
}
else
{
x1a4_preDelayTime = 0;
for (int i=0 ; i<8 ; ++i)
{
x1ac_preDelayLine[i] = nullptr;
x1b8_preDelayPtr[i] = nullptr;
}
}
m_dirty = false;
}
template <typename T>
void EffectReverbHi<T>::_handleReverb(T* audio, int c, int chanCount, int sampleCount)
void EffectReverbHiImp<T>::_handleReverb(T* audio, int c, int chanCount, int sampleCount)
{
float dampWet = EffectReverb<T, 2, 3>::x118_x19c_level * 0.6f;
float dampWet = x19c_level * 0.6f;
float dampDry = 0.6f - dampWet;
float* combCoefs = EffectReverb<T, 2, 3>::xf4_x16c_combCoef[c];
float& lpLastOut = EffectReverb<T, 2, 3>::x10c_x190_lpLastout[c];
float* preDelayLine = EffectReverb<T, 2, 3>::x124_x1ac_preDelayLine[c].get();
float* preDelayPtr = EffectReverb<T, 2, 3>::x130_x1b8_preDelayPtr[c];
float* lastPreDelaySamp = &preDelayLine[EffectReverb<T, 2, 3>::x120_x1a4_preDelayTime - 1];
float* combCoefs = x16c_combCoef[c];
float& lpLastOut = x190_lpLastout[c];
float* preDelayLine = x1ac_preDelayLine[c].get();
float* preDelayPtr = x1b8_preDelayPtr[c];
float* lastPreDelaySamp = &preDelayLine[x1a4_preDelayTime - 1];
ReverbDelayLine* linesC = EffectReverb<T, 2, 3>::x78_xb4_C[c];
ReverbDelayLine* linesAP = EffectReverb<T, 2, 3>::x0_x0_AP[c];
ReverbDelayLine* linesC = xb4_C[c];
ReverbDelayLine* linesAP = x0_AP[c];
ReverbDelayLine& lineLP = x78_LP[c];
float allPassCoef = EffectReverb<T, 2, 3>::xf0_x168_allPassCoef;
float damping = EffectReverb<T, 2, 3>::x11c_x1a0_damping;
int32_t preDelayTime = EffectReverb<T, 2, 3>::x120_x1a4_preDelayTime;
float allPassCoef = x168_allPassCoef;
float damping = x1a0_damping;
int32_t preDelayTime = x1a4_preDelayTime;
float sample = audio[c];
for (int s=1 ; s<sampleCount ; ++s)
@ -351,11 +411,12 @@ void EffectReverbHi<T>::_handleReverb(T* audio, int c, int chanCount, int sample
audio[(s-1) * chanCount + c] = ClampFull<T>(dampWet * allPass + dampDry * sample);
sample = audio[s * chanCount + c];
}
EffectReverb<T, 2, 3>::x130_x1b8_preDelayPtr[c] = preDelayPtr;
x1b8_preDelayPtr[c] = preDelayPtr;
}
template <typename T>
void EffectReverbHi<T>::_doCrosstalk(T* audio, float wet, float dry, int chanCount, int sampleCount)
void EffectReverbHiImp<T>::_doCrosstalk(T* audio, float wet, float dry, int chanCount, int sampleCount)
{
for (int i=0 ; i<sampleCount ; ++i)
{
@ -372,9 +433,9 @@ void EffectReverbHi<T>::_doCrosstalk(T* audio, float wet, float dry, int chanCou
}
template <typename T>
void EffectReverbHi<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)
void EffectReverbHiImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)
{
if (EffectReverb<T, 2, 3>::m_dirty)
if (m_dirty)
_update();
for (size_t f=0 ; f<frameCount ; f+=160)
@ -393,22 +454,12 @@ void EffectReverbHi<T>::applyEffect(T* audio, size_t frameCount, const ChannelMa
}
}
template <typename T>
EffectReverbHi<T>::EffectReverbHi(float coloration, float mix, float time,
float damping, float preDelay, float crosstalk,
double sampleRate)
: EffectReverb<T, 2, 3>(coloration, mix, time, damping, preDelay, sampleRate),
x1dc_crosstalk(clamp(0.f, crosstalk, 1.f))
{
_update();
}
template class EffectReverbStdImp<int16_t>;
template class EffectReverbStdImp<int32_t>;
template class EffectReverbStdImp<float>;
template class EffectReverbStd<int16_t>;
template class EffectReverbStd<int32_t>;
template class EffectReverbStd<float>;
template class EffectReverbHi<int16_t>;
template class EffectReverbHi<int32_t>;
template class EffectReverbHi<float>;
template class EffectReverbHiImp<int16_t>;
template class EffectReverbHiImp<int32_t>;
template class EffectReverbHiImp<float>;
}

View File

@ -15,14 +15,14 @@ Engine::Engine(IBackendVoiceAllocator& backend)
: m_backend(backend)
{}
Voice* Engine::_allocateVoice(const AudioGroup& group, double sampleRate,
std::shared_ptr<Voice> Engine::_allocateVoice(const AudioGroup& group, double sampleRate,
bool dynamicPitch, bool emitter, Submix* smx)
{
auto it = m_activeVoices.emplace(m_activeVoices.end(), *this, group, m_nextVid++, emitter, smx);
m_activeVoices.back().m_backendVoice =
m_backend.allocateVoice(m_activeVoices.back(), sampleRate, dynamicPitch);
m_activeVoices.back().m_engineIt = it;
return &m_activeVoices.back();
auto it = m_activeVoices.emplace(m_activeVoices.end(), new Voice(*this, group, m_nextVid++, emitter, smx));
m_activeVoices.back()->m_backendVoice =
m_backend.allocateVoice(*m_activeVoices.back(), sampleRate, dynamicPitch);
m_activeVoices.back()->m_engineIt = it;
return m_activeVoices.back();
}
Submix* Engine::_allocateSubmix(Submix* smx)
@ -33,7 +33,7 @@ Submix* Engine::_allocateSubmix(Submix* smx)
return &m_activeSubmixes.back();
}
std::list<Voice>::iterator Engine::_destroyVoice(Voice* voice)
std::list<std::shared_ptr<Voice>>::iterator Engine::_destroyVoice(Voice* voice)
{
#ifndef NDEBUG
assert(this == &voice->getEngine());
@ -51,12 +51,31 @@ std::list<Submix>::iterator Engine::_destroySubmix(Submix* smx)
return m_activeSubmixes.erase(smx->m_engineIt);
}
void Engine::_bringOutYourDead()
{
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ;)
{
Voice* vox = it->get();
vox->_bringOutYourDead();
if (vox->m_dead)
{
it = _destroyVoice(vox);
continue;
}
++it;
}
}
/** Update all active audio entities and fill OS audio buffers as needed */
void Engine::pumpEngine()
{
m_backend.pumpAndMixVoices();
_bringOutYourDead();
/* Determine lowest available free vid */
int maxVid = -1;
for (Voice& vox : m_activeVoices)
maxVid = std::max(maxVid, vox.maxVid());
for (std::shared_ptr<Voice>& vox : m_activeVoices)
maxVid = std::max(maxVid, vox->maxVid());
m_nextVid = maxVid + 1;
}
@ -92,9 +111,10 @@ void Engine::removeAudioGroup(int groupId)
/* Destroy runtime entities within group */
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ;)
{
if (it->getAudioGroup().groupId() == groupId)
Voice* vox = it->get();
if (vox->getAudioGroup().groupId() == groupId)
{
it->_destroy();
vox->_destroy();
it = m_activeVoices.erase(it);
continue;
}
@ -103,9 +123,10 @@ void Engine::removeAudioGroup(int groupId)
for (auto it = m_activeEmitters.begin() ; it != m_activeEmitters.end() ;)
{
if (it->getAudioGroup().groupId() == groupId)
Emitter* emitter = it->get();
if (emitter->getAudioGroup().groupId() == groupId)
{
it->_destroy();
emitter->_destroy();
it = m_activeEmitters.erase(it);
continue;
}
@ -114,9 +135,10 @@ void Engine::removeAudioGroup(int groupId)
for (auto it = m_activeSequencers.begin() ; it != m_activeSequencers.end() ;)
{
if (it->getAudioGroup().groupId() == groupId)
Sequencer* seq = it->get();
if (seq->getAudioGroup().groupId() == groupId)
{
it->_destroy();
seq->_destroy();
it = m_activeSequencers.erase(it);
continue;
}
@ -146,10 +168,12 @@ void Engine::removeSubmix(Submix* smx)
/* Delete all voices bound to submix */
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ;)
{
Submix* vsmx = it->getSubmix();
Voice* vox = it->get();
Submix* vsmx = vox->getSubmix();
if (vsmx && vsmx == smx)
{
it->_destroy();
vox->_destroy();
it = m_activeVoices.erase(it);
continue;
}
@ -174,7 +198,7 @@ void Engine::removeSubmix(Submix* smx)
}
/** Start soundFX playing from loaded audio groups */
Voice* Engine::fxStart(int sfxId, float vol, float pan, Submix* smx)
std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, Submix* smx)
{
auto search = m_sfxLookup.find(sfxId);
if (search == m_sfxLookup.end())
@ -184,14 +208,14 @@ Voice* Engine::fxStart(int sfxId, float vol, float pan, Submix* smx)
if (!grp)
return nullptr;
Voice* ret = _allocateVoice(*grp, 32000.0, true, false, smx);
std::shared_ptr<Voice> ret = _allocateVoice(*grp, 32000.0, true, false, smx);
ret->setVolume(vol);
ret->setPan(pan);
return ret;
}
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
Emitter* Engine::addEmitter(const Vector3f& pos, const Vector3f& dir, float maxDist,
std::shared_ptr<Emitter> Engine::addEmitter(const Vector3f& pos, const Vector3f& dir, float maxDist,
float falloff, int sfxId, float minVol, float maxVol, Submix* smx)
{
auto search = m_sfxLookup.find(sfxId);
@ -202,9 +226,9 @@ Emitter* Engine::addEmitter(const Vector3f& pos, const Vector3f& dir, float maxD
if (!grp)
return nullptr;
Voice* vox = _allocateVoice(*grp, 32000.0, true, true, smx);
m_activeEmitters.emplace_back(*this, *grp, *vox);
Emitter& ret = m_activeEmitters.back();
std::shared_ptr<Voice> vox = _allocateVoice(*grp, 32000.0, true, true, smx);
m_activeEmitters.emplace(m_activeEmitters.end(), new Emitter(*this, *grp, *vox));
Emitter& ret = *m_activeEmitters.back();
ret.setPos(pos);
ret.setDir(dir);
ret.setMaxDist(maxDist);
@ -212,36 +236,38 @@ Emitter* Engine::addEmitter(const Vector3f& pos, const Vector3f& dir, float maxD
ret.setMinVol(minVol);
ret.setMaxVol(maxVol);
return &ret;
return m_activeEmitters.back();
}
/** Start song playing from loaded audio groups */
Sequencer* Engine::seqPlay(int groupId, int songId, const unsigned char* arrData)
std::shared_ptr<Sequencer> Engine::seqPlay(int groupId, int songId, const unsigned char* arrData)
{
return {};
}
/** Find voice from VoiceId */
Voice* Engine::findVoice(int vid)
std::shared_ptr<Voice> Engine::findVoice(int vid)
{
for (Voice& vox : m_activeVoices)
if (vox.vid() == vid)
return &vox;
return nullptr;
for (std::shared_ptr<Voice>& vox : m_activeVoices)
if (vox->vid() == vid)
return vox;
return {};
}
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `flag` set */
void Engine::killKeygroup(uint8_t kg, uint8_t flag)
void Engine::killKeygroup(uint8_t kg, bool now)
{
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ;)
{
if (it->m_keygroup == kg)
Voice* vox = it->get();
if (vox->m_keygroup == kg)
{
if (flag)
if (now)
{
it = _destroyVoice(&*it);
it = _destroyVoice(vox);
continue;
}
it->keyOff();
vox->keyOff();
}
++it;
}
@ -251,8 +277,11 @@ void Engine::killKeygroup(uint8_t kg, uint8_t flag)
void Engine::sendMacroMessage(ObjectId macroId, int32_t val)
{
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ; ++it)
if (it->getObjectId() == macroId)
it->message(val);
{
Voice* vox = it->get();
if (vox->getObjectId() == macroId)
vox->message(val);
}
}
}

View File

@ -27,7 +27,7 @@ void SoundMacroState::Evaluator::addComponent(uint8_t midiCtrl, float scale,
m_comps.push_back({midiCtrl, scale, combine, varType});
}
float SoundMacroState::Evaluator::evaluate(Voice& vox, const SoundMacroState& st)
float SoundMacroState::Evaluator::evaluate(const Voice& vox, const SoundMacroState& st) const
{
float value = 0.f;
@ -115,7 +115,7 @@ void SoundMacroState::initialize(const unsigned char* ptr, int step)
initialize(ptr, step, 1000.f, 0, 0, 0);
}
void SoundMacroState::initialize(const unsigned char* ptr, int step, float ticksPerSec,
void SoundMacroState::initialize(const unsigned char* ptr, int step, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod)
{
m_ticksPerSec = ticksPerSec;
@ -138,7 +138,7 @@ void SoundMacroState::initialize(const unsigned char* ptr, int step, float ticks
m_header.swapBig();
}
bool SoundMacroState::advance(Voice& vox, float dt)
bool SoundMacroState::advance(Voice& vox, double dt)
{
/* Nothing if uninitialized or finished */
if (m_pc.empty() || m_pc.back().first == nullptr || m_pc.back().second == -1)
@ -320,7 +320,7 @@ bool SoundMacroState::advance(Voice& vox, float dt)
//int8_t priority = cmd.m_data[5];
//int8_t maxVoices = cmd.m_data[6];
Voice* sibVox = vox.startChildMacro(addNote, macroId, macroStep);
std::shared_ptr<Voice> sibVox = vox.startChildMacro(addNote, macroId, macroStep);
if (sibVox)
m_lastPlayMacroVid = sibVox->vid();
@ -335,14 +335,14 @@ bool SoundMacroState::advance(Voice& vox, float dt)
{
if (m_lastPlayMacroVid != -1)
{
Voice* otherVox = vox.getEngine().findVoice(m_lastPlayMacroVid);
std::shared_ptr<Voice> otherVox = vox.getEngine().findVoice(m_lastPlayMacroVid);
if (otherVox)
otherVox->keyOff();
}
}
else
{
Voice* otherVox = vox.getEngine().findVoice(m_variables[vid]);
std::shared_ptr<Voice> otherVox = vox.getEngine().findVoice(m_variables[vid]);
if (otherVox)
otherVox->keyOff();
}
@ -800,7 +800,7 @@ bool SoundMacroState::advance(Voice& vox, float dt)
if (isVar)
{
Voice* findVox = vox.getEngine().findVoice(m_variables[vid]);
std::shared_ptr<Voice> findVox = vox.getEngine().findVoice(m_variables[vid]);
if (findVox)
findVox->message(val);
}

View File

@ -17,19 +17,37 @@ Submix::Submix(Engine& engine, Submix* smx)
m_submix->m_activeSubmixes.insert(this);
}
bool Submix::canApplyEffect() const
EffectChorus& Submix::makeChorus(uint32_t baseDelay, uint32_t variation, uint32_t period)
{
return makeEffect<EffectChorus>(baseDelay, variation, period);
}
EffectDelay& Submix::makeDelay(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput)
{
return makeEffect<EffectDelay>(initDelay, initFeedback, initOutput);
}
EffectReverb& Submix::makeReverbStd(float coloration, float mix, float time,
float damping, float preDelay)
{
return makeEffect<EffectReverb>(coloration, mix, time, damping, preDelay);
}
EffectReverbHi& Submix::makeReverbHi(float coloration, float mix, float time,
float damping, float preDelay, float crosstalk)
{
return makeEffect<EffectReverbHi>(coloration, mix, time, damping, preDelay, crosstalk);
}
void Submix::applyEffect(int16_t* audio, size_t frameCount, const ChannelMap& chanMap) const
{
}
void Submix::applyEffect(int16_t* audio, const ChannelMap& chanMap, double sampleRate) const
void Submix::applyEffect(int32_t* audio, size_t frameCount, const ChannelMap& chanMap) const
{
}
void Submix::applyEffect(int32_t* audio, const ChannelMap& chanMap, double sampleRate) const
{
}
void Submix::applyEffect(float* audio, const ChannelMap& chanMap, double sampleRate) const
void Submix::applyEffect(float* audio, size_t frameCount, const ChannelMap& chanMap) const
{
}

View File

@ -90,17 +90,32 @@ void Voice::_setTotalPitch(int32_t cents)
m_backendVoice->setPitchRatio(ratio);
}
Voice* Voice::_allocateVoice(double sampleRate, bool dynamicPitch)
void Voice::_bringOutYourDead()
{
auto it = m_childVoices.emplace(m_childVoices.end(), m_engine, m_audioGroup,
m_engine.m_nextVid++, m_emitter, m_submix);
m_childVoices.back().m_backendVoice =
m_engine.getBackend().allocateVoice(m_childVoices.back(), sampleRate, dynamicPitch);
m_childVoices.back().m_engineIt = it;
return &m_childVoices.back();
for (auto it = m_childVoices.begin() ; it != m_childVoices.end() ;)
{
Voice* vox = it->get();
vox->_bringOutYourDead();
if (vox->m_dead)
{
it = _destroyVoice(vox);
continue;
}
++it;
}
}
std::list<Voice>::iterator Voice::_destroyVoice(Voice* voice)
std::shared_ptr<Voice> Voice::_allocateVoice(double sampleRate, bool dynamicPitch)
{
auto it = m_childVoices.emplace(m_childVoices.end(), new Voice(m_engine, m_audioGroup,
m_engine.m_nextVid++, m_emitter, m_submix));
m_childVoices.back()->m_backendVoice =
m_engine.getBackend().allocateVoice(*m_childVoices.back(), sampleRate, dynamicPitch);
m_childVoices.back()->m_engineIt = it;
return m_childVoices.back();
}
std::list<std::shared_ptr<Voice>>::iterator Voice::_destroyVoice(Voice* voice)
{
voice->_destroy();
return m_childVoices.erase(voice->m_engineIt);
@ -231,16 +246,35 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
size_t samplesProc = 0;
if (m_curSample)
{
m_dead = m_state.advance(*this, samples / m_sampleRate);
uint32_t block = m_curSamplePos / 14;
uint32_t rem = m_curSamplePos % 14;
if (rem)
{
uint32_t decSamples = DSPDecompressFrameRanged(data, m_curSampleData + 8 * block,
uint32_t remCount = std::min(samplesRem, m_lastSamplePos - block * 14);
uint32_t decSamples;
switch (m_curFormat)
{
case SampleFormat::DSP:
{
decSamples = DSPDecompressFrameRanged(data, m_curSampleData + 8 * block,
m_curSample->second.m_coefs,
&m_prev1, &m_prev2, rem,
std::min(samplesRem,
m_lastSamplePos - block * 14));
&m_prev1, &m_prev2, rem, remCount);
break;
}
case SampleFormat::PCM:
{
const int16_t* pcm = reinterpret_cast<const int16_t*>(m_curSampleData);
for (uint32_t i=0 ; i<remCount ; ++i)
data[i] = SBig(pcm[m_curSamplePos+i]);
decSamples = remCount;
break;
}
default: return 0;
}
/* Per-sample processing */
for (int i=0 ; i<decSamples ; ++i)
@ -265,11 +299,28 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
while (samplesRem)
{
block = m_curSamplePos / 14;
uint32_t decSamples = DSPDecompressFrame(data, m_curSampleData + 8 * block,
uint32_t remCount = std::min(samplesRem, m_lastSamplePos - block * 14);
uint32_t decSamples;
switch (m_curFormat)
{
case SampleFormat::DSP:
{
decSamples = DSPDecompressFrame(data, m_curSampleData + 8 * block,
m_curSample->second.m_coefs,
&m_prev1, &m_prev2,
std::min(samplesRem,
m_lastSamplePos - block * 14));
&m_prev1, &m_prev2, remCount);
break;
}
case SampleFormat::PCM:
{
const int16_t* pcm = reinterpret_cast<const int16_t*>(m_curSampleData);
for (uint32_t i=0 ; i<remCount ; ++i)
data[i] = SBig(pcm[m_curSamplePos+i]);
decSamples = remCount;
break;
}
default: return 0;
}
/* Per-sample processing */
for (int i=0 ; i<decSamples ; ++i)
@ -292,28 +343,33 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
}
}
else
{
m_dead = m_state.advance(*this, 0.0);
memset(data, 0, sizeof(int16_t) * samples);
}
if (m_dead)
m_voxState = VoiceState::Finished;
return samples;
}
int Voice::maxVid() const
{
int maxVid = m_vid;
for (const Voice& vox : m_childVoices)
maxVid = std::max(maxVid, vox.maxVid());
for (const std::shared_ptr<Voice>& vox : m_childVoices)
maxVid = std::max(maxVid, vox->maxVid());
return maxVid;
}
Voice* Voice::startChildMacro(int8_t addNote, ObjectId macroId, int macroStep)
std::shared_ptr<Voice> Voice::startChildMacro(int8_t addNote, ObjectId macroId, int macroStep)
{
Voice* vox = _allocateVoice(32000.0, true);
vox->loadSoundMacro(macroId, macroStep, 1000.f, m_state.m_initKey + addNote,
std::shared_ptr<Voice> vox = _allocateVoice(32000.0, true);
vox->loadSoundMacro(macroId, macroStep, 1000.0, m_state.m_initKey + addNote,
m_state.m_initVel, m_state.m_initMod);
return vox;
}
bool Voice::loadSoundMacro(ObjectId macroId, int macroStep, float ticksPerSec,
bool Voice::loadSoundMacro(ObjectId macroId, int macroStep, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod,
bool pushPc)
{
@ -332,6 +388,7 @@ bool Voice::loadSoundMacro(ObjectId macroId, int macroStep, float ticksPerSec,
m_state.m_header.swapBig();
}
m_voxState = VoiceState::Playing;
return true;
}
@ -341,6 +398,11 @@ void Voice::keyOff()
m_sustainKeyOff = true;
else
_doKeyOff();
for (const std::shared_ptr<Voice>& vox : m_childVoices)
vox->keyOff();
m_voxState = VoiceState::KeyOff;
}
void Voice::message(int32_t val)
@ -358,18 +420,25 @@ void Voice::startSample(int16_t sampId, int32_t offset)
m_pitchDirty = true;
m_backendVoice->stop();
m_backendVoice->resetSampleRate(m_curSample->first.m_sampleRate);
m_backendVoice->start();
m_curSamplePos = offset;
m_curSampleData = m_audioGroup.getSampleData(m_curSample->first.m_sampleOff);
m_prev1 = 0;
m_prev2 = 0;
uint32_t numSamples = m_curSample->first.m_numSamples & 0xffffff;
SampleFormat fmt = SampleFormat(m_curSample->first.m_numSamples >> 24);
m_lastSamplePos = m_curSample->first.m_loopLengthSamples ?
(m_curSample->first.m_loopStartSample + m_curSample->first.m_loopLengthSamples) :
m_curSample->first.m_numSamples;
(m_curSample->first.m_loopStartSample + m_curSample->first.m_loopLengthSamples) : numSamples;
if (fmt != SampleFormat::DSP && fmt != SampleFormat::PCM)
{
m_curSample = nullptr;
return;
}
_checkSamplePos();
/* Seek DSPADPCM state if needed */
if (m_curSamplePos)
if (m_curSamplePos && fmt == SampleFormat::DSP)
{
uint32_t block = m_curSamplePos / 14;
uint32_t rem = m_curSamplePos % 14;
@ -381,6 +450,8 @@ void Voice::startSample(int16_t sampId, int32_t offset)
DSPDecompressFrameStateOnly(m_curSampleData + 8 * block, m_curSample->second.m_coefs,
&m_prev1, &m_prev2, rem);
}
m_backendVoice->start();
}
}
@ -457,6 +528,9 @@ void Voice::setPedal(bool pedal)
_doKeyOff();
}
m_sustained = pedal;
for (std::shared_ptr<Voice>& vox : m_childVoices)
vox->setPedal(pedal);
}
void Voice::setDoppler(float doppler)