Work on Sequencer

This commit is contained in:
Jack Andersen 2016-05-14 20:48:26 -10:00
parent 1382a1e946
commit 18f3ce6f44
12 changed files with 611 additions and 43 deletions

View File

@ -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: */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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