mirror of https://github.com/AxioDL/amuse.git
Work on Sequencer
This commit is contained in:
parent
1382a1e946
commit
18f3ce6f44
|
@ -43,7 +43,7 @@ int main(int argc, char* argv[])
|
||||||
* A client-assigned integer serves as the handle to the group once loaded
|
* A client-assigned integer serves as the handle to the group once loaded
|
||||||
*/
|
*/
|
||||||
int groupId = 1;
|
int groupId = 1;
|
||||||
amuse::AudioGroupData data = LoadMyAudioGroup(groupId);
|
amuse::IntrusiveAudioGroupData data = LoadMyAudioGroup(groupId);
|
||||||
snd.addAudioGroup(groupId, data);
|
snd.addAudioGroup(groupId, data);
|
||||||
|
|
||||||
/* Starting a SoundMacro playing is accomplished like so: */
|
/* Starting a SoundMacro playing is accomplished like so: */
|
||||||
|
|
229
driver/main.cpp
229
driver/main.cpp
|
@ -130,6 +130,7 @@ struct AppCallback;
|
||||||
struct EventCallback : boo::IWindowCallback
|
struct EventCallback : boo::IWindowCallback
|
||||||
{
|
{
|
||||||
AppCallback& m_app;
|
AppCallback& m_app;
|
||||||
|
bool m_tracking = false;
|
||||||
public:
|
public:
|
||||||
void charKeyDown(unsigned long charCode, boo::EModifierKey mods, bool isRepeat);
|
void charKeyDown(unsigned long charCode, boo::EModifierKey mods, bool isRepeat);
|
||||||
void charKeyUp(unsigned long charCode, boo::EModifierKey mods);
|
void charKeyUp(unsigned long charCode, boo::EModifierKey mods);
|
||||||
|
@ -137,6 +138,18 @@ public:
|
||||||
void specialKeyUp(boo::ESpecialKey key, boo::EModifierKey mods);
|
void specialKeyUp(boo::ESpecialKey key, boo::EModifierKey mods);
|
||||||
void resized(const boo::SWindowRect&, const boo::SWindowRect&) {}
|
void resized(const boo::SWindowRect&, const boo::SWindowRect&) {}
|
||||||
|
|
||||||
|
void mouseDown(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey)
|
||||||
|
{
|
||||||
|
m_tracking = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey)
|
||||||
|
{
|
||||||
|
m_tracking = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mouseMove(const boo::SWindowCoord& coord);
|
||||||
|
|
||||||
EventCallback(AppCallback& app) : m_app(app) {}
|
EventCallback(AppCallback& app) : m_app(app) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -158,18 +171,44 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
/* Song playback selection */
|
/* Song playback selection */
|
||||||
int m_setupId = -1;
|
int m_setupId = -1;
|
||||||
int m_chanId = -1;
|
int m_chanId = -1;
|
||||||
|
int8_t m_octave = 4;
|
||||||
|
int8_t m_velocity = 64;
|
||||||
|
std::shared_ptr<amuse::Sequencer> m_seq;
|
||||||
|
|
||||||
/* SFX playback selection */
|
/* SFX playback selection */
|
||||||
int m_sfxId = -1;
|
int m_sfxId = -1;
|
||||||
std::shared_ptr<amuse::Voice> m_vox;
|
std::shared_ptr<amuse::Voice> m_vox;
|
||||||
|
size_t m_lastVoxCount = 0;
|
||||||
|
|
||||||
/* Control state */
|
/* Control state */
|
||||||
float m_volume = 1.f;
|
float m_volume = 1.f;
|
||||||
|
float m_modulation = 0.f;
|
||||||
|
float m_pitchBend = 0.f;
|
||||||
bool m_updateDisp = false;
|
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;
|
||||||
|
|
||||||
|
void UpdateSongDisplay()
|
||||||
|
{
|
||||||
|
size_t voxCount = 0;
|
||||||
|
if (m_seq)
|
||||||
|
voxCount = m_seq->getVoiceCount();
|
||||||
|
printf("\r "
|
||||||
|
"\r %" PRISize " Setup %d, Chan %d, VOL: %d%%\r", voxCount,
|
||||||
|
m_setupId, m_chanId, int(std::rint(m_volume * 100)));
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SelectSong(int setupId)
|
||||||
|
{
|
||||||
|
m_setupId = setupId;
|
||||||
|
if (m_seq)
|
||||||
|
m_seq->allOff();
|
||||||
|
m_seq = m_engine->seqPlay(m_groupId, setupId, nullptr);
|
||||||
|
UpdateSongDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
void SongLoop(const amuse::AudioGroup& group,
|
void SongLoop(const amuse::AudioGroup& group,
|
||||||
const amuse::SongGroupIndex& index)
|
const amuse::SongGroupIndex& index)
|
||||||
{
|
{
|
||||||
|
@ -200,10 +239,42 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
if (m_wantsNext)
|
if (m_wantsNext)
|
||||||
{
|
{
|
||||||
m_wantsNext = false;
|
m_wantsNext = false;
|
||||||
|
auto nextIt = setupIt;
|
||||||
|
++nextIt;
|
||||||
|
if (nextIt != sortEntries.cend())
|
||||||
|
{
|
||||||
|
++setupIt;
|
||||||
|
SelectSong(setupIt->first);
|
||||||
|
m_updateDisp = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_wantsPrev)
|
||||||
|
{
|
||||||
|
m_wantsPrev = false;
|
||||||
|
if (setupIt != sortEntries.cbegin())
|
||||||
|
{
|
||||||
|
--setupIt;
|
||||||
|
SelectSong(setupIt->first);
|
||||||
|
m_updateDisp = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_updateDisp)
|
||||||
|
{
|
||||||
|
m_updateDisp = false;
|
||||||
|
UpdateSongDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_engine->pumpEngine();
|
m_engine->pumpEngine();
|
||||||
|
|
||||||
|
size_t voxCount = m_seq->getVoiceCount();
|
||||||
|
if (m_lastVoxCount != voxCount)
|
||||||
|
{
|
||||||
|
m_lastVoxCount = voxCount;
|
||||||
|
UpdateSongDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
m_win->waitForRetrace();
|
m_win->waitForRetrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -282,7 +353,8 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
|
|
||||||
void charKeyDown(unsigned long charCode)
|
void charKeyDown(unsigned long charCode)
|
||||||
{
|
{
|
||||||
if (charCode == 'q' || charCode == 'Q')
|
charCode = tolower(charCode);
|
||||||
|
if (charCode == 'q')
|
||||||
{
|
{
|
||||||
m_running = false;
|
m_running = false;
|
||||||
return;
|
return;
|
||||||
|
@ -301,13 +373,148 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if (m_seq && m_chanId != -1)
|
||||||
{
|
{
|
||||||
|
switch (charCode)
|
||||||
|
{
|
||||||
|
case 'z':
|
||||||
|
m_octave = amuse::clamp(-1, m_octave - 1, 8);
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
m_octave = amuse::clamp(-1, m_octave + 1, 8);
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
m_velocity = amuse::clamp(0, m_velocity - 1, 127);
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
m_velocity = amuse::clamp(0, m_velocity + 1, 127);
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
m_seq->setCtrlValue(m_chanId, 64, 127);
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
m_seq->keyOn(m_chanId, (m_octave + 1) * 12, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
m_seq->keyOn(m_chanId, (m_octave + 1) * 12 + 1, m_velocity);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
m_seq->keyOn(m_chanId, (m_octave + 1) * 12 + 2, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
m_seq->keyOn(m_chanId, (m_octave + 1) * 12 + 3, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
m_seq->keyOn(m_chanId, (m_octave + 1) * 12 + 4, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
m_seq->keyOn(m_chanId, (m_octave + 1) * 12 + 5, m_velocity);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
m_seq->keyOn(m_chanId, (m_octave + 1) * 12 + 6, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
m_seq->keyOn(m_chanId, (m_octave + 1) * 12 + 7, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'y':
|
||||||
|
m_seq->keyOn(m_chanId, (m_octave + 1) * 12 + 8, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
m_seq->keyOn(m_chanId, (m_octave + 1) * 12 + 9, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
m_seq->keyOn(m_chanId, (m_octave + 1) * 12 + 10, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
m_seq->keyOn(m_chanId, (m_octave + 1) * 12 + 11, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'k':
|
||||||
|
m_seq->keyOn(m_chanId, (m_octave + 1) * 12 + 12, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
m_seq->keyOn(m_chanId, (m_octave + 1) * 12 + 13, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
m_seq->keyOn(m_chanId, (m_octave + 1) * 12 + 14, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
m_seq->keyOn(m_chanId, (m_octave + 1) * 12 + 15, m_velocity);
|
||||||
|
break;
|
||||||
|
case ';':
|
||||||
|
case ':':
|
||||||
|
m_seq->keyOn(m_chanId, (m_octave + 1) * 12 + 16, m_velocity);
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void charKeyUp(unsigned long charCode)
|
void charKeyUp(unsigned long charCode)
|
||||||
{
|
{
|
||||||
|
charCode = tolower(charCode);
|
||||||
|
|
||||||
|
if (!m_sfxGroup && m_chanId != -1)
|
||||||
|
{
|
||||||
|
switch (charCode)
|
||||||
|
{
|
||||||
|
case '\t':
|
||||||
|
m_seq->setCtrlValue(m_chanId, 64, 0);
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
m_seq->keyOff(m_chanId, (m_octave + 1) * 12, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
m_seq->keyOff(m_chanId, (m_octave + 1) * 12 + 1, m_velocity);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
m_seq->keyOff(m_chanId, (m_octave + 1) * 12 + 2, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
m_seq->keyOff(m_chanId, (m_octave + 1) * 12 + 3, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
m_seq->keyOff(m_chanId, (m_octave + 1) * 12 + 4, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
m_seq->keyOff(m_chanId, (m_octave + 1) * 12 + 5, m_velocity);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
m_seq->keyOff(m_chanId, (m_octave + 1) * 12 + 6, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
m_seq->keyOff(m_chanId, (m_octave + 1) * 12 + 7, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'y':
|
||||||
|
m_seq->keyOff(m_chanId, (m_octave + 1) * 12 + 8, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
m_seq->keyOff(m_chanId, (m_octave + 1) * 12 + 9, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
m_seq->keyOff(m_chanId, (m_octave + 1) * 12 + 10, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
m_seq->keyOff(m_chanId, (m_octave + 1) * 12 + 11, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'k':
|
||||||
|
m_seq->keyOff(m_chanId, (m_octave + 1) * 12 + 12, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
m_seq->keyOff(m_chanId, (m_octave + 1) * 12 + 13, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
m_seq->keyOff(m_chanId, (m_octave + 1) * 12 + 14, m_velocity);
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
m_seq->keyOff(m_chanId, (m_octave + 1) * 12 + 15, m_velocity);
|
||||||
|
break;
|
||||||
|
case ';':
|
||||||
|
case ':':
|
||||||
|
m_seq->keyOff(m_chanId, (m_octave + 1) * 12 + 16, m_velocity);
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int appMain(boo::IApplication* app)
|
int appMain(boo::IApplication* app)
|
||||||
|
@ -469,6 +676,24 @@ void EventCallback::specialKeyUp(boo::ESpecialKey key, boo::EModifierKey mods)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EventCallback::mouseMove(const boo::SWindowCoord& coord)
|
||||||
|
{
|
||||||
|
if (m_tracking)
|
||||||
|
{
|
||||||
|
m_app.m_modulation = amuse::clamp(0.f, coord.norm[0], 1.f);
|
||||||
|
if (m_app.m_vox)
|
||||||
|
m_app.m_vox->setCtrlValue(1, m_app.m_modulation * 127.f);
|
||||||
|
if (m_app.m_seq && m_app.m_chanId != -1)
|
||||||
|
m_app.m_seq->setCtrlValue(m_app.m_chanId, 1, m_app.m_modulation * 127.f);
|
||||||
|
|
||||||
|
m_app.m_pitchBend = amuse::clamp(-1.f, coord.norm[1] * 2.f - 1.f, 1.f);
|
||||||
|
if (m_app.m_vox)
|
||||||
|
m_app.m_vox->setPitchWheel(m_app.m_pitchBend);
|
||||||
|
if (m_app.m_seq && m_app.m_chanId != -1)
|
||||||
|
m_app.m_seq->setPitchWheel(m_app.m_chanId, m_app.m_pitchBend);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
int wmain(int argc, const boo::SystemChar** argv)
|
int wmain(int argc, const boo::SystemChar** argv)
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -44,6 +44,7 @@ public:
|
||||||
x48_feedback[i] = feedback;
|
x48_feedback[i] = feedback;
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setChanFeedback(int chanIdx, uint32_t feedback)
|
void setChanFeedback(int chanIdx, uint32_t feedback)
|
||||||
{
|
{
|
||||||
feedback = clamp(0u, feedback, 100u);
|
feedback = clamp(0u, feedback, 100u);
|
||||||
|
@ -66,7 +67,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Type-specific implementation of chorus effect */
|
/** Type-specific implementation of delay effect */
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class EffectDelayImp : public EffectBase<T>, public EffectDelay
|
class EffectDelayImp : public EffectBase<T>, public EffectDelay
|
||||||
{
|
{
|
||||||
|
|
|
@ -65,6 +65,7 @@ public:
|
||||||
x148_x1d0_time = clamp(0.01f, time, 10.f);
|
x148_x1d0_time = clamp(0.01f, time, 10.f);
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDamping(float damping)
|
void setDamping(float damping)
|
||||||
{
|
{
|
||||||
x14c_x1d4_damping = clamp(0.f, damping, 1.f);
|
x14c_x1d4_damping = clamp(0.f, damping, 1.f);
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include "Emitter.hpp"
|
#include "Emitter.hpp"
|
||||||
#include "AudioGroupSampleDirectory.hpp"
|
#include "AudioGroupSampleDirectory.hpp"
|
||||||
|
#include "Sequencer.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
|
@ -15,7 +16,6 @@ class IBackendVoiceAllocator;
|
||||||
class Voice;
|
class Voice;
|
||||||
class Submix;
|
class Submix;
|
||||||
class Emitter;
|
class Emitter;
|
||||||
class Sequencer;
|
|
||||||
class AudioGroup;
|
class AudioGroup;
|
||||||
class AudioGroupData;
|
class AudioGroupData;
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ class Engine
|
||||||
{
|
{
|
||||||
friend class Voice;
|
friend class Voice;
|
||||||
friend class Emitter;
|
friend class Emitter;
|
||||||
|
friend class Sequencer::ChannelState;
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
@ -2,14 +2,67 @@
|
||||||
#define __AMUSE_SEQUENCER_HPP__
|
#define __AMUSE_SEQUENCER_HPP__
|
||||||
|
|
||||||
#include "Entity.hpp"
|
#include "Entity.hpp"
|
||||||
|
#include "AudioGroupProject.hpp"
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
|
class Submix;
|
||||||
|
class Voice;
|
||||||
|
|
||||||
class Sequencer : public Entity
|
class Sequencer : public Entity
|
||||||
{
|
{
|
||||||
|
friend class Engine;
|
||||||
|
const SongGroupIndex& m_songGroup; /**< Quick access to song group project index */
|
||||||
|
const SongGroupIndex::MIDISetup* m_midiSetup = nullptr; /**< Selected MIDI setup */
|
||||||
|
Submix* m_submix = nullptr; /**< Submix this sequencer outputs to (or NULL for the main output mix) */
|
||||||
|
|
||||||
|
/** State of a single MIDI channel */
|
||||||
|
struct ChannelState
|
||||||
|
{
|
||||||
|
Sequencer& m_parent;
|
||||||
|
const SongGroupIndex::MIDISetup& m_setup;
|
||||||
|
const SongGroupIndex::PageEntry* m_page = nullptr;
|
||||||
|
Submix* m_submix = nullptr;
|
||||||
|
~ChannelState();
|
||||||
|
ChannelState(Sequencer& parent, uint8_t chanId);
|
||||||
|
|
||||||
|
/** Voices corresponding to currently-pressed keys in channel */
|
||||||
|
std::unordered_map<uint8_t, std::shared_ptr<Voice>> m_chanVoxs;
|
||||||
|
int8_t m_ctrlVals[128]; /**< MIDI controller values */
|
||||||
|
|
||||||
|
std::shared_ptr<Voice> keyOn(uint8_t note, uint8_t velocity);
|
||||||
|
void keyOff(uint8_t note, uint8_t velocity);
|
||||||
|
void setCtrlValue(uint8_t ctrl, int8_t val);
|
||||||
|
void setPitchWheel(float pitchWheel);
|
||||||
|
void allOff();
|
||||||
|
};
|
||||||
|
std::unordered_map<uint8_t, ChannelState> m_chanStates; /**< Lazily-allocated channel states */
|
||||||
|
|
||||||
|
void _destroy();
|
||||||
public:
|
public:
|
||||||
~Sequencer();
|
~Sequencer();
|
||||||
|
Sequencer(Engine& engine, const AudioGroup& group,
|
||||||
|
const SongGroupIndex& songGroup, int setupId, Submix* smx);
|
||||||
|
|
||||||
|
/** Get number of active voices */
|
||||||
|
size_t getVoiceCount() const;
|
||||||
|
|
||||||
|
/** Register key press with voice set */
|
||||||
|
std::shared_ptr<Voice> keyOn(uint8_t chan, uint8_t note, uint8_t velocity);
|
||||||
|
|
||||||
|
/** Register key release with voice set */
|
||||||
|
void keyOff(uint8_t chan, uint8_t note, uint8_t velocity);
|
||||||
|
|
||||||
|
/** Set MIDI control value [0,127] for all voices */
|
||||||
|
void setCtrlValue(uint8_t chan, uint8_t ctrl, int8_t val);
|
||||||
|
|
||||||
|
/** Set pitchwheel value for use with voice controllers */
|
||||||
|
void setPitchWheel(uint8_t chan, float pitchWheel);
|
||||||
|
|
||||||
|
/** Send keyoffs to all active notes, silence immediately if `now` set */
|
||||||
|
void allOff(bool now=false);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,17 +14,20 @@
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
class IBackendSubmix;
|
class IBackendSubmix;
|
||||||
|
class Sequencer;
|
||||||
|
|
||||||
/** Intermediate mix of voices for applying auxiliary effects */
|
/** Intermediate mix of voices for applying auxiliary effects */
|
||||||
class Submix
|
class Submix
|
||||||
{
|
{
|
||||||
friend class Engine;
|
friend class Engine;
|
||||||
friend class Voice;
|
friend class Voice;
|
||||||
|
friend class Sequencer;
|
||||||
Engine& m_root;
|
Engine& m_root;
|
||||||
Submix* m_submix = nullptr; /**< Parent submix of this submix (or NULL if mixing to main output) */
|
Submix* m_submix = nullptr; /**< Parent submix of this submix (or NULL if mixing to main output) */
|
||||||
std::list<Submix>::iterator m_engineIt; /**< Iterator to self within Engine's list for quick deletion */
|
std::list<Submix>::iterator m_engineIt; /**< Iterator to self within Engine's list for quick deletion */
|
||||||
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<Sequencer*> m_activeSequencers; /**< Secondary index of Sequencers 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 */
|
std::vector<std::unique_ptr<EffectBaseTypeless>> m_effectStack; /**< Ordered list of effects to apply to submix */
|
||||||
bool m_destroyed = false;
|
bool m_destroyed = false;
|
||||||
|
|
|
@ -15,6 +15,8 @@ namespace amuse
|
||||||
{
|
{
|
||||||
class IBackendVoice;
|
class IBackendVoice;
|
||||||
class Submix;
|
class Submix;
|
||||||
|
struct Keymap;
|
||||||
|
struct LayerMapping;
|
||||||
|
|
||||||
/** State of voice over lifetime */
|
/** State of voice over lifetime */
|
||||||
enum class VoiceState
|
enum class VoiceState
|
||||||
|
@ -28,6 +30,7 @@ enum class VoiceState
|
||||||
class Voice : public Entity
|
class Voice : public Entity
|
||||||
{
|
{
|
||||||
friend class Engine;
|
friend class Engine;
|
||||||
|
friend class Sequencer;
|
||||||
friend class SoundMacroState;
|
friend class SoundMacroState;
|
||||||
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 */
|
||||||
|
@ -58,11 +61,16 @@ class Voice : public Entity
|
||||||
VoiceState m_voxState = VoiceState::Dead; /**< Current high-level state of voice */
|
VoiceState m_voxState = VoiceState::Dead; /**< Current high-level state of voice */
|
||||||
bool m_sustained = false; /**< Sustain pedal pressed for this voice */
|
bool m_sustained = false; /**< Sustain pedal pressed for this voice */
|
||||||
bool m_sustainKeyOff = false; /**< Keyoff event occured while sustained */
|
bool m_sustainKeyOff = false; /**< Keyoff event occured while sustained */
|
||||||
|
uint8_t m_curAftertouch = 0; /**< Aftertouch value (key pressure when 'bottoming out') */
|
||||||
|
|
||||||
float m_curVol; /**< Current volume of voice */
|
float m_curVol; /**< Current volume of voice */
|
||||||
float m_curReverbVol; /**< Current reverb volume of voice */
|
float m_curReverbVol; /**< Current reverb volume of voice */
|
||||||
float m_curPan; /**< Current pan of voice */
|
float m_curPan; /**< Current pan of voice */
|
||||||
float m_curSpan; /**< Current surround pan of voice */
|
float m_curSpan; /**< Current surround pan of voice */
|
||||||
|
float m_curPitchWheel; /**< Current normalized wheel value for control */
|
||||||
|
int32_t m_pitchWheelUp; /**< Up range for pitchwheel control in cents */
|
||||||
|
int32_t m_pitchWheelDown; /**< Down range for pitchwheel control in cents */
|
||||||
|
int32_t m_pitchWheelVal; /**< Current resolved pitchwheel delta for control */
|
||||||
int32_t m_curPitch; /**< Current base pitch in cents */
|
int32_t m_curPitch; /**< Current base pitch in cents */
|
||||||
bool m_pitchDirty; /**< m_curPitch has been updated and needs sending to voice */
|
bool m_pitchDirty; /**< m_curPitch has been updated and needs sending to voice */
|
||||||
|
|
||||||
|
@ -103,6 +111,7 @@ class Voice : public Entity
|
||||||
float m_tremoloModScale; /**< minimum volume factor produced via LFO, scaled via mod wheel */
|
float m_tremoloModScale; /**< minimum volume factor produced via LFO, scaled via mod wheel */
|
||||||
|
|
||||||
float m_lfoPeriods[2]; /**< time-periods for LFO1 and LFO2 */
|
float m_lfoPeriods[2]; /**< time-periods for LFO1 and LFO2 */
|
||||||
|
int8_t* m_ctrlVals = nullptr; /**< MIDI Controller values (external storage) */
|
||||||
|
|
||||||
void _destroy();
|
void _destroy();
|
||||||
void _reset();
|
void _reset();
|
||||||
|
@ -110,12 +119,21 @@ class Voice : public Entity
|
||||||
void _doKeyOff();
|
void _doKeyOff();
|
||||||
bool _advanceSample(int16_t& samp);
|
bool _advanceSample(int16_t& samp);
|
||||||
void _setTotalPitch(int32_t cents);
|
void _setTotalPitch(int32_t cents);
|
||||||
|
bool _isRecursivelyDead();
|
||||||
void _bringOutYourDead();
|
void _bringOutYourDead();
|
||||||
std::shared_ptr<Voice> _findVoice(int vid, std::weak_ptr<Voice> thisPtr);
|
std::shared_ptr<Voice> _findVoice(int vid, std::weak_ptr<Voice> thisPtr);
|
||||||
|
|
||||||
std::shared_ptr<Voice> _allocateVoice(double sampleRate, bool dynamicPitch);
|
std::shared_ptr<Voice> _allocateVoice(double sampleRate, bool dynamicPitch);
|
||||||
std::list<std::shared_ptr<Voice>>::iterator _destroyVoice(Voice* voice);
|
std::list<std::shared_ptr<Voice>>::iterator _destroyVoice(Voice* voice);
|
||||||
|
|
||||||
|
bool _loadSoundMacro(const unsigned char* macroData, int macroStep, double ticksPerSec,
|
||||||
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc=false);
|
||||||
|
bool _loadKeymap(const Keymap* keymap, int macroStep, double ticksPerSec,
|
||||||
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc=false);
|
||||||
|
bool _loadLayer(const std::vector<const LayerMapping*>& layer, int macroStep, double ticksPerSec,
|
||||||
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc=false);
|
||||||
|
std::shared_ptr<Voice> _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec,
|
||||||
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc=false);
|
||||||
public:
|
public:
|
||||||
~Voice();
|
~Voice();
|
||||||
Voice(Engine& engine, const AudioGroup& group, int vid, bool emitter, Submix* smx);
|
Voice(Engine& engine, const AudioGroup& group, int vid, bool emitter, Submix* smx);
|
||||||
|
@ -140,10 +158,10 @@ public:
|
||||||
/** Allocate parallel macro and tie to voice for possible emitter influence */
|
/** Allocate parallel macro and tie to voice for possible emitter influence */
|
||||||
std::shared_ptr<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 Sound Object from within group into voice */
|
||||||
bool loadSoundMacro(ObjectId macroId, int macroStep, double ticksPerSec,
|
bool loadSoundObject(ObjectId objectId, 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);
|
||||||
|
|
||||||
/** Signals voice to begin fade-out (or defer if sustained), eventually reaching silence */
|
/** Signals voice to begin fade-out (or defer if sustained), eventually reaching silence */
|
||||||
void keyOff();
|
void keyOff();
|
||||||
|
@ -181,9 +199,6 @@ public:
|
||||||
/** Set voice relative-pitch in cents */
|
/** Set voice relative-pitch in cents */
|
||||||
void setPitchKey(int32_t cents);
|
void setPitchKey(int32_t cents);
|
||||||
|
|
||||||
/** Set voice modulation */
|
|
||||||
void setModulation(float mod);
|
|
||||||
|
|
||||||
/** Set sustain status within voice; clearing will trigger a deferred keyoff */
|
/** Set sustain status within voice; clearing will trigger a deferred keyoff */
|
||||||
void setPedal(bool pedal);
|
void setPedal(bool pedal);
|
||||||
|
|
||||||
|
@ -223,18 +238,41 @@ public:
|
||||||
/** Set pitch envelope */
|
/** Set pitch envelope */
|
||||||
void setPitchAdsr(ObjectId adsrId, int32_t cents);
|
void setPitchAdsr(ObjectId adsrId, int32_t cents);
|
||||||
|
|
||||||
|
/** Set pitchwheel value for use with controller */
|
||||||
|
void setPitchWheel(float pitchWheel);
|
||||||
|
|
||||||
/** Set effective pitch range via the pitchwheel controller */
|
/** Set effective pitch range via the pitchwheel controller */
|
||||||
void setPitchWheelRange(int8_t up, int8_t down);
|
void setPitchWheelRange(int8_t up, int8_t down);
|
||||||
|
|
||||||
|
/** Set aftertouch */
|
||||||
|
void setAftertouch(uint8_t aftertouch);
|
||||||
|
|
||||||
/** Assign voice to keygroup for coordinated mass-silencing */
|
/** Assign voice to keygroup for coordinated mass-silencing */
|
||||||
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 {return 0;}
|
int8_t getCtrlValue(uint8_t ctrl) const
|
||||||
void setCtrlValue(uint8_t ctrl, int8_t val) {}
|
{
|
||||||
int8_t getPitchWheel() const {return 0;}
|
if (!m_ctrlVals)
|
||||||
int8_t getModWheel() const {return 0;}
|
return 0;
|
||||||
int8_t getAftertouch() const {return 0;}
|
return m_ctrlVals[ctrl];
|
||||||
|
}
|
||||||
|
void setCtrlValue(uint8_t ctrl, int8_t val)
|
||||||
|
{
|
||||||
|
if (!m_ctrlVals)
|
||||||
|
return;
|
||||||
|
m_ctrlVals[ctrl] = val;
|
||||||
|
}
|
||||||
|
int8_t getModWheel() const
|
||||||
|
{
|
||||||
|
if (!m_ctrlVals)
|
||||||
|
return 0;
|
||||||
|
return m_ctrlVals[1];
|
||||||
|
}
|
||||||
|
void installCtrlValues(int8_t* cvs) {m_ctrlVals = cvs;}
|
||||||
|
int8_t getPitchWheel() const {return m_curPitchWheel * 127;}
|
||||||
|
int8_t getAftertouch() const {return m_curAftertouch;}
|
||||||
|
size_t getTotalVoices() const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -223,7 +223,7 @@ std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, Submix*
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
std::shared_ptr<Voice> ret = _allocateVoice(*grp, 32000.0, true, false, smx);
|
std::shared_ptr<Voice> ret = _allocateVoice(*grp, 32000.0, true, false, smx);
|
||||||
if (!ret->loadSoundMacro(search->second.second, 0, 1000.f, 0x3c, 0, 0))
|
if (!ret->loadSoundObject(search->second.second, 0, 1000.f, 0x3c, 0, 0))
|
||||||
{
|
{
|
||||||
_destroyVoice(ret.get());
|
_destroyVoice(ret.get());
|
||||||
return {};
|
return {};
|
||||||
|
@ -248,7 +248,7 @@ std::shared_ptr<Emitter> Engine::addEmitter(const Vector3f& pos, const Vector3f&
|
||||||
std::shared_ptr<Voice> vox = _allocateVoice(*grp, 32000.0, true, true, smx);
|
std::shared_ptr<Voice> vox = _allocateVoice(*grp, 32000.0, true, true, smx);
|
||||||
m_activeEmitters.emplace(m_activeEmitters.end(), new Emitter(*this, *grp, std::move(vox)));
|
m_activeEmitters.emplace(m_activeEmitters.end(), new Emitter(*this, *grp, std::move(vox)));
|
||||||
Emitter& ret = *m_activeEmitters.back();
|
Emitter& ret = *m_activeEmitters.back();
|
||||||
if (!ret.getVoice()->loadSoundMacro(search->second.second, 0, 1000.f, 0x3c, 0, 0))
|
if (!ret.getVoice()->loadSoundObject(search->second.second, 0, 1000.f, 0x3c, 0, 0))
|
||||||
{
|
{
|
||||||
ret._destroy();
|
ret._destroy();
|
||||||
m_activeEmitters.pop_back();
|
m_activeEmitters.pop_back();
|
||||||
|
|
|
@ -1,8 +1,165 @@
|
||||||
#include "amuse/Sequencer.hpp"
|
#include "amuse/Sequencer.hpp"
|
||||||
|
#include "amuse/Submix.hpp"
|
||||||
|
#include "amuse/Voice.hpp"
|
||||||
|
#include "amuse/Engine.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
|
|
||||||
|
void Sequencer::_destroy()
|
||||||
|
{
|
||||||
|
Entity::_destroy();
|
||||||
|
if (m_submix)
|
||||||
|
m_submix->m_activeSequencers.erase(this);
|
||||||
|
|
||||||
|
for (const auto& chan : m_chanStates)
|
||||||
|
for (const auto& vox : chan.second.m_chanVoxs)
|
||||||
|
vox.second->_destroy();
|
||||||
|
}
|
||||||
|
|
||||||
Sequencer::~Sequencer() {}
|
Sequencer::~Sequencer() {}
|
||||||
|
|
||||||
|
Sequencer::Sequencer(Engine& engine, const AudioGroup& group,
|
||||||
|
const SongGroupIndex& songGroup, int setupId, Submix* smx)
|
||||||
|
: Entity(engine, group), m_songGroup(songGroup), m_submix(smx)
|
||||||
|
{
|
||||||
|
auto it = m_songGroup.m_midiSetups.find(setupId);
|
||||||
|
if (it != m_songGroup.m_midiSetups.cend())
|
||||||
|
m_midiSetup = it->second->data();
|
||||||
|
if (m_submix)
|
||||||
|
m_submix->m_activeSequencers.insert(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sequencer::ChannelState::~ChannelState()
|
||||||
|
{
|
||||||
|
if (m_submix)
|
||||||
|
m_parent.m_engine.removeSubmix(m_submix);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sequencer::ChannelState::ChannelState(Sequencer& parent, uint8_t chanId)
|
||||||
|
: m_parent(parent), m_setup(m_parent.m_midiSetup[chanId])
|
||||||
|
{
|
||||||
|
if (chanId == 10)
|
||||||
|
{
|
||||||
|
auto it = m_parent.m_songGroup.m_drumPages.find(m_setup.programNo);
|
||||||
|
if (it != m_parent.m_songGroup.m_drumPages.cend())
|
||||||
|
m_page = it->second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto it = m_parent.m_songGroup.m_normPages.find(m_setup.programNo);
|
||||||
|
if (it != m_parent.m_songGroup.m_normPages.cend())
|
||||||
|
m_page = it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_submix = m_parent.m_engine.addSubmix(m_parent.m_submix);
|
||||||
|
if (m_setup.reverb)
|
||||||
|
m_submix->makeReverbStd(0.5f, m_setup.reverb / 127.f, 5.f, 0.5f, 0.f);
|
||||||
|
if (m_setup.chorus)
|
||||||
|
m_submix->makeChorus(15, m_setup.chorus * 5 / 127, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Sequencer::getVoiceCount() const
|
||||||
|
{
|
||||||
|
size_t ret = 0;
|
||||||
|
for (const auto& chan : m_chanStates)
|
||||||
|
for (const auto& vox : chan.second.m_chanVoxs)
|
||||||
|
ret += vox.second->getTotalVoices();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velocity)
|
||||||
|
{
|
||||||
|
if (!m_page)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
std::shared_ptr<Voice> ret = m_parent.m_engine._allocateVoice(m_parent.m_audioGroup, 32000.0,
|
||||||
|
true, false, m_submix);
|
||||||
|
m_chanVoxs[note] = ret;
|
||||||
|
if (!ret->loadSoundObject(SBig(m_page->objId), 0, 1000.f, note, velocity, m_ctrlVals[1]))
|
||||||
|
{
|
||||||
|
m_parent.m_engine._destroyVoice(ret.get());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
ret->setVolume(m_setup.volume / 127.f);
|
||||||
|
ret->setPan(m_setup.panning / 64.f - 127.f);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Voice> Sequencer::keyOn(uint8_t chan, uint8_t note, uint8_t velocity)
|
||||||
|
{
|
||||||
|
auto chanSearch = m_chanStates.find(chan);
|
||||||
|
if (chanSearch == m_chanStates.cend())
|
||||||
|
{
|
||||||
|
auto it = m_chanStates.emplace(std::make_pair(chan, ChannelState(*this, chan)));
|
||||||
|
it.first->second.keyOn(note, velocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return chanSearch->second.keyOn(note, velocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequencer::ChannelState::keyOff(uint8_t note, uint8_t velocity)
|
||||||
|
{
|
||||||
|
auto keySearch = m_chanVoxs.find(note);
|
||||||
|
if (keySearch == m_chanVoxs.cend())
|
||||||
|
return;
|
||||||
|
|
||||||
|
keySearch->second->keyOff();
|
||||||
|
m_chanVoxs.erase(keySearch);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequencer::keyOff(uint8_t chan, uint8_t note, uint8_t velocity)
|
||||||
|
{
|
||||||
|
auto chanSearch = m_chanStates.find(chan);
|
||||||
|
if (chanSearch == m_chanStates.cend())
|
||||||
|
return;
|
||||||
|
|
||||||
|
chanSearch->second.keyOff(note, velocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequencer::ChannelState::setCtrlValue(uint8_t ctrl, int8_t val)
|
||||||
|
{
|
||||||
|
m_ctrlVals[ctrl] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequencer::setCtrlValue(uint8_t chan, uint8_t ctrl, int8_t val)
|
||||||
|
{
|
||||||
|
auto chanSearch = m_chanStates.find(chan);
|
||||||
|
if (chanSearch == m_chanStates.cend())
|
||||||
|
return;
|
||||||
|
|
||||||
|
chanSearch->second.setCtrlValue(ctrl, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequencer::ChannelState::setPitchWheel(float pitchWheel)
|
||||||
|
{
|
||||||
|
for (const auto& vox : m_chanVoxs)
|
||||||
|
vox.second->setPitchWheel(pitchWheel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequencer::setPitchWheel(uint8_t chan, float pitchWheel)
|
||||||
|
{
|
||||||
|
auto chanSearch = m_chanStates.find(chan);
|
||||||
|
if (chanSearch == m_chanStates.cend())
|
||||||
|
return;
|
||||||
|
|
||||||
|
chanSearch->second.setPitchWheel(pitchWheel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequencer::ChannelState::allOff()
|
||||||
|
{
|
||||||
|
for (const auto& vox : m_chanVoxs)
|
||||||
|
vox.second->keyOff();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequencer::allOff(bool now)
|
||||||
|
{
|
||||||
|
if (now)
|
||||||
|
for (auto& chan : m_chanStates)
|
||||||
|
chan.second.m_chanVoxs.clear();
|
||||||
|
else
|
||||||
|
for (auto& chan : m_chanStates)
|
||||||
|
chan.second.allOff();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,7 +189,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||||
if (macroId == m_header.m_macroId)
|
if (macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = macroStep;
|
m_pc.back().second = macroStep;
|
||||||
else
|
else
|
||||||
vox.loadSoundMacro(macroId, macroStep, m_ticksPerSec,
|
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec,
|
||||||
m_initKey, m_initVel, m_initMod);
|
m_initKey, m_initVel, m_initMod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,7 +207,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||||
if (macroId == m_header.m_macroId)
|
if (macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = macroStep;
|
m_pc.back().second = macroStep;
|
||||||
else
|
else
|
||||||
vox.loadSoundMacro(macroId, macroStep, m_ticksPerSec,
|
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec,
|
||||||
m_initKey, m_initVel, m_initMod);
|
m_initKey, m_initVel, m_initMod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,7 +291,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||||
if (macroId == m_header.m_macroId)
|
if (macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = macroStep;
|
m_pc.back().second = macroStep;
|
||||||
else
|
else
|
||||||
vox.loadSoundMacro(macroId, macroStep, m_ticksPerSec,
|
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec,
|
||||||
m_initKey, m_initVel, m_initMod);
|
m_initKey, m_initVel, m_initMod);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -380,7 +380,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||||
if (macroId == m_header.m_macroId)
|
if (macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = macroStep;
|
m_pc.back().second = macroStep;
|
||||||
else
|
else
|
||||||
vox.loadSoundMacro(macroId, macroStep, m_ticksPerSec,
|
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec,
|
||||||
m_initKey, m_initVel, m_initMod);
|
m_initKey, m_initVel, m_initMod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,7 +501,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||||
if (macroId == m_header.m_macroId)
|
if (macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = macroStep;
|
m_pc.back().second = macroStep;
|
||||||
else
|
else
|
||||||
vox.loadSoundMacro(macroId, macroStep, m_ticksPerSec,
|
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec,
|
||||||
m_initKey, m_initVel, m_initMod);
|
m_initKey, m_initVel, m_initMod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -753,7 +753,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||||
if (macroId == m_header.m_macroId)
|
if (macroId == m_header.m_macroId)
|
||||||
m_pc.push_back({m_pc.back().first, macroStep});
|
m_pc.push_back({m_pc.back().first, macroStep});
|
||||||
else
|
else
|
||||||
vox.loadSoundMacro(macroId, macroStep, m_ticksPerSec,
|
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec,
|
||||||
m_initKey, m_initVel, m_initMod, true);
|
m_initKey, m_initVel, m_initMod, true);
|
||||||
|
|
||||||
m_header = *reinterpret_cast<const Header*>(m_pc.back().first);
|
m_header = *reinterpret_cast<const Header*>(m_pc.back().first);
|
||||||
|
@ -1226,7 +1226,7 @@ void SoundMacroState::keyoffNotify(Voice& vox)
|
||||||
if (m_keyoffTrap.macroId == m_header.m_macroId)
|
if (m_keyoffTrap.macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = m_keyoffTrap.macroStep;
|
m_pc.back().second = m_keyoffTrap.macroStep;
|
||||||
else
|
else
|
||||||
vox.loadSoundMacro(m_keyoffTrap.macroId, m_keyoffTrap.macroStep,
|
vox.loadSoundObject(m_keyoffTrap.macroId, m_keyoffTrap.macroStep,
|
||||||
m_ticksPerSec, m_initKey, m_initVel, m_initMod);
|
m_ticksPerSec, m_initKey, m_initVel, m_initMod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1242,7 +1242,7 @@ void SoundMacroState::sampleEndNotify(Voice& vox)
|
||||||
if (m_sampleEndTrap.macroId == m_header.m_macroId)
|
if (m_sampleEndTrap.macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = m_sampleEndTrap.macroStep;
|
m_pc.back().second = m_sampleEndTrap.macroStep;
|
||||||
else
|
else
|
||||||
vox.loadSoundMacro(m_sampleEndTrap.macroId, m_sampleEndTrap.macroStep,
|
vox.loadSoundObject(m_sampleEndTrap.macroId, m_sampleEndTrap.macroStep,
|
||||||
m_ticksPerSec, m_initKey, m_initVel, m_initMod);
|
m_ticksPerSec, m_initKey, m_initVel, m_initMod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1256,7 +1256,7 @@ void SoundMacroState::messageNotify(Voice& vox, int32_t val)
|
||||||
if (m_messageTrap.macroId == m_header.m_macroId)
|
if (m_messageTrap.macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = m_messageTrap.macroStep;
|
m_pc.back().second = m_messageTrap.macroStep;
|
||||||
else
|
else
|
||||||
vox.loadSoundMacro(m_messageTrap.macroId, m_messageTrap.macroStep,
|
vox.loadSoundObject(m_messageTrap.macroId, m_messageTrap.macroStep,
|
||||||
m_ticksPerSec, m_initKey, m_initVel, m_initMod);
|
m_ticksPerSec, m_initKey, m_initVel, m_initMod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
119
lib/Voice.cpp
119
lib/Voice.cpp
|
@ -44,6 +44,11 @@ void Voice::_reset()
|
||||||
m_curReverbVol = 0.f;
|
m_curReverbVol = 0.f;
|
||||||
m_curPan = 0.f;
|
m_curPan = 0.f;
|
||||||
m_curSpan = 0.f;
|
m_curSpan = 0.f;
|
||||||
|
m_curPitchWheel = 0.f;
|
||||||
|
m_curAftertouch = 0;
|
||||||
|
m_pitchWheelUp = 6;
|
||||||
|
m_pitchWheelDown = 6;
|
||||||
|
m_pitchWheelVal = 0;
|
||||||
m_pitchSweep1 = 0;
|
m_pitchSweep1 = 0;
|
||||||
m_pitchSweep1Times = 0;
|
m_pitchSweep1Times = 0;
|
||||||
m_pitchSweep2 = 0;
|
m_pitchSweep2 = 0;
|
||||||
|
@ -57,6 +62,7 @@ void Voice::_reset()
|
||||||
m_tremoloModScale = 0.f;
|
m_tremoloModScale = 0.f;
|
||||||
m_lfoPeriods[0] = 0.f;
|
m_lfoPeriods[0] = 0.f;
|
||||||
m_lfoPeriods[1] = 0.f;
|
m_lfoPeriods[1] = 0.f;
|
||||||
|
memset(m_ctrlVals, 0, 128);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Voice::_checkSamplePos()
|
bool Voice::_checkSamplePos()
|
||||||
|
@ -96,13 +102,23 @@ void Voice::_setTotalPitch(int32_t cents)
|
||||||
m_backendVoice->setPitchRatio(ratio);
|
m_backendVoice->setPitchRatio(ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Voice::_isRecursivelyDead()
|
||||||
|
{
|
||||||
|
if (m_voxState != VoiceState::Dead)
|
||||||
|
return false;
|
||||||
|
for (std::shared_ptr<Voice>& vox : m_childVoices)
|
||||||
|
if (!vox->_isRecursivelyDead())
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Voice::_bringOutYourDead()
|
void Voice::_bringOutYourDead()
|
||||||
{
|
{
|
||||||
for (auto it = m_childVoices.begin() ; it != m_childVoices.end() ;)
|
for (auto it = m_childVoices.begin() ; it != m_childVoices.end() ;)
|
||||||
{
|
{
|
||||||
Voice* vox = it->get();
|
Voice* vox = it->get();
|
||||||
vox->_bringOutYourDead();
|
vox->_bringOutYourDead();
|
||||||
if (vox->m_voxState == VoiceState::Dead)
|
if (vox->_isRecursivelyDead())
|
||||||
{
|
{
|
||||||
it = _destroyVoice(vox);
|
it = _destroyVoice(vox);
|
||||||
continue;
|
continue;
|
||||||
|
@ -253,7 +269,7 @@ bool Voice::_advanceSample(int16_t& samp)
|
||||||
/* Apply total pitch */
|
/* Apply total pitch */
|
||||||
if (pitchDirty)
|
if (pitchDirty)
|
||||||
{
|
{
|
||||||
_setTotalPitch(totalPitch + m_pitchSweep1 + m_pitchSweep2);
|
_setTotalPitch(totalPitch + m_pitchSweep1 + m_pitchSweep2 + m_pitchWheelVal);
|
||||||
refresh = true;
|
refresh = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,11 +406,12 @@ int Voice::maxVid() const
|
||||||
return maxVid;
|
return maxVid;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Voice> Voice::startChildMacro(int8_t addNote, ObjectId macroId, int macroStep)
|
std::shared_ptr<Voice> Voice::_startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec,
|
||||||
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc)
|
||||||
{
|
{
|
||||||
std::shared_ptr<Voice> vox = _allocateVoice(32000.0, true);
|
std::shared_ptr<Voice> vox = _allocateVoice(32000.0, true);
|
||||||
if (!vox->loadSoundMacro(macroId, macroStep, 1000.0, m_state.m_initKey + addNote,
|
if (!vox->loadSoundObject(macroId, macroStep, ticksPerSec, midiKey,
|
||||||
m_state.m_initVel, m_state.m_initMod))
|
midiVel, midiMod, pushPc))
|
||||||
{
|
{
|
||||||
_destroyVoice(vox.get());
|
_destroyVoice(vox.get());
|
||||||
return {};
|
return {};
|
||||||
|
@ -402,14 +419,15 @@ std::shared_ptr<Voice> Voice::startChildMacro(int8_t addNote, ObjectId macroId,
|
||||||
return vox;
|
return vox;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Voice::loadSoundMacro(ObjectId macroId, int macroStep, double ticksPerSec,
|
std::shared_ptr<Voice> Voice::startChildMacro(int8_t addNote, ObjectId macroId, int macroStep)
|
||||||
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod,
|
|
||||||
bool pushPc)
|
|
||||||
{
|
{
|
||||||
const unsigned char* macroData = m_audioGroup.getPool().soundMacro(macroId);
|
return _startChildMacro(macroId, macroStep, 1000.0, m_state.m_initKey + addNote,
|
||||||
if (!macroData)
|
m_state.m_initVel, m_state.m_initMod);
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
bool Voice::_loadSoundMacro(const unsigned char* macroData, int macroStep, double ticksPerSec,
|
||||||
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc)
|
||||||
|
{
|
||||||
if (m_state.m_pc.empty())
|
if (m_state.m_pc.empty())
|
||||||
m_state.initialize(macroData, macroStep, ticksPerSec, midiKey, midiVel, midiMod);
|
m_state.initialize(macroData, macroStep, ticksPerSec, midiKey, midiVel, midiMod);
|
||||||
else
|
else
|
||||||
|
@ -426,6 +444,53 @@ bool Voice::loadSoundMacro(ObjectId macroId, int macroStep, double ticksPerSec,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Voice::_loadKeymap(const Keymap* keymap, int macroStep, double ticksPerSec,
|
||||||
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc)
|
||||||
|
{
|
||||||
|
const Keymap& km = keymap[midiKey];
|
||||||
|
midiKey += km.transpose;
|
||||||
|
return loadSoundObject(SBig(km.objectId), macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Voice::_loadLayer(const std::vector<const LayerMapping*>& layer, int macroStep, double ticksPerSec,
|
||||||
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
for (const LayerMapping* mapping : layer)
|
||||||
|
{
|
||||||
|
if (midiKey >= mapping->keyLo && midiKey <= mapping->keyHi)
|
||||||
|
{
|
||||||
|
midiKey += mapping->transpose;
|
||||||
|
if (m_voxState != VoiceState::Playing)
|
||||||
|
ret |= loadSoundObject(SBig(mapping->objectId), macroStep, ticksPerSec,
|
||||||
|
midiKey, midiVel, midiMod, pushPc);
|
||||||
|
else
|
||||||
|
ret |= _startChildMacro(SBig(mapping->objectId), macroStep, ticksPerSec,
|
||||||
|
midiKey, midiVel, midiMod, pushPc).operator bool();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Voice::loadSoundObject(ObjectId objectId, int macroStep, double ticksPerSec,
|
||||||
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod,
|
||||||
|
bool pushPc)
|
||||||
|
{
|
||||||
|
const unsigned char* macroData = m_audioGroup.getPool().soundMacro(objectId);
|
||||||
|
if (macroData)
|
||||||
|
return _loadSoundMacro(macroData, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc);
|
||||||
|
|
||||||
|
const Keymap* keymap = m_audioGroup.getPool().keymap(objectId);
|
||||||
|
if (keymap)
|
||||||
|
return _loadKeymap(keymap, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc);
|
||||||
|
|
||||||
|
const std::vector<const LayerMapping*>* layer = m_audioGroup.getPool().layer(objectId);
|
||||||
|
if (layer)
|
||||||
|
return _loadLayer(*layer, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void Voice::keyOff()
|
void Voice::keyOff()
|
||||||
{
|
{
|
||||||
if (m_sustained)
|
if (m_sustained)
|
||||||
|
@ -546,10 +611,6 @@ void Voice::setPitchKey(int32_t cents)
|
||||||
m_pitchDirty = true;
|
m_pitchDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setModulation(float mod)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Voice::setPedal(bool pedal)
|
void Voice::setPedal(bool pedal)
|
||||||
{
|
{
|
||||||
if (m_sustained && !pedal && m_sustainKeyOff)
|
if (m_sustained && !pedal && m_sustainKeyOff)
|
||||||
|
@ -629,8 +690,36 @@ void Voice::setPitchAdsr(ObjectId adsrId, int32_t cents)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Voice::setPitchWheel(float pitchWheel)
|
||||||
|
{
|
||||||
|
m_curPitchWheel = amuse::clamp(-1.f, pitchWheel, 1.f);
|
||||||
|
if (pitchWheel > 0.f)
|
||||||
|
m_pitchWheelVal = m_pitchWheelUp * m_curPitchWheel;
|
||||||
|
else if (pitchWheel < 0.f)
|
||||||
|
m_pitchWheelVal = m_pitchWheelDown * -m_curPitchWheel;
|
||||||
|
else
|
||||||
|
m_pitchWheelVal = 0;
|
||||||
|
m_pitchDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
void Voice::setPitchWheelRange(int8_t up, int8_t down)
|
void Voice::setPitchWheelRange(int8_t up, int8_t down)
|
||||||
{
|
{
|
||||||
|
m_pitchWheelUp = up * 100;
|
||||||
|
m_pitchWheelDown = down * 100;
|
||||||
|
setPitchWheel(m_curPitchWheel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::setAftertouch(uint8_t aftertouch)
|
||||||
|
{
|
||||||
|
m_curAftertouch = aftertouch;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Voice::getTotalVoices() const
|
||||||
|
{
|
||||||
|
size_t ret = 1;
|
||||||
|
for (const std::shared_ptr<Voice>& vox : m_childVoices)
|
||||||
|
ret += vox->getTotalVoices();
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue