mirror of https://github.com/AxioDL/amuse.git
Refactors, bug fixes, lotsa things
This commit is contained in:
parent
1fb9ef3239
commit
bee8719d4e
|
@ -61,6 +61,6 @@ add_library(amuse
|
||||||
${EXTRAS})
|
${EXTRAS})
|
||||||
|
|
||||||
if(TARGET boo)
|
if(TARGET boo)
|
||||||
add_executable(amusetool WIN32 driver/main.cpp)
|
add_executable(amuseplay WIN32 driver/main.cpp)
|
||||||
target_link_libraries(amusetool amuse boo ${BOO_SYS_LIBS} logvisor athena-core)
|
target_link_libraries(amuseplay amuse boo ${BOO_SYS_LIBS} logvisor athena-core)
|
||||||
endif()
|
endif()
|
||||||
|
|
112
driver/main.cpp
112
driver/main.cpp
|
@ -10,7 +10,7 @@
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
static logvisor::Module Log("amusetool");
|
static logvisor::Module Log("amuseplay");
|
||||||
|
|
||||||
static amuse::IntrusiveAudioGroupData LoadFromArgs(int argc, const boo::SystemChar** argv,
|
static amuse::IntrusiveAudioGroupData LoadFromArgs(int argc, const boo::SystemChar** argv,
|
||||||
std::string& descOut, bool& good)
|
std::string& descOut, bool& good)
|
||||||
|
@ -161,9 +161,11 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
|
|
||||||
/* SFX playback selection */
|
/* SFX playback selection */
|
||||||
int m_sfxId = -1;
|
int m_sfxId = -1;
|
||||||
amuse::Voice* m_vox = nullptr;
|
std::shared_ptr<amuse::Voice> m_vox;
|
||||||
|
|
||||||
/* Control state */
|
/* Control state */
|
||||||
|
float m_volume = 1.f;
|
||||||
|
bool m_updateDisp = false;
|
||||||
bool m_running = true;
|
bool m_running = true;
|
||||||
bool m_wantsNext = false;
|
bool m_wantsNext = false;
|
||||||
bool m_wantsPrev = false;
|
bool m_wantsPrev = false;
|
||||||
|
@ -180,8 +182,7 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░\n"
|
"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░\n"
|
||||||
"<left/right>: cycle MIDI setup / channel, <up/down>: volume\n"
|
"<left/right>: cycle MIDI setup / channel, <up/down>: volume\n"
|
||||||
"<tab>: sustain pedal, <window-Y>: pitch wheel, <window-X>: mod wheel\n"
|
"<tab>: sustain pedal, <window-Y>: pitch wheel, <window-X>: mod wheel\n"
|
||||||
"<Z/X>: octave, <C/V>: velocity\n"
|
"<Z/X>: octave, <C/V>: velocity, <Q>: quit\n");
|
||||||
"<Q>: quit\n");
|
|
||||||
|
|
||||||
std::map<int, const std::array<amuse::SongGroupIndex::MIDISetup, 16>*> sortEntries
|
std::map<int, const std::array<amuse::SongGroupIndex::MIDISetup, 16>*> sortEntries
|
||||||
(index.m_midiSetups.cbegin(), index.m_midiSetups.cend());
|
(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,
|
void SFXLoop(const amuse::AudioGroup& group,
|
||||||
const amuse::SFXGroupIndex& index)
|
const amuse::SFXGroupIndex& index)
|
||||||
{
|
{
|
||||||
printf("<space>: keyon/keyon, <left/right>: cycle SFX, <up/down>: volume\n"
|
printf("<space>: keyon/keyon, <left/right>: cycle SFX, <up/down>: volume, <Q>: quit\n");
|
||||||
"<Q>: quit\n");
|
|
||||||
|
|
||||||
std::map<uint16_t, const amuse::SFXGroupIndex::SFXEntry*> sortEntries
|
std::map<uint16_t, const amuse::SFXGroupIndex::SFXEntry*> sortEntries
|
||||||
(index.m_sfxEntries.cbegin(), index.m_sfxEntries.cend());
|
(index.m_sfxEntries.cbegin(), index.m_sfxEntries.cend());
|
||||||
auto sfxIt = sortEntries.cbegin();
|
auto sfxIt = sortEntries.cbegin();
|
||||||
if (sfxIt != sortEntries.cend())
|
if (sfxIt != sortEntries.cend())
|
||||||
{
|
SelectSFX(sfxIt->first);
|
||||||
m_sfxId = sfxIt->first;
|
|
||||||
m_vox = m_engine->fxStart(m_sfxId, 1.f, 0.f);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (m_running)
|
while (m_running)
|
||||||
{
|
{
|
||||||
m_events.dispatchEvents();
|
m_events.dispatchEvents();
|
||||||
m_engine->pumpEngine();
|
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();
|
m_win->waitForRetrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void charKeyDown(unsigned long charCode)
|
void charKeyDown(unsigned long charCode)
|
||||||
{
|
{
|
||||||
|
if (charCode == 'q' || charCode == 'Q')
|
||||||
|
{
|
||||||
|
m_running = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_sfxGroup)
|
if (m_sfxGroup)
|
||||||
{
|
{
|
||||||
switch (charCode)
|
switch (charCode)
|
||||||
|
@ -240,7 +289,8 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
if (m_vox && m_vox->state() == amuse::VoiceState::Playing)
|
if (m_vox && m_vox->state() == amuse::VoiceState::Playing)
|
||||||
m_vox->keyOff();
|
m_vox->keyOff();
|
||||||
else if (m_sfxId != -1)
|
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;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,10 +306,24 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
int appMain(boo::IApplication* app)
|
int appMain(boo::IApplication* app)
|
||||||
{
|
{
|
||||||
/* Event window */
|
/* Event window */
|
||||||
m_win = app->newWindow("amusetool", 1);
|
m_win = app->newWindow("amuseplay", 1);
|
||||||
m_win->setWindowFrame(100, 100, 100, 100);
|
|
||||||
m_win->setCallback(&m_events);
|
m_win->setCallback(&m_events);
|
||||||
|
m_win->setWindowFrame(100, 100, 100, 100);
|
||||||
|
m_win->setStyle(~boo::EWindowStyle::Resize);
|
||||||
m_win->showWindow();
|
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 */
|
/* Load data */
|
||||||
std::string desc;
|
std::string desc;
|
||||||
|
@ -267,7 +331,7 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
amuse::IntrusiveAudioGroupData data = LoadFromArgs(m_argc, m_argv, desc, good);
|
amuse::IntrusiveAudioGroupData data = LoadFromArgs(m_argc, m_argv, desc, good);
|
||||||
if (!good)
|
if (!good)
|
||||||
Log.report(logvisor::Fatal, "incomplete data in args");
|
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 */
|
/* Load project to assemble group list */
|
||||||
amuse::AudioGroupProject proj(data.getProj());
|
amuse::AudioGroupProject proj(data.getProj());
|
||||||
|
@ -280,12 +344,12 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
printf("Multiple Audio Groups discovered:\n");
|
printf("Multiple Audio Groups discovered:\n");
|
||||||
for (const auto& pair : proj.songGroups())
|
for (const auto& pair : proj.songGroups())
|
||||||
{
|
{
|
||||||
printf(" %d (SongGroup) %" PRISize " normal-pages, %" PRISize " drum-pages\n",
|
printf(" %d (SongGroup) %" PRISize " normal-pages, %" PRISize " drum-pages\n",
|
||||||
pair.first, pair.second.m_normPages.size(), pair.second.m_drumPages.size());
|
pair.first, pair.second.m_normPages.size(), pair.second.m_drumPages.size());
|
||||||
}
|
}
|
||||||
for (const auto& pair : proj.sfxGroups())
|
for (const auto& pair : proj.sfxGroups())
|
||||||
{
|
{
|
||||||
printf(" %d (SFXGroup) %" PRISize " sfx-entries\n",
|
printf(" %d (SFXGroup) %" PRISize " sfx-entries\n",
|
||||||
pair.first, pair.second.m_sfxEntries.size());
|
pair.first, pair.second.m_sfxEntries.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,6 +440,20 @@ void EventCallback::specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods,
|
||||||
case boo::ESpecialKey::Right:
|
case boo::ESpecialKey::Right:
|
||||||
m_app.m_wantsNext = true;
|
m_app.m_wantsNext = true;
|
||||||
break;
|
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;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -393,7 +471,7 @@ int main(int argc, const boo::SystemChar** argv)
|
||||||
logvisor::RegisterConsoleLogger();
|
logvisor::RegisterConsoleLogger();
|
||||||
AppCallback app(argc, argv);
|
AppCallback app(argc, argv);
|
||||||
int ret = boo::ApplicationRun(boo::IApplication::EPlatformType::Auto,
|
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");
|
printf("IM DYING!!\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,6 @@ public:
|
||||||
AudioGroup(int groupId, const AudioGroupData& data);
|
AudioGroup(int groupId, const AudioGroupData& data);
|
||||||
|
|
||||||
int groupId() const {return m_groupId;}
|
int groupId() const {return m_groupId;}
|
||||||
bool sfxInGroup(int sfxId) const;
|
|
||||||
bool songInGroup(int songId) const;
|
|
||||||
|
|
||||||
const Sample* getSample(int sfxId) const;
|
const Sample* getSample(int sfxId) const;
|
||||||
const unsigned char* getSampleData(uint32_t offset) const;
|
const unsigned char* getSampleData(uint32_t offset) const;
|
||||||
|
|
|
@ -57,6 +57,8 @@ public:
|
||||||
BooBackendSubmix(boo::IAudioSubmix& parent, Submix& clientSmx);
|
BooBackendSubmix(boo::IAudioSubmix& parent, Submix& clientSmx);
|
||||||
void setChannelGains(const float gains[8]);
|
void setChannelGains(const float gains[8]);
|
||||||
std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch);
|
std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch);
|
||||||
|
double getSampleRate() const;
|
||||||
|
SubmixFormat getSampleFormat() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Backend voice allocator implementation for boo mixer */
|
/** 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<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch);
|
||||||
std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx);
|
std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx);
|
||||||
AudioChannelSet getAvailableSet();
|
AudioChannelSet getAvailableSet();
|
||||||
|
void pumpAndMixVoices();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,14 @@ namespace amuse
|
||||||
{
|
{
|
||||||
class ChannelMap;
|
class ChannelMap;
|
||||||
|
|
||||||
|
class EffectBaseTypeless
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~EffectBaseTypeless() = default;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class EffectBase
|
class EffectBase : public EffectBaseTypeless
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)=0;
|
virtual void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)=0;
|
||||||
|
|
|
@ -7,12 +7,51 @@
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
|
template <typename T>
|
||||||
|
class EffectChorusImp;
|
||||||
|
|
||||||
#define AMUSE_CHORUS_NUM_BLOCKS 3
|
#define AMUSE_CHORUS_NUM_BLOCKS 3
|
||||||
|
|
||||||
/** Mixes the audio back into itself after continuously-varying delay */
|
/** 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>
|
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 */
|
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);
|
void doSrc2(size_t blockSamples, size_t chanCount);
|
||||||
} x6c_src;
|
} 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_sampsPerMs; /**< canonical count of samples per ms for the current backend */
|
||||||
uint32_t m_blockSamples; /**< count of samples in a 5ms block */
|
uint32_t m_blockSamples; /**< count of samples in a 5ms block */
|
||||||
bool m_dirty = true; /**< needs update of internal parameter data */
|
|
||||||
|
|
||||||
void _update();
|
void _update();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~EffectChorus();
|
~EffectChorusImp();
|
||||||
EffectChorus(uint32_t baseDelay, uint32_t variation, uint32_t period, double sampleRate);
|
EffectChorusImp(uint32_t baseDelay, uint32_t variation, uint32_t period, double sampleRate);
|
||||||
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,29 +8,20 @@
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
|
template <typename T>
|
||||||
|
class EffectDelayImp;
|
||||||
|
|
||||||
/** Mixes the audio back into itself after specified delay */
|
/** Mixes the audio back into itself after specified delay */
|
||||||
template <typename T>
|
class EffectDelay
|
||||||
class EffectDelay : public EffectBase<T>
|
|
||||||
{
|
{
|
||||||
uint32_t x0_currentSize[8]; /**< per-channel delay-line buffer sizes */
|
protected:
|
||||||
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 x3c_delay[8]; /**< [10, 5000] time in ms of each channel's delay */
|
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 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 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 */
|
bool m_dirty = true; /**< needs update of internal parameter data */
|
||||||
void _update();
|
|
||||||
public:
|
public:
|
||||||
EffectDelay(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput, double sampleRate);
|
template <typename T>
|
||||||
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
using ImpType = EffectDelayImp<T>;
|
||||||
|
|
||||||
void setDelay(uint32_t delay)
|
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__
|
#endif // __AMUSE_EFFECTDELAY_HPP__
|
||||||
|
|
|
@ -21,46 +21,45 @@ struct ReverbDelayLine
|
||||||
void setdelay(int32_t delay);
|
void setdelay(int32_t delay);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class EffectReverbStdImp;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class EffectReverbHiImp;
|
||||||
|
|
||||||
/** Reverb effect with configurable reflection filtering */
|
/** Reverb effect with configurable reflection filtering */
|
||||||
template <typename T, size_t AP, size_t C>
|
class EffectReverb
|
||||||
class EffectReverb : public EffectBase<T>
|
|
||||||
{
|
{
|
||||||
protected:
|
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 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 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 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 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 */
|
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 */
|
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,
|
EffectReverb(float coloration, float mix, float time,
|
||||||
float damping, float preDelay, double sampleRate);
|
float damping, float preDelay);
|
||||||
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
public:
|
||||||
|
template <typename T>
|
||||||
|
using ImpType = EffectReverbStdImp<T>;
|
||||||
|
|
||||||
void setColoration(float coloration)
|
void setColoration(float coloration)
|
||||||
{
|
{
|
||||||
x140_x1c8_coloration = clamp(0.f, coloration, 1.f);
|
x140_x1c8_coloration = clamp(0.f, coloration, 1.f);
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setMix(float mix)
|
void setMix(float mix)
|
||||||
{
|
{
|
||||||
x144_x1cc_mix = clamp(0.f, mix, 1.f);
|
x144_x1cc_mix = clamp(0.f, mix, 1.f);
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTime(float time)
|
void setTime(float time)
|
||||||
{
|
{
|
||||||
x148_x1d0_time = clamp(0.01f, time, 10.f);
|
x148_x1d0_time = clamp(0.01f, time, 10.f);
|
||||||
|
@ -71,6 +70,7 @@ public:
|
||||||
x14c_x1d4_damping = clamp(0.f, damping, 1.f);
|
x14c_x1d4_damping = clamp(0.f, damping, 1.f);
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPreDelay(float preDelay)
|
void setPreDelay(float preDelay)
|
||||||
{
|
{
|
||||||
x150_x1d8_preDelay = clamp(0.f, preDelay, 0.1f);
|
x150_x1d8_preDelay = clamp(0.f, preDelay, 0.1f);
|
||||||
|
@ -78,37 +78,76 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Standard-quality 2-stage reverb */
|
/** Reverb effect with configurable reflection filtering, adds per-channel low-pass and crosstalk */
|
||||||
template <typename T>
|
class EffectReverbHi : public EffectReverb
|
||||||
class EffectReverbStd : public EffectReverb<T, 2, 2>
|
|
||||||
{
|
{
|
||||||
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 */
|
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);
|
template <typename T>
|
||||||
void _doCrosstalk(T* audio, float wet, float dry, int chanCount, int sampleCount);
|
friend class EffectReverbHiImp;
|
||||||
public:
|
|
||||||
EffectReverbHi(float coloration, float mix, float time,
|
EffectReverbHi(float coloration, float mix, float time,
|
||||||
float damping, float preDelay, float crosstalk, double sampleRate);
|
float damping, float preDelay, float crosstalk);
|
||||||
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
public:
|
||||||
|
template <typename T>
|
||||||
|
using ImpType = EffectReverbHiImp<T>;
|
||||||
|
|
||||||
void setCrosstalk(float crosstalk)
|
void setCrosstalk(float crosstalk)
|
||||||
{
|
{
|
||||||
x1dc_crosstalk = clamp(0.f, crosstalk, 1.f);
|
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__
|
#endif // __AMUSE_EFFECTREVERB_HPP__
|
||||||
|
|
|
@ -26,18 +26,19 @@ class Engine
|
||||||
|
|
||||||
IBackendVoiceAllocator& m_backend;
|
IBackendVoiceAllocator& m_backend;
|
||||||
std::unordered_map<int, std::unique_ptr<AudioGroup>> m_audioGroups;
|
std::unordered_map<int, std::unique_ptr<AudioGroup>> m_audioGroups;
|
||||||
std::list<Voice> m_activeVoices;
|
std::list<std::shared_ptr<Voice>> m_activeVoices;
|
||||||
std::list<Emitter> m_activeEmitters;
|
std::list<std::shared_ptr<Emitter>> m_activeEmitters;
|
||||||
std::list<Sequencer> m_activeSequencers;
|
std::list<std::shared_ptr<Sequencer>> m_activeSequencers;
|
||||||
std::list<Submix> m_activeSubmixes;
|
std::list<Submix> m_activeSubmixes;
|
||||||
std::unordered_map<uint16_t, std::pair<AudioGroup*, ObjectId>> m_sfxLookup;
|
std::unordered_map<uint16_t, std::pair<AudioGroup*, ObjectId>> m_sfxLookup;
|
||||||
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
|
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
|
||||||
int m_nextVid = 0;
|
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);
|
bool dynamicPitch, bool emitter, Submix* smx);
|
||||||
Submix* _allocateSubmix(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);
|
std::list<Submix>::iterator _destroySubmix(Submix* smx);
|
||||||
|
void _bringOutYourDead();
|
||||||
public:
|
public:
|
||||||
Engine(IBackendVoiceAllocator& backend);
|
Engine(IBackendVoiceAllocator& backend);
|
||||||
|
|
||||||
|
@ -60,21 +61,21 @@ public:
|
||||||
void removeSubmix(Submix* smx);
|
void removeSubmix(Submix* smx);
|
||||||
|
|
||||||
/** Start soundFX playing from loaded audio groups */
|
/** 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 */
|
/** 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,
|
float falloff, int sfxId, float minVol, float maxVol,
|
||||||
Submix* smx=nullptr);
|
Submix* smx=nullptr);
|
||||||
|
|
||||||
/** Start song playing from loaded audio groups */
|
/** 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 */
|
/** 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 */
|
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */
|
||||||
void killKeygroup(uint8_t kg, uint8_t flag);
|
void killKeygroup(uint8_t kg, bool now);
|
||||||
|
|
||||||
/** Send all voices using `macroId` the message `val` */
|
/** Send all voices using `macroId` the message `val` */
|
||||||
void sendMacroMessage(ObjectId macroId, int32_t val);
|
void sendMacroMessage(ObjectId macroId, int32_t val);
|
||||||
|
|
|
@ -26,7 +26,8 @@ using ObjectId = uint16_t;
|
||||||
/** Common 'engine child' class */
|
/** Common 'engine child' class */
|
||||||
class Entity
|
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 Engine;
|
||||||
friend class SoundMacroState;
|
friend class SoundMacroState;
|
||||||
bool m_destroyed = false;
|
bool m_destroyed = false;
|
||||||
|
|
|
@ -8,6 +8,13 @@ namespace amuse
|
||||||
class IBackendVoice;
|
class IBackendVoice;
|
||||||
class Voice;
|
class Voice;
|
||||||
|
|
||||||
|
enum class SubmixFormat
|
||||||
|
{
|
||||||
|
Int16,
|
||||||
|
Int32,
|
||||||
|
Float
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Client-implemented submix instance
|
* @brief Client-implemented submix instance
|
||||||
*/
|
*/
|
||||||
|
@ -21,6 +28,12 @@ public:
|
||||||
|
|
||||||
/** Amuse obtains a new voice from the platform outputting to this submix */
|
/** 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;
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,9 @@ public:
|
||||||
|
|
||||||
/** Amuse obtains speaker-configuration from the platform this way */
|
/** Amuse obtains speaker-configuration from the platform this way */
|
||||||
virtual AudioChannelSet getAvailableSet()=0;
|
virtual AudioChannelSet getAvailableSet()=0;
|
||||||
|
|
||||||
|
/** Amuse flushes voice samples to the backend this way */
|
||||||
|
virtual void pumpAndMixVoices()=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,7 @@ class SoundMacroState
|
||||||
/** 'program counter' stack for the active SoundMacro */
|
/** 'program counter' stack for the active SoundMacro */
|
||||||
std::vector<std::pair<const unsigned char*, int>> m_pc;
|
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_initVel; /**< Velocity played for this macro invocation */
|
||||||
uint8_t m_initMod; /**< Modulation 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 */
|
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 */
|
uint8_t m_curMod; /**< Current modulation played for this macro invocation */
|
||||||
uint32_t m_curKey; /**< Current key played for this macro invocation (in cents) */
|
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_keyoff; /**< keyoff message has been received */
|
||||||
bool m_sampleEnd; /**< sample has finished playback */
|
bool m_sampleEnd; /**< sample has finished playback */
|
||||||
|
|
||||||
bool m_inWait = false; /**< set when timer/keyoff/sampleend wait active */
|
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_keyoffWait = false; /**< set when active wait is a keyoff wait */
|
||||||
bool m_sampleEndWait = false; /**< set when active wait is a sampleend 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_loopCountdown = -1; /**< countdown for current loop */
|
||||||
int m_lastPlayMacroVid = -1; /**< VoiceId from last PlayMacro command */
|
int m_lastPlayMacroVid = -1; /**< VoiceId from last PlayMacro command */
|
||||||
|
@ -180,7 +180,7 @@ class SoundMacroState
|
||||||
Combine combine, VarType varType);
|
Combine combine, VarType varType);
|
||||||
|
|
||||||
/** Calculate value */
|
/** Calculate value */
|
||||||
float evaluate(Voice& vox, const SoundMacroState& st);
|
float evaluate(const Voice& vox, const SoundMacroState& st) const;
|
||||||
|
|
||||||
/** Determine if able to use */
|
/** Determine if able to use */
|
||||||
operator bool() const {return m_comps.size() != 0;}
|
operator bool() const {return m_comps.size() != 0;}
|
||||||
|
@ -220,13 +220,13 @@ class SoundMacroState
|
||||||
public:
|
public:
|
||||||
/** initialize state for SoundMacro data at `ptr` */
|
/** initialize state for SoundMacro data at `ptr` */
|
||||||
void initialize(const unsigned char* ptr, int step);
|
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);
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod);
|
||||||
|
|
||||||
/** advances `dt` seconds worth of commands in the SoundMacro
|
/** advances `dt` seconds worth of commands in the SoundMacro
|
||||||
* @return `true` if END reached
|
* @return `true` if END reached
|
||||||
*/
|
*/
|
||||||
bool advance(Voice& vox, float dt);
|
bool advance(Voice& vox, double dt);
|
||||||
|
|
||||||
/** keyoff event */
|
/** keyoff event */
|
||||||
void keyoffNotify(Voice& vox);
|
void keyoffNotify(Voice& vox);
|
||||||
|
|
|
@ -5,6 +5,10 @@
|
||||||
#include "SoundMacroState.hpp"
|
#include "SoundMacroState.hpp"
|
||||||
#include "IBackendSubmix.hpp"
|
#include "IBackendSubmix.hpp"
|
||||||
#include "IBackendVoice.hpp"
|
#include "IBackendVoice.hpp"
|
||||||
|
#include "EffectBase.hpp"
|
||||||
|
#include "EffectChorus.hpp"
|
||||||
|
#include "EffectDelay.hpp"
|
||||||
|
#include "EffectReverb.hpp"
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
|
@ -22,8 +26,10 @@ class Submix
|
||||||
std::unique_ptr<IBackendSubmix> m_backendSubmix; /**< Handle to client-implemented backend 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<Voice*> m_activeVoices; /**< Secondary index of Voices within Submix */
|
||||||
std::unordered_set<Submix*> m_activeSubmixes; /**< Secondary index of Submixes 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;
|
bool m_destroyed = false;
|
||||||
void _destroy();
|
void _destroy();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Submix(Engine& engine, Submix* smx);
|
Submix(Engine& engine, Submix* smx);
|
||||||
~Submix()
|
~Submix()
|
||||||
|
@ -37,13 +43,61 @@ public:
|
||||||
/** Obtain pointer to Submix's parent Submix */
|
/** Obtain pointer to Submix's parent Submix */
|
||||||
Submix* getParentSubmix() {return m_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 */
|
/** 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 */
|
/** in/out transformation entry for audio effect */
|
||||||
void applyEffect(int16_t* audio, const ChannelMap& chanMap, double sampleRate) const;
|
void applyEffect(int16_t* audio, size_t frameCount, const ChannelMap& chanMap) const;
|
||||||
void applyEffect(int32_t* audio, const ChannelMap& chanMap, double sampleRate) const;
|
|
||||||
void applyEffect(float* audio, const ChannelMap& chanMap, double sampleRate) 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;}
|
Engine& getEngine() {return m_root;}
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,15 +32,22 @@ class Voice : public Entity
|
||||||
int m_vid; /**< VoiceID of this voice instance */
|
int m_vid; /**< VoiceID of this voice instance */
|
||||||
bool m_emitter; /**< Voice is part of an Emitter */
|
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) */
|
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 */
|
std::unique_ptr<IBackendVoice> m_backendVoice; /**< Handle to client-implemented backend voice */
|
||||||
SoundMacroState m_state; /**< State container for SoundMacro playback */
|
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 */
|
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 Sample* m_curSample = nullptr; /**< Current sample entry playing */
|
||||||
const unsigned char* m_curSampleData = nullptr; /**< Current sample data 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_curSamplePos = 0; /**< Current sample position */
|
||||||
uint32_t m_lastSamplePos = 0; /**< Last sample position (or last loop sample) */
|
uint32_t m_lastSamplePos = 0; /**< Last sample position (or last loop sample) */
|
||||||
int16_t m_prev1 = 0; /**< DSPADPCM prev 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 */
|
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 _destroy();
|
||||||
void _reset();
|
void _reset();
|
||||||
bool _checkSamplePos();
|
bool _checkSamplePos();
|
||||||
void _doKeyOff();
|
void _doKeyOff();
|
||||||
bool _advanceSample(int16_t& samp);
|
bool _advanceSample(int16_t& samp);
|
||||||
void _setTotalPitch(int32_t cents);
|
void _setTotalPitch(int32_t cents);
|
||||||
|
void _bringOutYourDead();
|
||||||
|
|
||||||
Voice* _allocateVoice(double sampleRate, bool dynamicPitch);
|
std::shared_ptr<Voice> _allocateVoice(double sampleRate, bool dynamicPitch);
|
||||||
std::list<Voice>::iterator _destroyVoice(Voice* voice);
|
std::list<std::shared_ptr<Voice>>::iterator _destroyVoice(Voice* voice);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Voice(Engine& engine, const AudioGroup& group, int vid, bool emitter, Submix* smx);
|
Voice(Engine& engine, const AudioGroup& group, int vid, bool emitter, Submix* smx);
|
||||||
|
@ -128,10 +138,10 @@ public:
|
||||||
int maxVid() const;
|
int maxVid() const;
|
||||||
|
|
||||||
/** Allocate parallel macro and tie to voice for possible emitter influence */
|
/** 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 */
|
/** 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,
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod,
|
||||||
bool pushPc=false);
|
bool pushPc=false);
|
||||||
|
|
||||||
|
@ -220,11 +230,11 @@ public:
|
||||||
void setKeygroup(uint8_t kg) {m_keygroup = kg;}
|
void setKeygroup(uint8_t kg) {m_keygroup = kg;}
|
||||||
|
|
||||||
uint8_t getLastNote() const {return m_state.m_initKey;}
|
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) {}
|
void setCtrlValue(uint8_t ctrl, int8_t val) {}
|
||||||
int8_t getPitchWheel() const {}
|
int8_t getPitchWheel() const {return 0;}
|
||||||
int8_t getModWheel() const {}
|
int8_t getModWheel() const {return 0;}
|
||||||
int8_t getAftertouch() const {}
|
int8_t getAftertouch() const {return 0;}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,20 +6,12 @@ namespace amuse
|
||||||
|
|
||||||
AudioGroup::AudioGroup(int groupId, const AudioGroupData& data)
|
AudioGroup::AudioGroup(int groupId, const AudioGroupData& data)
|
||||||
: m_groupId(groupId),
|
: m_groupId(groupId),
|
||||||
m_pool(data.getPool()),
|
|
||||||
m_proj(data.getProj()),
|
m_proj(data.getProj()),
|
||||||
|
m_pool(data.getPool()),
|
||||||
m_sdir(data.getSdir()),
|
m_sdir(data.getSdir()),
|
||||||
m_samp(data.getSamp())
|
m_samp(data.getSamp())
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool AudioGroup::sfxInGroup(int sfxId) const
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AudioGroup::songInGroup(int songId) const
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const Sample* AudioGroup::getSample(int sfxId) const
|
const Sample* AudioGroup::getSample(int sfxId) const
|
||||||
{
|
{
|
||||||
for (const auto& ent : m_sdir.m_entries)
|
for (const auto& ent : m_sdir.m_entries)
|
||||||
|
|
|
@ -91,9 +91,11 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data)
|
||||||
bIdx = &idx;
|
bIdx = &idx;
|
||||||
|
|
||||||
/* SFX entries */
|
/* SFX entries */
|
||||||
|
uint16_t count = SBig(*reinterpret_cast<const uint16_t*>(data + header.pageTableOff));
|
||||||
|
idx.m_sfxEntries.reserve(count);
|
||||||
const SFXGroupIndex::SFXEntry* entries =
|
const SFXGroupIndex::SFXEntry* entries =
|
||||||
reinterpret_cast<const SFXGroupIndex::SFXEntry*>(data + header.pageTableOff);
|
reinterpret_cast<const SFXGroupIndex::SFXEntry*>(data + header.pageTableOff + 4);
|
||||||
while (entries->objId != 0xffff)
|
for (int i=0 ; i<count ; ++i)
|
||||||
{
|
{
|
||||||
idx.m_sfxEntries[SBig(entries->defineId)] = entries;
|
idx.m_sfxEntries[SBig(entries->defineId)] = entries;
|
||||||
++entries;
|
++entries;
|
||||||
|
|
|
@ -6,7 +6,6 @@ namespace amuse
|
||||||
|
|
||||||
void AudioGroupSampleDirectory::Entry::swapBig()
|
void AudioGroupSampleDirectory::Entry::swapBig()
|
||||||
{
|
{
|
||||||
m_sfxId = SBig(m_sfxId);
|
|
||||||
m_sampleOff = SBig(m_sampleOff);
|
m_sampleOff = SBig(m_sampleOff);
|
||||||
m_unk = SBig(m_unk);
|
m_unk = SBig(m_unk);
|
||||||
m_sampleRate = SBig(m_sampleRate);
|
m_sampleRate = SBig(m_sampleRate);
|
||||||
|
@ -30,6 +29,27 @@ void AudioGroupSampleDirectory::ADPCMParms::swapBig()
|
||||||
|
|
||||||
AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data)
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,21 +54,21 @@ bool BooBackendSubmix::SubmixCallback::canApplyEffect() const
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendSubmix::SubmixCallback::applyEffect(int16_t* audio, size_t frameCount,
|
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,
|
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,
|
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)
|
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);
|
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)
|
BooBackendVoiceAllocator::BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine)
|
||||||
: m_booEngine(booEngine)
|
: m_booEngine(booEngine)
|
||||||
{}
|
{}
|
||||||
|
@ -110,4 +120,9 @@ AudioChannelSet BooBackendVoiceAllocator::getAvailableSet()
|
||||||
return AudioChannelSet(m_booEngine.getAvailableSet());
|
return AudioChannelSet(m_booEngine.getAvailableSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BooBackendVoiceAllocator::pumpAndMixVoices()
|
||||||
|
{
|
||||||
|
m_booEngine.pumpAndMixVoices();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,12 +139,16 @@ static const float rsmpTab12khz[] =
|
||||||
-0.000977, 0.101593, 0.802216, 0.097504,
|
-0.000977, 0.101593, 0.802216, 0.097504,
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
EffectChorus::EffectChorus(uint32_t baseDelay, uint32_t variation, uint32_t period)
|
||||||
EffectChorus<T>::EffectChorus(uint32_t baseDelay, uint32_t variation,
|
|
||||||
uint32_t period, double sampleRate)
|
|
||||||
: x90_baseDelay(clamp(5u, baseDelay, 15u)),
|
: x90_baseDelay(clamp(5u, baseDelay, 15u)),
|
||||||
x94_variation(clamp(0u, variation, 5u)),
|
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_sampsPerMs(std::ceil(sampleRate / 1000.0)),
|
||||||
m_blockSamples(m_sampsPerMs * 5)
|
m_blockSamples(m_sampsPerMs * 5)
|
||||||
{
|
{
|
||||||
|
@ -160,7 +164,7 @@ EffectChorus<T>::EffectChorus(uint32_t baseDelay, uint32_t variation,
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void EffectChorus<T>::_update()
|
void EffectChorusImp<T>::_update()
|
||||||
{
|
{
|
||||||
size_t chanPitch = m_blockSamples * AMUSE_CHORUS_NUM_BLOCKS;
|
size_t chanPitch = m_blockSamples * AMUSE_CHORUS_NUM_BLOCKS;
|
||||||
size_t fifteenSamps = 15 * m_sampsPerMs;
|
size_t fifteenSamps = 15 * m_sampsPerMs;
|
||||||
|
@ -178,13 +182,13 @@ void EffectChorus<T>::_update()
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
EffectChorus<T>::~EffectChorus()
|
EffectChorusImp<T>::~EffectChorusImp()
|
||||||
{
|
{
|
||||||
delete[] x0_lastChans[0][0];
|
delete[] x0_lastChans[0][0];
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
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 old1 = x74_old[0];
|
||||||
float old2 = x74_old[1];
|
float old2 = x74_old[1];
|
||||||
|
@ -225,7 +229,7 @@ void EffectChorus<T>::SrcInfo::doSrc1(size_t blockSamples, size_t chanCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
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 old1 = x74_old[0];
|
||||||
float old2 = x74_old[1];
|
float old2 = x74_old[1];
|
||||||
|
@ -284,7 +288,7 @@ void EffectChorus<T>::SrcInfo::doSrc2(size_t blockSamples, size_t chanCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
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)
|
if (m_dirty)
|
||||||
_update();
|
_update();
|
||||||
|
@ -356,8 +360,8 @@ void EffectChorus<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap&
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template class EffectChorus<int16_t>;
|
template class EffectChorusImp<int16_t>;
|
||||||
template class EffectChorus<int32_t>;
|
template class EffectChorusImp<int32_t>;
|
||||||
template class EffectChorus<float>;
|
template class EffectChorusImp<float>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ namespace amuse
|
||||||
{
|
{
|
||||||
|
|
||||||
template <typename T>
|
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)
|
uint32_t initOutput, double sampleRate)
|
||||||
: m_sampsPerMs(std::ceil(sampleRate / 1000.0)),
|
: m_sampsPerMs(std::ceil(sampleRate / 1000.0)),
|
||||||
m_blockSamples(m_sampsPerMs * 5)
|
m_blockSamples(m_sampsPerMs * 5)
|
||||||
{
|
{
|
||||||
|
@ -28,7 +28,7 @@ EffectDelay<T>::EffectDelay(uint32_t initDelay, uint32_t initFeedback,
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void EffectDelay<T>::_update()
|
void EffectDelayImp<T>::_update()
|
||||||
{
|
{
|
||||||
for (int i=0 ; i<8 ; ++i)
|
for (int i=0 ; i<8 ; ++i)
|
||||||
{
|
{
|
||||||
|
@ -38,14 +38,14 @@ void EffectDelay<T>::_update()
|
||||||
x24_currentOutput[i] = x54_output[i] * 128 / 100;
|
x24_currentOutput[i] = x54_output[i] * 128 / 100;
|
||||||
|
|
||||||
x30_chanLines[i].reset(new T[m_blockSamples * x0_currentSize[i]]);
|
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;
|
m_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
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)
|
if (m_dirty)
|
||||||
_update();
|
_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);
|
samp = ClampFull<T>(samp * x18_currentFeedback[c] / 128 + liveSamp);
|
||||||
liveSamp = samp * x24_currentOutput[c] / 128;
|
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;
|
audio += chanMap.m_channelCount * m_blockSamples;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template class EffectDelayImp<int16_t>;
|
||||||
|
template class EffectDelayImp<int32_t>;
|
||||||
|
template class EffectDelayImp<float>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,101 +53,112 @@ void ReverbDelayLine::setdelay(int32_t delay)
|
||||||
x4_outPoint += x8_length;
|
x4_outPoint += x8_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, size_t AP, size_t C>
|
EffectReverb::EffectReverb(float coloration, float mix, float time,
|
||||||
EffectReverb<T, AP, C>::EffectReverb(float coloration, float mix, float time,
|
float damping, float preDelay)
|
||||||
float damping, float preDelay, double sampleRate)
|
|
||||||
: x140_x1c8_coloration(clamp(0.f, coloration, 1.f)),
|
: x140_x1c8_coloration(clamp(0.f, coloration, 1.f)),
|
||||||
x144_x1cc_mix(clamp(0.f, mix, 1.f)),
|
x144_x1cc_mix(clamp(0.f, mix, 1.f)),
|
||||||
x148_x1d0_time(clamp(0.01f, time, 10.f)),
|
x148_x1d0_time(clamp(0.01f, time, 10.f)),
|
||||||
x14c_x1d4_damping(clamp(0.f, damping, 1.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)
|
m_sampleRate(sampleRate)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
template <typename T, size_t AP, size_t C>
|
template <typename T>
|
||||||
void EffectReverb<T, AP, C>::_update()
|
void EffectReverbStdImp<T>::_update()
|
||||||
{
|
{
|
||||||
float timeSamples = x148_x1d0_time * m_sampleRate;
|
float timeSamples = x148_x1d0_time * m_sampleRate;
|
||||||
for (int c=0 ; c<8 ; ++c)
|
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;
|
size_t tapDelay = CTapDelays[t] * m_sampleRate / 32000.0;
|
||||||
combLine.allocate(tapDelay);
|
combLine.allocate(tapDelay);
|
||||||
combLine.setdelay(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;
|
size_t tapDelay = APTapDelays[t] * m_sampleRate / 32000.0;
|
||||||
allPassLine.allocate(tapDelay);
|
allPassLine.allocate(tapDelay);
|
||||||
allPassLine.setdelay(tapDelay);
|
allPassLine.setdelay(tapDelay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
xf0_x168_allPassCoef = x140_x1c8_coloration;
|
xf0_allPassCoef = x140_x1c8_coloration;
|
||||||
x118_x19c_level = x144_x1cc_mix;
|
x118_level = x144_x1cc_mix;
|
||||||
x11c_x1a0_damping = x14c_x1d4_damping;
|
x11c_damping = x14c_x1d4_damping;
|
||||||
|
|
||||||
if (x11c_x1a0_damping < 0.05f)
|
if (x11c_damping < 0.05f)
|
||||||
x11c_x1a0_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)
|
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)
|
for (int i=0 ; i<8 ; ++i)
|
||||||
{
|
{
|
||||||
x124_x1ac_preDelayLine[i].reset(new float[x120_x1a4_preDelayTime]);
|
x124_preDelayLine[i].reset(new float[x120_preDelayTime]);
|
||||||
memset(x124_x1ac_preDelayLine[i].get(), 0, x120_x1a4_preDelayTime * sizeof(float));
|
memset(x124_preDelayLine[i].get(), 0, x120_preDelayTime * sizeof(float));
|
||||||
x130_x1b8_preDelayPtr[i] = x124_x1ac_preDelayLine[i].get();
|
x130_preDelayPtr[i] = x124_preDelayLine[i].get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
x120_x1a4_preDelayTime = 0;
|
x120_preDelayTime = 0;
|
||||||
for (int i=0 ; i<8 ; ++i)
|
for (int i=0 ; i<8 ; ++i)
|
||||||
{
|
{
|
||||||
x124_x1ac_preDelayLine[i] = nullptr;
|
x124_preDelayLine[i] = nullptr;
|
||||||
x130_x1b8_preDelayPtr[i] = nullptr;
|
x130_preDelayPtr[i] = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_dirty = false;
|
m_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, size_t AP, size_t C>
|
template <typename T>
|
||||||
void EffectReverb<T, AP, C>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)
|
void EffectReverbStdImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)
|
||||||
{
|
{
|
||||||
if (m_dirty)
|
if (m_dirty)
|
||||||
_update();
|
_update();
|
||||||
|
|
||||||
float dampWet = x118_x19c_level * 0.6f;
|
float dampWet = x118_level * 0.6f;
|
||||||
float dampDry = 0.6f - dampWet;
|
float dampDry = 0.6f - dampWet;
|
||||||
|
|
||||||
for (size_t f=0 ; f<frameCount ;)
|
for (size_t f=0 ; f<frameCount ;)
|
||||||
{
|
{
|
||||||
for (int c=0 ; c<chanMap.m_channelCount ; ++c)
|
for (int c=0 ; c<chanMap.m_channelCount ; ++c)
|
||||||
{
|
{
|
||||||
float* combCoefs = xf4_x16c_combCoef[c];
|
float* combCoefs = xf4_combCoef[c];
|
||||||
float& lpLastOut = x10c_x190_lpLastout[c];
|
float& lpLastOut = x10c_lpLastout[c];
|
||||||
float* preDelayLine = x124_x1ac_preDelayLine[c].get();
|
float* preDelayLine = x124_preDelayLine[c].get();
|
||||||
float* preDelayPtr = x130_x1b8_preDelayPtr[c];
|
float* preDelayPtr = x130_preDelayPtr[c];
|
||||||
float* lastPreDelaySamp = &preDelayLine[x120_x1a4_preDelayTime - 1];
|
float* lastPreDelaySamp = &preDelayLine[x120_preDelayTime - 1];
|
||||||
|
|
||||||
ReverbDelayLine* linesC = x78_xb4_C[c];
|
ReverbDelayLine* linesC = x78_C[c];
|
||||||
ReverbDelayLine* linesAP = x0_x0_AP[c];
|
ReverbDelayLine* linesAP = x0_AP[c];
|
||||||
|
|
||||||
float sample = audio[c];
|
float sample = audio[c];
|
||||||
for (int s=1 ; s<160 && f<frameCount ; ++s, ++f)
|
for (int s=1 ; s<160 && f<frameCount ; ++s, ++f)
|
||||||
{
|
{
|
||||||
/* Pre-delay stage */
|
/* Pre-delay stage */
|
||||||
float sample2 = sample;
|
float sample2 = sample;
|
||||||
if (x120_x1a4_preDelayTime != 0)
|
if (x120_preDelayTime != 0)
|
||||||
{
|
{
|
||||||
sample2 = *preDelayPtr;
|
sample2 = *preDelayPtr;
|
||||||
*preDelayPtr = sample;
|
*preDelayPtr = sample;
|
||||||
|
@ -183,8 +194,8 @@ void EffectReverb<T, AP, C>::applyEffect(T* audio, size_t frameCount, const Chan
|
||||||
|
|
||||||
/* All-pass filter stage */
|
/* All-pass filter stage */
|
||||||
linesAP[0].xc_inputs[linesAP[0].x0_inPoint] =
|
linesAP[0].xc_inputs[linesAP[0].x0_inPoint] =
|
||||||
xf0_x168_allPassCoef * linesAP[0].x10_lastInput + linesC[0].x10_lastInput + linesC[1].x10_lastInput;
|
xf0_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] -
|
float lowPass = -(xf0_allPassCoef * linesAP[0].xc_inputs[linesAP[0].x0_inPoint] -
|
||||||
linesAP[0].x10_lastInput);
|
linesAP[0].x10_lastInput);
|
||||||
linesAP[0].x0_inPoint += 4;
|
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)
|
if (linesAP[0].x4_outPoint == linesAP[0].x8_length)
|
||||||
linesAP[0].x4_outPoint = 0;
|
linesAP[0].x4_outPoint = 0;
|
||||||
|
|
||||||
lpLastOut = x11c_x1a0_damping * lpLastOut + lowPass * 0.3f;
|
lpLastOut = x11c_damping * lpLastOut + lowPass * 0.3f;
|
||||||
linesAP[1].xc_inputs[linesAP[1].x0_inPoint] = xf0_x168_allPassCoef * linesAP[1].x10_lastInput + lpLastOut;
|
linesAP[1].xc_inputs[linesAP[1].x0_inPoint] = xf0_allPassCoef * linesAP[1].x10_lastInput + lpLastOut;
|
||||||
float allPass = -(xf0_x168_allPassCoef * linesAP[1].xc_inputs[linesAP[1].x0_inPoint] -
|
float allPass = -(xf0_allPassCoef * linesAP[1].xc_inputs[linesAP[1].x0_inPoint] -
|
||||||
linesAP[1].x10_lastInput);
|
linesAP[1].x10_lastInput);
|
||||||
linesAP[1].x0_inPoint += 4;
|
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);
|
audio[(s-1) * chanMap.m_channelCount + c] = ClampFull<T>(dampWet * allPass + dampDry * sample);
|
||||||
sample = audio[s * chanMap.m_channelCount + c];
|
sample = audio[s * chanMap.m_channelCount + c];
|
||||||
}
|
}
|
||||||
x130_x1b8_preDelayPtr[c] = preDelayPtr;
|
x130_preDelayPtr[c] = preDelayPtr;
|
||||||
}
|
}
|
||||||
audio += chanMap.m_channelCount * 160;
|
audio += chanMap.m_channelCount * 160;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
EffectReverbStd<T>::EffectReverbStd(float coloration, float mix, float time,
|
EffectReverbHiImp<T>::EffectReverbHiImp(float coloration, float mix, float time,
|
||||||
float damping, float preDelay, double sampleRate)
|
float damping, float preDelay, float crosstalk,
|
||||||
: EffectReverb<T, 2, 2>(coloration, mix, time, damping, preDelay, sampleRate)
|
double sampleRate)
|
||||||
|
: EffectReverbHi(coloration, mix, time, damping, preDelay, crosstalk),
|
||||||
|
m_sampleRate(sampleRate)
|
||||||
{
|
{
|
||||||
EffectReverb<T, 2, 2>::_update();
|
_update();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
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 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];
|
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.allocate(tapDelay);
|
||||||
lpLine.setdelay(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>
|
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 dampDry = 0.6f - dampWet;
|
||||||
|
|
||||||
float* combCoefs = EffectReverb<T, 2, 3>::xf4_x16c_combCoef[c];
|
float* combCoefs = x16c_combCoef[c];
|
||||||
float& lpLastOut = EffectReverb<T, 2, 3>::x10c_x190_lpLastout[c];
|
float& lpLastOut = x190_lpLastout[c];
|
||||||
float* preDelayLine = EffectReverb<T, 2, 3>::x124_x1ac_preDelayLine[c].get();
|
float* preDelayLine = x1ac_preDelayLine[c].get();
|
||||||
float* preDelayPtr = EffectReverb<T, 2, 3>::x130_x1b8_preDelayPtr[c];
|
float* preDelayPtr = x1b8_preDelayPtr[c];
|
||||||
float* lastPreDelaySamp = &preDelayLine[EffectReverb<T, 2, 3>::x120_x1a4_preDelayTime - 1];
|
float* lastPreDelaySamp = &preDelayLine[x1a4_preDelayTime - 1];
|
||||||
|
|
||||||
ReverbDelayLine* linesC = EffectReverb<T, 2, 3>::x78_xb4_C[c];
|
ReverbDelayLine* linesC = xb4_C[c];
|
||||||
ReverbDelayLine* linesAP = EffectReverb<T, 2, 3>::x0_x0_AP[c];
|
ReverbDelayLine* linesAP = x0_AP[c];
|
||||||
ReverbDelayLine& lineLP = x78_LP[c];
|
ReverbDelayLine& lineLP = x78_LP[c];
|
||||||
|
|
||||||
float allPassCoef = EffectReverb<T, 2, 3>::xf0_x168_allPassCoef;
|
float allPassCoef = x168_allPassCoef;
|
||||||
float damping = EffectReverb<T, 2, 3>::x11c_x1a0_damping;
|
float damping = x1a0_damping;
|
||||||
int32_t preDelayTime = EffectReverb<T, 2, 3>::x120_x1a4_preDelayTime;
|
int32_t preDelayTime = x1a4_preDelayTime;
|
||||||
|
|
||||||
float sample = audio[c];
|
float sample = audio[c];
|
||||||
for (int s=1 ; s<sampleCount ; ++s)
|
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);
|
audio[(s-1) * chanCount + c] = ClampFull<T>(dampWet * allPass + dampDry * sample);
|
||||||
sample = audio[s * chanCount + c];
|
sample = audio[s * chanCount + c];
|
||||||
}
|
}
|
||||||
EffectReverb<T, 2, 3>::x130_x1b8_preDelayPtr[c] = preDelayPtr;
|
|
||||||
|
x1b8_preDelayPtr[c] = preDelayPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
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)
|
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>
|
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();
|
_update();
|
||||||
|
|
||||||
for (size_t f=0 ; f<frameCount ; f+=160)
|
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>
|
template class EffectReverbStdImp<int16_t>;
|
||||||
EffectReverbHi<T>::EffectReverbHi(float coloration, float mix, float time,
|
template class EffectReverbStdImp<int32_t>;
|
||||||
float damping, float preDelay, float crosstalk,
|
template class EffectReverbStdImp<float>;
|
||||||
double sampleRate)
|
|
||||||
: EffectReverb<T, 2, 3>(coloration, mix, time, damping, preDelay, sampleRate),
|
|
||||||
x1dc_crosstalk(clamp(0.f, crosstalk, 1.f))
|
|
||||||
{
|
|
||||||
_update();
|
|
||||||
}
|
|
||||||
|
|
||||||
template class EffectReverbStd<int16_t>;
|
template class EffectReverbHiImp<int16_t>;
|
||||||
template class EffectReverbStd<int32_t>;
|
template class EffectReverbHiImp<int32_t>;
|
||||||
template class EffectReverbStd<float>;
|
template class EffectReverbHiImp<float>;
|
||||||
|
|
||||||
template class EffectReverbHi<int16_t>;
|
|
||||||
template class EffectReverbHi<int32_t>;
|
|
||||||
template class EffectReverbHi<float>;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
107
lib/Engine.cpp
107
lib/Engine.cpp
|
@ -15,14 +15,14 @@ Engine::Engine(IBackendVoiceAllocator& backend)
|
||||||
: m_backend(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)
|
bool dynamicPitch, bool emitter, Submix* smx)
|
||||||
{
|
{
|
||||||
auto it = m_activeVoices.emplace(m_activeVoices.end(), *this, group, m_nextVid++, emitter, smx);
|
auto it = m_activeVoices.emplace(m_activeVoices.end(), new Voice(*this, group, m_nextVid++, emitter, smx));
|
||||||
m_activeVoices.back().m_backendVoice =
|
m_activeVoices.back()->m_backendVoice =
|
||||||
m_backend.allocateVoice(m_activeVoices.back(), sampleRate, dynamicPitch);
|
m_backend.allocateVoice(*m_activeVoices.back(), sampleRate, dynamicPitch);
|
||||||
m_activeVoices.back().m_engineIt = it;
|
m_activeVoices.back()->m_engineIt = it;
|
||||||
return &m_activeVoices.back();
|
return m_activeVoices.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
Submix* Engine::_allocateSubmix(Submix* smx)
|
Submix* Engine::_allocateSubmix(Submix* smx)
|
||||||
|
@ -33,7 +33,7 @@ Submix* Engine::_allocateSubmix(Submix* smx)
|
||||||
return &m_activeSubmixes.back();
|
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
|
#ifndef NDEBUG
|
||||||
assert(this == &voice->getEngine());
|
assert(this == &voice->getEngine());
|
||||||
|
@ -51,12 +51,31 @@ std::list<Submix>::iterator Engine::_destroySubmix(Submix* smx)
|
||||||
return m_activeSubmixes.erase(smx->m_engineIt);
|
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 */
|
/** Update all active audio entities and fill OS audio buffers as needed */
|
||||||
void Engine::pumpEngine()
|
void Engine::pumpEngine()
|
||||||
{
|
{
|
||||||
|
m_backend.pumpAndMixVoices();
|
||||||
|
_bringOutYourDead();
|
||||||
|
|
||||||
|
/* Determine lowest available free vid */
|
||||||
int maxVid = -1;
|
int maxVid = -1;
|
||||||
for (Voice& vox : m_activeVoices)
|
for (std::shared_ptr<Voice>& vox : m_activeVoices)
|
||||||
maxVid = std::max(maxVid, vox.maxVid());
|
maxVid = std::max(maxVid, vox->maxVid());
|
||||||
m_nextVid = maxVid + 1;
|
m_nextVid = maxVid + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,9 +111,10 @@ void Engine::removeAudioGroup(int groupId)
|
||||||
/* Destroy runtime entities within group */
|
/* Destroy runtime entities within group */
|
||||||
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ;)
|
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);
|
it = m_activeVoices.erase(it);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -103,9 +123,10 @@ void Engine::removeAudioGroup(int groupId)
|
||||||
|
|
||||||
for (auto it = m_activeEmitters.begin() ; it != m_activeEmitters.end() ;)
|
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);
|
it = m_activeEmitters.erase(it);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -114,9 +135,10 @@ void Engine::removeAudioGroup(int groupId)
|
||||||
|
|
||||||
for (auto it = m_activeSequencers.begin() ; it != m_activeSequencers.end() ;)
|
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);
|
it = m_activeSequencers.erase(it);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -146,10 +168,12 @@ void Engine::removeSubmix(Submix* smx)
|
||||||
/* Delete all voices bound to submix */
|
/* Delete all voices bound to submix */
|
||||||
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ;)
|
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)
|
if (vsmx && vsmx == smx)
|
||||||
{
|
{
|
||||||
it->_destroy();
|
vox->_destroy();
|
||||||
it = m_activeVoices.erase(it);
|
it = m_activeVoices.erase(it);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -174,7 +198,7 @@ void Engine::removeSubmix(Submix* smx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start soundFX playing from loaded audio groups */
|
/** 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);
|
auto search = m_sfxLookup.find(sfxId);
|
||||||
if (search == m_sfxLookup.end())
|
if (search == m_sfxLookup.end())
|
||||||
|
@ -184,15 +208,15 @@ Voice* Engine::fxStart(int sfxId, float vol, float pan, Submix* smx)
|
||||||
if (!grp)
|
if (!grp)
|
||||||
return nullptr;
|
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->setVolume(vol);
|
||||||
ret->setPan(pan);
|
ret->setPan(pan);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
|
/** 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)
|
float falloff, int sfxId, float minVol, float maxVol, Submix* smx)
|
||||||
{
|
{
|
||||||
auto search = m_sfxLookup.find(sfxId);
|
auto search = m_sfxLookup.find(sfxId);
|
||||||
if (search == m_sfxLookup.end())
|
if (search == m_sfxLookup.end())
|
||||||
|
@ -202,9 +226,9 @@ Emitter* Engine::addEmitter(const Vector3f& pos, const Vector3f& dir, float maxD
|
||||||
if (!grp)
|
if (!grp)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
Voice* vox = _allocateVoice(*grp, 32000.0, true, true, smx);
|
std::shared_ptr<Voice> vox = _allocateVoice(*grp, 32000.0, true, true, smx);
|
||||||
m_activeEmitters.emplace_back(*this, *grp, *vox);
|
m_activeEmitters.emplace(m_activeEmitters.end(), new Emitter(*this, *grp, *vox));
|
||||||
Emitter& ret = m_activeEmitters.back();
|
Emitter& ret = *m_activeEmitters.back();
|
||||||
ret.setPos(pos);
|
ret.setPos(pos);
|
||||||
ret.setDir(dir);
|
ret.setDir(dir);
|
||||||
ret.setMaxDist(maxDist);
|
ret.setMaxDist(maxDist);
|
||||||
|
@ -212,36 +236,38 @@ Emitter* Engine::addEmitter(const Vector3f& pos, const Vector3f& dir, float maxD
|
||||||
ret.setMinVol(minVol);
|
ret.setMinVol(minVol);
|
||||||
ret.setMaxVol(maxVol);
|
ret.setMaxVol(maxVol);
|
||||||
|
|
||||||
return &ret;
|
return m_activeEmitters.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start song playing from loaded audio groups */
|
/** 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 */
|
/** Find voice from VoiceId */
|
||||||
Voice* Engine::findVoice(int vid)
|
std::shared_ptr<Voice> Engine::findVoice(int vid)
|
||||||
{
|
{
|
||||||
for (Voice& vox : m_activeVoices)
|
for (std::shared_ptr<Voice>& vox : m_activeVoices)
|
||||||
if (vox.vid() == vid)
|
if (vox->vid() == vid)
|
||||||
return &vox;
|
return vox;
|
||||||
return nullptr;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `flag` set */
|
/** 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() ;)
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
it->keyOff();
|
vox->keyOff();
|
||||||
}
|
}
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
@ -251,8 +277,11 @@ void Engine::killKeygroup(uint8_t kg, uint8_t flag)
|
||||||
void Engine::sendMacroMessage(ObjectId macroId, int32_t val)
|
void Engine::sendMacroMessage(ObjectId macroId, int32_t val)
|
||||||
{
|
{
|
||||||
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ; ++it)
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ void SoundMacroState::Evaluator::addComponent(uint8_t midiCtrl, float scale,
|
||||||
m_comps.push_back({midiCtrl, scale, combine, varType});
|
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;
|
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);
|
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)
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod)
|
||||||
{
|
{
|
||||||
m_ticksPerSec = ticksPerSec;
|
m_ticksPerSec = ticksPerSec;
|
||||||
|
@ -138,7 +138,7 @@ void SoundMacroState::initialize(const unsigned char* ptr, int step, float ticks
|
||||||
m_header.swapBig();
|
m_header.swapBig();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SoundMacroState::advance(Voice& vox, float dt)
|
bool SoundMacroState::advance(Voice& vox, double dt)
|
||||||
{
|
{
|
||||||
/* Nothing if uninitialized or finished */
|
/* Nothing if uninitialized or finished */
|
||||||
if (m_pc.empty() || m_pc.back().first == nullptr || m_pc.back().second == -1)
|
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 priority = cmd.m_data[5];
|
||||||
//int8_t maxVoices = cmd.m_data[6];
|
//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)
|
if (sibVox)
|
||||||
m_lastPlayMacroVid = sibVox->vid();
|
m_lastPlayMacroVid = sibVox->vid();
|
||||||
|
|
||||||
|
@ -335,14 +335,14 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
{
|
{
|
||||||
if (m_lastPlayMacroVid != -1)
|
if (m_lastPlayMacroVid != -1)
|
||||||
{
|
{
|
||||||
Voice* otherVox = vox.getEngine().findVoice(m_lastPlayMacroVid);
|
std::shared_ptr<Voice> otherVox = vox.getEngine().findVoice(m_lastPlayMacroVid);
|
||||||
if (otherVox)
|
if (otherVox)
|
||||||
otherVox->keyOff();
|
otherVox->keyOff();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Voice* otherVox = vox.getEngine().findVoice(m_variables[vid]);
|
std::shared_ptr<Voice> otherVox = vox.getEngine().findVoice(m_variables[vid]);
|
||||||
if (otherVox)
|
if (otherVox)
|
||||||
otherVox->keyOff();
|
otherVox->keyOff();
|
||||||
}
|
}
|
||||||
|
@ -800,7 +800,7 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
|
|
||||||
if (isVar)
|
if (isVar)
|
||||||
{
|
{
|
||||||
Voice* findVox = vox.getEngine().findVoice(m_variables[vid]);
|
std::shared_ptr<Voice> findVox = vox.getEngine().findVoice(m_variables[vid]);
|
||||||
if (findVox)
|
if (findVox)
|
||||||
findVox->message(val);
|
findVox->message(val);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,19 +17,37 @@ Submix::Submix(Engine& engine, Submix* smx)
|
||||||
m_submix->m_activeSubmixes.insert(this);
|
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, size_t frameCount, const ChannelMap& chanMap) const
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Submix::applyEffect(float* audio, const ChannelMap& chanMap, double sampleRate) const
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
130
lib/Voice.cpp
130
lib/Voice.cpp
|
@ -90,17 +90,32 @@ void Voice::_setTotalPitch(int32_t cents)
|
||||||
m_backendVoice->setPitchRatio(ratio);
|
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,
|
for (auto it = m_childVoices.begin() ; it != m_childVoices.end() ;)
|
||||||
m_engine.m_nextVid++, m_emitter, m_submix);
|
{
|
||||||
m_childVoices.back().m_backendVoice =
|
Voice* vox = it->get();
|
||||||
m_engine.getBackend().allocateVoice(m_childVoices.back(), sampleRate, dynamicPitch);
|
vox->_bringOutYourDead();
|
||||||
m_childVoices.back().m_engineIt = it;
|
if (vox->m_dead)
|
||||||
return &m_childVoices.back();
|
{
|
||||||
|
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();
|
voice->_destroy();
|
||||||
return m_childVoices.erase(voice->m_engineIt);
|
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;
|
size_t samplesProc = 0;
|
||||||
if (m_curSample)
|
if (m_curSample)
|
||||||
{
|
{
|
||||||
|
m_dead = m_state.advance(*this, samples / m_sampleRate);
|
||||||
|
|
||||||
uint32_t block = m_curSamplePos / 14;
|
uint32_t block = m_curSamplePos / 14;
|
||||||
uint32_t rem = m_curSamplePos % 14;
|
uint32_t rem = m_curSamplePos % 14;
|
||||||
|
|
||||||
if (rem)
|
if (rem)
|
||||||
{
|
{
|
||||||
uint32_t decSamples = DSPDecompressFrameRanged(data, m_curSampleData + 8 * block,
|
uint32_t remCount = std::min(samplesRem, m_lastSamplePos - block * 14);
|
||||||
m_curSample->second.m_coefs,
|
uint32_t decSamples;
|
||||||
&m_prev1, &m_prev2, rem,
|
|
||||||
std::min(samplesRem,
|
switch (m_curFormat)
|
||||||
m_lastSamplePos - block * 14));
|
{
|
||||||
|
case SampleFormat::DSP:
|
||||||
|
{
|
||||||
|
decSamples = DSPDecompressFrameRanged(data, m_curSampleData + 8 * block,
|
||||||
|
m_curSample->second.m_coefs,
|
||||||
|
&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 */
|
/* Per-sample processing */
|
||||||
for (int i=0 ; i<decSamples ; ++i)
|
for (int i=0 ; i<decSamples ; ++i)
|
||||||
|
@ -265,11 +299,28 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
while (samplesRem)
|
while (samplesRem)
|
||||||
{
|
{
|
||||||
block = m_curSamplePos / 14;
|
block = m_curSamplePos / 14;
|
||||||
uint32_t decSamples = DSPDecompressFrame(data, m_curSampleData + 8 * block,
|
uint32_t remCount = std::min(samplesRem, m_lastSamplePos - block * 14);
|
||||||
m_curSample->second.m_coefs,
|
uint32_t decSamples;
|
||||||
&m_prev1, &m_prev2,
|
|
||||||
std::min(samplesRem,
|
switch (m_curFormat)
|
||||||
m_lastSamplePos - block * 14));
|
{
|
||||||
|
case SampleFormat::DSP:
|
||||||
|
{
|
||||||
|
decSamples = DSPDecompressFrame(data, m_curSampleData + 8 * block,
|
||||||
|
m_curSample->second.m_coefs,
|
||||||
|
&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 */
|
/* Per-sample processing */
|
||||||
for (int i=0 ; i<decSamples ; ++i)
|
for (int i=0 ; i<decSamples ; ++i)
|
||||||
|
@ -292,28 +343,33 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
m_dead = m_state.advance(*this, 0.0);
|
||||||
memset(data, 0, sizeof(int16_t) * samples);
|
memset(data, 0, sizeof(int16_t) * samples);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_dead)
|
||||||
|
m_voxState = VoiceState::Finished;
|
||||||
return samples;
|
return samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Voice::maxVid() const
|
int Voice::maxVid() const
|
||||||
{
|
{
|
||||||
int maxVid = m_vid;
|
int maxVid = m_vid;
|
||||||
for (const Voice& vox : m_childVoices)
|
for (const std::shared_ptr<Voice>& vox : m_childVoices)
|
||||||
maxVid = std::max(maxVid, vox.maxVid());
|
maxVid = std::max(maxVid, vox->maxVid());
|
||||||
return 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);
|
std::shared_ptr<Voice> vox = _allocateVoice(32000.0, true);
|
||||||
vox->loadSoundMacro(macroId, macroStep, 1000.f, m_state.m_initKey + addNote,
|
vox->loadSoundMacro(macroId, macroStep, 1000.0, m_state.m_initKey + addNote,
|
||||||
m_state.m_initVel, m_state.m_initMod);
|
m_state.m_initVel, m_state.m_initMod);
|
||||||
return vox;
|
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,
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod,
|
||||||
bool pushPc)
|
bool pushPc)
|
||||||
{
|
{
|
||||||
|
@ -332,6 +388,7 @@ bool Voice::loadSoundMacro(ObjectId macroId, int macroStep, float ticksPerSec,
|
||||||
m_state.m_header.swapBig();
|
m_state.m_header.swapBig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_voxState = VoiceState::Playing;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,6 +398,11 @@ void Voice::keyOff()
|
||||||
m_sustainKeyOff = true;
|
m_sustainKeyOff = true;
|
||||||
else
|
else
|
||||||
_doKeyOff();
|
_doKeyOff();
|
||||||
|
|
||||||
|
for (const std::shared_ptr<Voice>& vox : m_childVoices)
|
||||||
|
vox->keyOff();
|
||||||
|
|
||||||
|
m_voxState = VoiceState::KeyOff;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::message(int32_t val)
|
void Voice::message(int32_t val)
|
||||||
|
@ -358,18 +420,25 @@ void Voice::startSample(int16_t sampId, int32_t offset)
|
||||||
m_pitchDirty = true;
|
m_pitchDirty = true;
|
||||||
m_backendVoice->stop();
|
m_backendVoice->stop();
|
||||||
m_backendVoice->resetSampleRate(m_curSample->first.m_sampleRate);
|
m_backendVoice->resetSampleRate(m_curSample->first.m_sampleRate);
|
||||||
m_backendVoice->start();
|
|
||||||
m_curSamplePos = offset;
|
m_curSamplePos = offset;
|
||||||
m_curSampleData = m_audioGroup.getSampleData(m_curSample->first.m_sampleOff);
|
m_curSampleData = m_audioGroup.getSampleData(m_curSample->first.m_sampleOff);
|
||||||
m_prev1 = 0;
|
m_prev1 = 0;
|
||||||
m_prev2 = 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_lastSamplePos = m_curSample->first.m_loopLengthSamples ?
|
||||||
(m_curSample->first.m_loopStartSample + m_curSample->first.m_loopLengthSamples) :
|
(m_curSample->first.m_loopStartSample + m_curSample->first.m_loopLengthSamples) : numSamples;
|
||||||
m_curSample->first.m_numSamples;
|
|
||||||
|
if (fmt != SampleFormat::DSP && fmt != SampleFormat::PCM)
|
||||||
|
{
|
||||||
|
m_curSample = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_checkSamplePos();
|
_checkSamplePos();
|
||||||
|
|
||||||
/* Seek DSPADPCM state if needed */
|
/* Seek DSPADPCM state if needed */
|
||||||
if (m_curSamplePos)
|
if (m_curSamplePos && fmt == SampleFormat::DSP)
|
||||||
{
|
{
|
||||||
uint32_t block = m_curSamplePos / 14;
|
uint32_t block = m_curSamplePos / 14;
|
||||||
uint32_t rem = 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,
|
DSPDecompressFrameStateOnly(m_curSampleData + 8 * block, m_curSample->second.m_coefs,
|
||||||
&m_prev1, &m_prev2, rem);
|
&m_prev1, &m_prev2, rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_backendVoice->start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,6 +528,9 @@ void Voice::setPedal(bool pedal)
|
||||||
_doKeyOff();
|
_doKeyOff();
|
||||||
}
|
}
|
||||||
m_sustained = pedal;
|
m_sustained = pedal;
|
||||||
|
|
||||||
|
for (std::shared_ptr<Voice>& vox : m_childVoices)
|
||||||
|
vox->setPedal(pedal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setDoppler(float doppler)
|
void Voice::setDoppler(float doppler)
|
||||||
|
|
Loading…
Reference in New Issue