mirror of https://github.com/AxioDL/amuse.git
152 lines
5.5 KiB
C++
152 lines
5.5 KiB
C++
#ifndef __AMUSE_SEQUENCER_HPP__
|
|
#define __AMUSE_SEQUENCER_HPP__
|
|
|
|
#include "Entity.hpp"
|
|
#include "AudioGroupProject.hpp"
|
|
#include "SongState.hpp"
|
|
#include "optional.hpp"
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <memory>
|
|
#include <list>
|
|
|
|
namespace amuse
|
|
{
|
|
class Submix;
|
|
class Voice;
|
|
|
|
/** State of sequencer over lifetime */
|
|
enum class SequencerState
|
|
{
|
|
Playing, /**< Sequencer actively playing arrangement */
|
|
Interactive, /**< Interactive sequencer for live MIDI message processing, will not automatically die */
|
|
Dead /**< Set when arrangement complete and `dieOnEnd` was set, or manually with die() */
|
|
};
|
|
|
|
/** Multi-voice lifetime manager and polyphonic parameter tracking */
|
|
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) */
|
|
std::list<std::shared_ptr<Sequencer>>::iterator m_engineIt; /**< Iterator to self within Engine's list for quick deletion */
|
|
|
|
const unsigned char* m_arrData = nullptr; /**< Current playing arrangement data */
|
|
SongState m_songState; /**< State of current arrangement playback */
|
|
double m_ticksPerSec = 1000.0; /**< Current ticks per second (tempo) for arrangement data */
|
|
SequencerState m_state = SequencerState::Interactive; /**< Current high-level state of sequencer */
|
|
bool m_dieOnEnd = false; /**< Sequencer will be killed when current arrangement completes */
|
|
|
|
float m_curVol = 1.f; /**< Current volume of sequencer */
|
|
|
|
/** State of a single MIDI channel */
|
|
struct ChannelState
|
|
{
|
|
Sequencer& m_parent;
|
|
uint8_t m_chanId;
|
|
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;
|
|
std::unordered_set<std::shared_ptr<Voice>> m_keyoffVoxs;
|
|
int8_t m_ctrlVals[128]; /**< MIDI controller values */
|
|
float m_curPitchWheel = 0.f; /**< MIDI pitch-wheel */
|
|
int8_t m_curProgram = 0; /**< MIDI program number */
|
|
float m_curVol = 1.f; /**< Current volume of channel */
|
|
float m_curPan = 0.f; /**< Current panning of channel */
|
|
|
|
void _bringOutYourDead();
|
|
size_t getVoiceCount() const;
|
|
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);
|
|
bool programChange(int8_t prog);
|
|
void nextProgram();
|
|
void prevProgram();
|
|
void setPitchWheel(float pitchWheel);
|
|
void setVolume(float vol);
|
|
void setPan(float pan);
|
|
void allOff();
|
|
void killKeygroup(uint8_t kg, bool now);
|
|
std::shared_ptr<Voice> findVoice(int vid);
|
|
void sendMacroMessage(ObjectId macroId, int32_t val);
|
|
};
|
|
std::array<std::experimental::optional<ChannelState>, 16> m_chanStates; /**< Lazily-allocated channel states */
|
|
|
|
void _bringOutYourDead();
|
|
void _destroy();
|
|
public:
|
|
~Sequencer();
|
|
Sequencer(Engine& engine, const AudioGroup& group, int groupId,
|
|
const SongGroupIndex& songGroup, int setupId, Submix* smx);
|
|
|
|
/** Advance current song data (if any) */
|
|
void advance(double dt);
|
|
|
|
/** Obtain pointer to Sequencer's Submix */
|
|
Submix* getSubmix() {return m_submix;}
|
|
|
|
/** Get current state of sequencer */
|
|
SequencerState state() const {return m_state;}
|
|
|
|
/** 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);
|
|
|
|
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */
|
|
void killKeygroup(uint8_t kg, bool now);
|
|
|
|
/** Find voice instance contained within Sequencer */
|
|
std::shared_ptr<Voice> findVoice(int vid);
|
|
|
|
/** Send all voices using `macroId` the message `val` */
|
|
void sendMacroMessage(ObjectId macroId, int32_t val);
|
|
|
|
/** Set tempo of sequencer and all voices in ticks per second */
|
|
void setTempo(double ticksPerSec);
|
|
|
|
/** Play MIDI arrangement */
|
|
void playSong(const unsigned char* arrData, bool dieOnEnd=true);
|
|
|
|
/** Set total volume of sequencer */
|
|
void setVolume(float vol);
|
|
|
|
/** Get current program number of channel */
|
|
int8_t getChanProgram(int8_t chanId) const;
|
|
|
|
/** Set current program number of channel */
|
|
bool setChanProgram(int8_t chanId, int8_t prog);
|
|
|
|
/** Advance to next program in channel */
|
|
void nextChanProgram(int8_t chanId);
|
|
|
|
/** Advance to prev program in channel */
|
|
void prevChanProgram(int8_t chanId);
|
|
|
|
/** Manually kill sequencer for deferred release from engine */
|
|
void kill() {m_state = SequencerState::Dead;}
|
|
};
|
|
|
|
}
|
|
|
|
#endif // __AMUSE_SEQUENCER_HPP__
|