mirror of https://github.com/AxioDL/amuse.git
185 lines
8.2 KiB
C++
185 lines
8.2 KiB
C++
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <list>
|
|
#include <memory>
|
|
#include <random>
|
|
#include <unordered_map>
|
|
|
|
#include "amuse/AudioGroupSampleDirectory.hpp"
|
|
#include "amuse/Emitter.hpp"
|
|
#include "amuse/IBackendVoiceAllocator.hpp"
|
|
#include "amuse/Listener.hpp"
|
|
#include "amuse/Sequencer.hpp"
|
|
#include "amuse/Studio.hpp"
|
|
|
|
namespace amuse {
|
|
class AudioGroup;
|
|
class AudioGroupData;
|
|
class Emitter;
|
|
class IBackendVoiceAllocator;
|
|
class IMIDIReader;
|
|
class Submix;
|
|
class Voice;
|
|
|
|
enum class AmplitudeMode {
|
|
PerSample, /**< Per-sample amplitude evaluation (dt = 1.0 / sampleRate, rather CPU demanding) */
|
|
BlockLinearized /**< Per-block lerp amplitude evaluation (dt = 160.0 / sampleRate) */
|
|
};
|
|
|
|
/** Main audio playback system for a single audio output */
|
|
class Engine {
|
|
friend class Emitter;
|
|
friend class Sequencer;
|
|
friend class Studio;
|
|
friend class Voice;
|
|
friend struct Sequencer::ChannelState;
|
|
|
|
IBackendVoiceAllocator& m_backend;
|
|
AmplitudeMode m_ampMode;
|
|
std::unique_ptr<IMIDIReader> m_midiReader;
|
|
std::unordered_map<const AudioGroupData*, std::unique_ptr<AudioGroup>> m_audioGroups;
|
|
std::list<ObjToken<Voice>> m_activeVoices;
|
|
std::list<ObjToken<Emitter>> m_activeEmitters;
|
|
std::list<ObjToken<Listener>> m_activeListeners;
|
|
std::list<ObjToken<Sequencer>> m_activeSequencers;
|
|
bool m_defaultStudioReady = false;
|
|
ObjToken<Studio> m_defaultStudio;
|
|
std::unordered_map<SFXId, std::tuple<AudioGroup*, GroupId, const SFXGroupIndex::SFXEntry*>> m_sfxLookup;
|
|
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
|
|
int m_nextVid = 0;
|
|
float m_masterVolume = 1.f;
|
|
AudioChannelSet m_channelSet = AudioChannelSet::Unknown;
|
|
|
|
AudioGroup* _addAudioGroup(const AudioGroupData& data, std::unique_ptr<AudioGroup>&& grp);
|
|
std::pair<AudioGroup*, const SongGroupIndex*> _findSongGroup(GroupId groupId) const;
|
|
std::pair<AudioGroup*, const SFXGroupIndex*> _findSFXGroup(GroupId groupId) const;
|
|
|
|
std::list<ObjToken<Voice>>::iterator _allocateVoice(const AudioGroup& group, GroupId groupId, double sampleRate,
|
|
bool dynamicPitch, bool emitter, ObjToken<Studio> studio);
|
|
std::list<ObjToken<Sequencer>>::iterator _allocateSequencer(const AudioGroup& group, GroupId groupId, SongId setupId,
|
|
ObjToken<Studio> studio);
|
|
ObjToken<Studio> _allocateStudio(bool mainOut);
|
|
std::list<ObjToken<Voice>>::iterator _destroyVoice(std::list<ObjToken<Voice>>::iterator it);
|
|
std::list<ObjToken<Sequencer>>::iterator _destroySequencer(std::list<ObjToken<Sequencer>>::iterator it);
|
|
void _bringOutYourDead();
|
|
|
|
public:
|
|
~Engine();
|
|
Engine(IBackendVoiceAllocator& backend, AmplitudeMode ampMode = AmplitudeMode::PerSample);
|
|
|
|
/** Access voice backend of engine */
|
|
IBackendVoiceAllocator& getBackend() { return m_backend; }
|
|
|
|
/** Access MIDI reader */
|
|
IMIDIReader* getMIDIReader() const { return m_midiReader.get(); }
|
|
|
|
/** Add audio group data pointers to engine; must remain resident! */
|
|
const AudioGroup* addAudioGroup(const AudioGroupData& data);
|
|
|
|
/** Remove audio group from engine */
|
|
void removeAudioGroup(const AudioGroupData& data);
|
|
|
|
/** Access engine's default studio */
|
|
ObjToken<Studio> getDefaultStudio() { return m_defaultStudio; }
|
|
|
|
/** Create new Studio within engine */
|
|
ObjToken<Studio> addStudio(bool mainOut);
|
|
|
|
/** Start soundFX playing from loaded audio groups */
|
|
ObjToken<Voice> fxStart(SFXId sfxId, float vol, float pan, ObjToken<Studio> smx);
|
|
ObjToken<Voice> fxStart(SFXId sfxId, float vol, float pan) { return fxStart(sfxId, vol, pan, m_defaultStudio); }
|
|
|
|
/** Start soundFX playing from explicit group data (for editor use) */
|
|
ObjToken<Voice> fxStart(const AudioGroup* group, GroupId groupId, SFXId sfxId, float vol, float pan,
|
|
ObjToken<Studio> smx);
|
|
ObjToken<Voice> fxStart(const AudioGroup* group, GroupId groupId, SFXId sfxId, float vol, float pan) {
|
|
return fxStart(group, groupId, sfxId, vol, pan, m_defaultStudio);
|
|
}
|
|
|
|
/** Start SoundMacro node playing directly (for editor use) */
|
|
ObjToken<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel, uint8_t mod,
|
|
ObjToken<Studio> smx);
|
|
ObjToken<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel, uint8_t mod) {
|
|
return macroStart(group, id, key, vel, mod, m_defaultStudio);
|
|
}
|
|
|
|
/** Start SoundMacro object playing directly (for editor use) */
|
|
ObjToken<Voice> macroStart(const AudioGroup* group, const SoundMacro* macro, uint8_t key, uint8_t vel, uint8_t mod,
|
|
ObjToken<Studio> smx);
|
|
ObjToken<Voice> macroStart(const AudioGroup* group, const SoundMacro* macro, uint8_t key, uint8_t vel, uint8_t mod) {
|
|
return macroStart(group, macro, key, vel, mod, m_defaultStudio);
|
|
}
|
|
|
|
/** Start PageObject node playing directly (for editor use) */
|
|
ObjToken<Voice> pageObjectStart(const AudioGroup* group, ObjectId id, uint8_t key, uint8_t vel, uint8_t mod,
|
|
ObjToken<Studio> smx);
|
|
ObjToken<Voice> pageObjectStart(const AudioGroup* group, ObjectId id, uint8_t key, uint8_t vel, uint8_t mod) {
|
|
return pageObjectStart(group, id, key, vel, mod, m_defaultStudio);
|
|
}
|
|
|
|
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
|
|
ObjToken<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff, SFXId sfxId,
|
|
float minVol, float maxVol, bool doppler, ObjToken<Studio> smx);
|
|
ObjToken<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff, SFXId sfxId,
|
|
float minVol, float maxVol, bool doppler) {
|
|
return addEmitter(pos, dir, maxDist, falloff, sfxId, minVol, maxVol, doppler, m_defaultStudio);
|
|
}
|
|
|
|
/** Build listener and add to engine's listener list */
|
|
ObjToken<Listener> addListener(const float* pos, const float* dir, const float* heading, const float* up,
|
|
float frontDiff, float backDiff, float soundSpeed, float volume);
|
|
|
|
/** Remove listener from engine's listener list */
|
|
void removeListener(Listener* listener);
|
|
|
|
/** Start song playing from loaded audio groups */
|
|
ObjToken<Sequencer> seqPlay(GroupId groupId, SongId songId, const unsigned char* arrData, bool loop,
|
|
ObjToken<Studio> smx);
|
|
ObjToken<Sequencer> seqPlay(GroupId groupId, SongId songId, const unsigned char* arrData, bool loop = true) {
|
|
return seqPlay(groupId, songId, arrData, loop, m_defaultStudio);
|
|
}
|
|
|
|
/** Start song playing from explicit group data (for editor use) */
|
|
ObjToken<Sequencer> seqPlay(const AudioGroup* group, GroupId groupId, SongId songId, const unsigned char* arrData,
|
|
bool loop, ObjToken<Studio> smx);
|
|
ObjToken<Sequencer> seqPlay(const AudioGroup* group, GroupId groupId, SongId songId, const unsigned char* arrData,
|
|
bool loop = true) {
|
|
return seqPlay(group, groupId, songId, arrData, loop, m_defaultStudio);
|
|
}
|
|
|
|
/** Set total volume of engine */
|
|
void setVolume(float vol);
|
|
|
|
/** Find voice from VoiceId */
|
|
ObjToken<Voice> findVoice(int vid);
|
|
|
|
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */
|
|
void killKeygroup(uint8_t kg, bool now);
|
|
|
|
/** Send all voices using `macroId` the message `val` */
|
|
void sendMacroMessage(ObjectId macroId, int32_t val);
|
|
|
|
/** Obtain next random number from engine's PRNG */
|
|
uint32_t nextRandom() { return m_random(); }
|
|
|
|
/** Obtain list of active voices */
|
|
std::list<ObjToken<Voice>>& getActiveVoices() { return m_activeVoices; }
|
|
|
|
/** Obtain total active voice count (including child voices) */
|
|
size_t getNumTotalActiveVoices() const;
|
|
|
|
/** Obtain list of active sequencers */
|
|
std::list<ObjToken<Sequencer>>& getActiveSequencers() { return m_activeSequencers; }
|
|
|
|
/** All mixing occurs in virtual 5ms intervals;
|
|
* this is called at the start of each interval for all mixable entities */
|
|
void _on5MsInterval(IBackendVoiceAllocator& engine, double dt);
|
|
|
|
/** When a pumping cycle is complete this is called to allow the client to
|
|
* perform periodic cleanup tasks */
|
|
void _onPumpCycleComplete(IBackendVoiceAllocator& engine);
|
|
};
|
|
} // namespace amuse
|