Refactors, bug fixes, lotsa things

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

View File

@ -61,6 +61,6 @@ add_library(amuse
${EXTRAS}) ${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()

View File

@ -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;
} }

View File

@ -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;

View File

@ -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();
}; };
} }

View File

@ -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;

View File

@ -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;
}
}; };
} }

View File

@ -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__

View File

@ -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__

View File

@ -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);

View File

@ -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;

View File

@ -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;
}; };
} }

View File

@ -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;
}; };
} }

View File

@ -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);

View File

@ -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;}
}; };

View File

@ -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;}
}; };

View File

@ -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)

View File

@ -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;

View File

@ -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;
}
} }
} }

View File

@ -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();
}
} }

View File

@ -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>;
} }

View File

@ -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>;
} }

View File

@ -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>;
} }

View File

@ -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);
}
} }
} }

View File

@ -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);
} }

View File

@ -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
{ {
} }

View File

@ -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)