diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ac20196 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,46 @@ +set(SOURCES + lib/AudioGroup.cpp + lib/AudioGroupData.cpp + lib/AudioGroupPool.cpp + lib/AudioGroupProject.cpp + lib/AudioGroupSampleDirectory.cpp + lib/Emitter.cpp + lib/Engine.cpp + lib/Envelope.cpp + lib/Listener.cpp + lib/Sequencer.cpp + lib/SoundMacroState.cpp + lib/Voice.cpp) + +set(HEADERS + include/amuse/AudioGroup.hpp + include/amuse/AudioGroupData.hpp + include/amuse/AudioGroupPool.hpp + include/amuse/AudioGroupProject.hpp + include/amuse/AudioGroupSampleDirectory.hpp + include/amuse/Emitter.hpp + include/amuse/Engine.hpp + include/amuse/Entity.hpp + include/amuse/Envelope.hpp + include/amuse/Listener.hpp + include/amuse/Sequencer.hpp + include/amuse/SoundMacroState.hpp + include/amuse/Voice.hpp + include/amuse/IBackendVoice.hpp + include/amuse/IBackendVoiceAllocator.hpp + include/amuse/Common.hpp + include/amuse/amuse.hpp) + +unset(EXTRAS) +if(TARGET boo) + include_directories(${BOO_INCLUDE_DIR}) + list(APPEND EXTRAS lib/BooBackend.cpp include/amuse/BooBackend.hpp) +endif() + +include_directories(include) +set(AMUSE_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include CACHE PATH "amuse include path" FORCE) + +add_library(amuse + ${SOURCES} + ${HEADERS} + ${EXTRAS}) diff --git a/README.md b/README.md index 11a2097..d25e6d7 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Here's an example usage: int main(int argc, char* argv[]) { /* Up to the client to implement voice allocation and mixing */ - std::unique_ptr voxAlloc = MakeMyVoiceAllocator(); + std::unique_ptr voxAlloc = MakeMyVoiceAllocator(); /* Application just needs one per audio output (not per channel) */ amuse::Engine snd(*voxAlloc); diff --git a/include/amuse/AudioGroup.hpp b/include/amuse/AudioGroup.hpp new file mode 100644 index 0000000..b95e6cb --- /dev/null +++ b/include/amuse/AudioGroup.hpp @@ -0,0 +1,33 @@ +#ifndef __AMUSE_AUDIOGROUP_HPP__ +#define __AMUSE_AUDIOGROUP_HPP__ + +#include "AudioGroupPool.hpp" +#include "AudioGroupProject.hpp" +#include "AudioGroupSampleDirectory.hpp" + +namespace amuse +{ +class AudioGroupData; + +class AudioGroup +{ + int m_groupId; + AudioGroupPool m_pool; + AudioGroupProject m_proj; + AudioGroupSampleDirectory m_sdir; + const unsigned char* m_samp; + bool m_valid; +public: + operator bool() const {return m_valid;} + AudioGroup(int groupId, const AudioGroupData& data); + + int groupId() const {return m_groupId;} + bool sfxInGroup(int sfxId) const; + bool songInGroup(int songId) const; + + const AudioGroupSampleDirectory::Entry* getSfxEntry(int sfxId) const; +}; + +} + +#endif // __AMUSE_AUDIOGROUP_HPP__ diff --git a/include/amuse/AudioGroupData.hpp b/include/amuse/AudioGroupData.hpp new file mode 100644 index 0000000..9691fdd --- /dev/null +++ b/include/amuse/AudioGroupData.hpp @@ -0,0 +1,40 @@ +#ifndef __AMUSE_AUDIOGROUPDATA_HPP__ +#define __AMUSE_AUDIOGROUPDATA_HPP__ + +namespace amuse +{ + +/** + * @brief Simple pointer-container of the four Audio Group chunks + */ +class AudioGroupData +{ +protected: + unsigned char* m_pool; + unsigned char* m_proj; + unsigned char* m_sdir; + unsigned char* m_samp; +public: + AudioGroupData(unsigned char* pool, unsigned char* proj, + unsigned char* sdir, unsigned char* samp) + : m_pool(pool), m_proj(proj), m_sdir(sdir), m_samp(samp) {} + + const unsigned char* getPool() const {return m_pool;} + const unsigned char* getProj() const {return m_proj;} + const unsigned char* getSdir() const {return m_sdir;} + const unsigned char* getSamp() const {return m_samp;} +}; + +/** + * @brief A buffer-owning version of AudioGroupData + */ +class IntrusiveAudioGroupData : public AudioGroupData +{ +public: + using AudioGroupData::AudioGroupData; + ~IntrusiveAudioGroupData(); +}; + +} + +#endif // __AMUSE_AUDIOGROUPDATA_HPP__ diff --git a/include/amuse/AudioGroupPool.hpp b/include/amuse/AudioGroupPool.hpp new file mode 100644 index 0000000..d1f48b3 --- /dev/null +++ b/include/amuse/AudioGroupPool.hpp @@ -0,0 +1,15 @@ +#ifndef __AMUSE_AUDIOGROUPPOOL_HPP__ +#define __AMUSE_AUDIOGROUPPOOL_HPP__ + +namespace amuse +{ + +class AudioGroupPool +{ +public: + AudioGroupPool(const unsigned char* data); +}; + +} + +#endif // __AMUSE_AUDIOGROUPPOOL_HPP__ diff --git a/include/amuse/AudioGroupProject.hpp b/include/amuse/AudioGroupProject.hpp new file mode 100644 index 0000000..c71d225 --- /dev/null +++ b/include/amuse/AudioGroupProject.hpp @@ -0,0 +1,15 @@ +#ifndef __AMUSE_AUDIOGROUPPROJECT_HPP__ +#define __AMUSE_AUDIOGROUPPROJECT_HPP__ + +namespace amuse +{ + +class AudioGroupProject +{ +public: + AudioGroupProject(const unsigned char* data); +}; + +} + +#endif // __AMUSE_AUDIOGROUPPROJECT_HPP__ diff --git a/include/amuse/AudioGroupSampleDirectory.hpp b/include/amuse/AudioGroupSampleDirectory.hpp new file mode 100644 index 0000000..f08fa50 --- /dev/null +++ b/include/amuse/AudioGroupSampleDirectory.hpp @@ -0,0 +1,45 @@ +#ifndef __AMUSE_AUDIOGROUPSAMPLEDIR_HPP__ +#define __AMUSE_AUDIOGROUPSAMPLEDIR_HPP__ + +#include +#include + +namespace amuse +{ + +class AudioGroupSampleDirectory +{ + friend class AudioGroup; +public: + struct Entry + { + uint16_t m_sfxId; + uint32_t m_sampleOff; + uint32_t m_unk; + uint8_t m_pitch; + uint16_t m_sampleRate; + uint32_t m_numSamples; + uint32_t m_loopStartSample; + uint32_t m_loopLengthSamples; + uint32_t m_adpcmParmOffset; + void swapBig(); + }; + struct ADPCMParms + { + uint16_t m_bytesPerFrame; + uint8_t m_ps; + uint8_t m_lps; + int16_t m_hist1; + int16_t m_hist2; + int16_t m_coefs[16]; + void swapBig(); + }; +private: + std::vector m_entries; +public: + AudioGroupSampleDirectory(const unsigned char* data); +}; + +} + +#endif // __AMUSE_AUDIOGROUPSAMPLEDIR_HPP__ diff --git a/include/amuse/BooBackend.hpp b/include/amuse/BooBackend.hpp new file mode 100644 index 0000000..207db75 --- /dev/null +++ b/include/amuse/BooBackend.hpp @@ -0,0 +1,43 @@ +#ifndef __AMUSE_BOO_BACKEND_HPP__ +#define __AMUSE_BOO_BACKEND_HPP__ + +#include +#include "IBackendVoice.hpp" +#include "IBackendVoiceAllocator.hpp" + +namespace amuse +{ + +/** Backend voice implementation for boo mixer */ +class BooBackendVoice : public IBackendVoice +{ + friend class BooBackendVoiceAllocator; + Voice& m_clientVox; + struct VoiceCallback : boo::IAudioVoiceCallback + { + BooBackendVoice& m_parent; + size_t supplyAudio(boo::IAudioVoice& voice, size_t frames, int16_t* data); + VoiceCallback(BooBackendVoice& parent) : m_parent(parent) {} + } m_cb; + std::unique_ptr m_booVoice; +public: + BooBackendVoice(boo::IAudioVoiceEngine& engine, Voice& clientVox, + double sampleRate, bool dynamicPitch); + void setMatrixCoefficients(const float coefs[8]); + void setPitchRatio(double ratio); + void start(); + void stop(); +}; + +/** Backend voice allocator implementation for boo mixer */ +class BooBackendVoiceAllocator : public IBackendVoiceAllocator +{ + boo::IAudioVoiceEngine& m_booEngine; +public: + BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine); + std::unique_ptr allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch); +}; + +} + +#endif // __AMUSE_BOO_BACKEND_HPP__ diff --git a/include/amuse/Common.hpp b/include/amuse/Common.hpp new file mode 100644 index 0000000..854f811 --- /dev/null +++ b/include/amuse/Common.hpp @@ -0,0 +1,127 @@ +#ifndef __AMUSE_COMMON_HPP__ +#define __AMUSE_COMMON_HPP__ + +namespace amuse +{ + +#undef bswap16 +#undef bswap32 +#undef bswap64 + +/* Type-sensitive byte swappers */ +template +static inline T bswap16(T val) +{ +#if __GNUC__ + return __builtin_bswap16(val); +#elif _WIN32 + return _byteswap_ushort(val); +#else + return (val = (val << 8) | ((val >> 8) & 0xFF)); +#endif +} + +template +static inline T bswap32(T val) +{ +#if __GNUC__ + return __builtin_bswap32(val); +#elif _WIN32 + return _byteswap_ulong(val); +#else + val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16; + val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8; + return val; +#endif +} + +template +static inline T bswap64(T val) +{ +#if __GNUC__ + return __builtin_bswap64(val); +#elif _WIN32 + return _byteswap_uint64(val); +#else + return ((val & 0xFF00000000000000ULL) >> 56) | + ((val & 0x00FF000000000000ULL) >> 40) | + ((val & 0x0000FF0000000000ULL) >> 24) | + ((val & 0x000000FF00000000ULL) >> 8) | + ((val & 0x00000000FF000000ULL) << 8) | + ((val & 0x0000000000FF0000ULL) << 24) | + ((val & 0x000000000000FF00ULL) << 40) | + ((val & 0x00000000000000FFULL) << 56); +#endif +} + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +static inline int16_t SBig(int16_t val) {return bswap16(val);} +static inline uint16_t SBig(uint16_t val) {return bswap16(val);} +static inline int32_t SBig(int32_t val) {return bswap32(val);} +static inline uint32_t SBig(uint32_t val) {return bswap32(val);} +static inline int64_t SBig(int64_t val) {return bswap64(val);} +static inline uint64_t SBig(uint64_t val) {return bswap64(val);} +static inline float SBig(float val) +{ + int32_t ival = bswap32(*((int32_t*)(&val))); + return *((float*)(&ival)); +} +static inline double SBig(double val) +{ + int64_t ival = bswap64(*((int64_t*)(&val))); + return *((double*)(&ival)); +} +#ifndef SBIG +#define SBIG(q) ( ( (q) & 0x000000FF ) << 24 | ( (q) & 0x0000FF00 ) << 8 \ + | ( (q) & 0x00FF0000 ) >> 8 | ( (q) & 0xFF000000 ) >> 24 ) +#endif + +static inline int16_t SLittle(int16_t val) {return val;} +static inline uint16_t SLittle(uint16_t val) {return val;} +static inline int32_t SLittle(int32_t val) {return val;} +static inline uint32_t SLittle(uint32_t val) {return val;} +static inline int64_t SLittle(int64_t val) {return val;} +static inline uint64_t SLittle(uint64_t val) {return val;} +static inline float SLittle(float val) {return val;} +static inline double SLittle(double val) {return val;} +#ifndef SLITTLE +#define SLITTLE(q) (q) +#endif +#else +static inline int16_t SLittle(int16_t val) {return bswap16(val);} +static inline uint16_t SLittle(uint16_t val) {return bswap16(val);} +static inline int32_t SLittle(int32_t val) {return bswap32(val);} +static inline uint32_t SLittle(uint32_t val) {return bswap32(val);} +static inline int64_t SLittle(int64_t val) {return bswap64(val);} +static inline uint64_t SLittle(uint64_t val) {return bswap64(val);} +static inline float SLittle(float val) +{ + int32_t ival = bswap32(*((int32_t*)(&val))); + return *((float*)(&ival)); +} +static inline double SLittle(double val) +{ + int64_t ival = bswap64(*((int64_t*)(&val))); + return *((double*)(&ival)); +} +#ifndef SLITTLE +#define SLITTLE(q) ( ( (q) & 0x000000FF ) << 24 | ( (q) & 0x0000FF00 ) << 8 \ + | ( (q) & 0x00FF0000 ) >> 8 | ( (q) & 0xFF000000 ) >> 24 ) +#endif + +static inline int16_t SBig(int16_t val) {return val;} +static inline uint16_t SBig(uint16_t val) {return val;} +static inline int32_t SBig(int32_t val) {return val;} +static inline uint32_t SBig(uint32_t val) {return val;} +static inline int64_t SBig(int64_t val) {return val;} +static inline uint64_t SBig(uint64_t val) {return val;} +static inline float SBig(float val) {return val;} +static inline double SBig(double val) {return val;} +#ifndef SBIG +#define SBIG(q) (q) +#endif +#endif + +} + +#endif // __AMUSE_COMMON_HPP__ diff --git a/include/amuse/Emitter.hpp b/include/amuse/Emitter.hpp new file mode 100644 index 0000000..f5eeb6b --- /dev/null +++ b/include/amuse/Emitter.hpp @@ -0,0 +1,28 @@ +#ifndef __AMUSE_EMITTER_HPP__ +#define __AMUSE_EMITTER_HPP__ + +#include "Entity.hpp" + +namespace amuse +{ +class Voice; + +using Vector3f = float[3]; + +class Emitter : public Entity +{ + Voice& m_vox; +public: + Emitter(Engine& engine, int groupId, Voice& vox); + + void setPos(const Vector3f& pos); + void setDir(const Vector3f& dir); + void setMaxDist(float maxDist); + void setMaxVol(float maxVol); + void setMinVol(float minVol); + void setFalloff(float falloff); +}; + +} + +#endif // __AMUSE_EMITTER_HPP__ diff --git a/include/amuse/Engine.hpp b/include/amuse/Engine.hpp new file mode 100644 index 0000000..0e03c10 --- /dev/null +++ b/include/amuse/Engine.hpp @@ -0,0 +1,55 @@ +#ifndef __AMUSE_ENGINE_HPP__ +#define __AMUSE_ENGINE_HPP__ + +#include +#include +#include +#include "Emitter.hpp" +#include "AudioGroupSampleDirectory.hpp" + +namespace amuse +{ +class IBackendVoiceAllocator; +class Voice; +class Emitter; +class Sequencer; +class AudioGroup; +class AudioGroupData; + +/** Main audio playback system for a single audio output */ +class Engine +{ + IBackendVoiceAllocator& m_backend; + std::unordered_map> m_audioGroups; + std::list> m_activeVoices; + std::list> m_activeEmitters; + std::list> m_activeSequencers; + Voice* _allocateVoice(int groupId, double sampleRate, bool dynamicPitch); + AudioGroup* _findGroupFromSfxId(int sfxId, const AudioGroupSampleDirectory::Entry*& entOut) const; + AudioGroup* _findGroupFromSongId(int songId) const; +public: + Engine(IBackendVoiceAllocator& backend); + + /** Update all active audio entities and fill OS audio buffers as needed */ + void pumpEngine(); + + /** Add audio group data pointers to engine; must remain resident! */ + bool addAudioGroup(int groupId, const AudioGroupData& data); + + /** Remove audio group from engine */ + void removeAudioGroup(int groupId); + + /** Start soundFX playing from loaded audio groups */ + Voice* fxStart(int sfxId, float vol, float pan); + + /** Start soundFX playing from loaded audio groups, attach to positional emitter */ + Emitter* addEmitter(const Vector3f& pos, const Vector3f& dir, float maxDist, + float falloff, int sfxId, float minVol, float maxVol); + + /** Start song playing from loaded audio groups */ + Sequencer* seqPlay(int songId, const unsigned char* arrData); +}; + +} + +#endif // __AMUSE_ENGINE_HPP__ diff --git a/include/amuse/Entity.hpp b/include/amuse/Entity.hpp new file mode 100644 index 0000000..cfcd45a --- /dev/null +++ b/include/amuse/Entity.hpp @@ -0,0 +1,24 @@ +#ifndef __AMUSE_ENTITY_HPP__ +#define __AMUSE_ENTITY_HPP__ + +namespace amuse +{ +class Engine; + +/** Common 'engine child' class */ +class Entity +{ +protected: + Engine& m_engine; + int m_groupId; +public: + Entity(Engine& engine, int groupId) + : m_engine(engine), m_groupId(groupId) {} + + Engine& getEngine() {return m_engine;} + int getGroupId() {return m_groupId;} +}; + +} + +#endif // __AMUSE_ENTITY_HPP__ diff --git a/include/amuse/Envelope.hpp b/include/amuse/Envelope.hpp new file mode 100644 index 0000000..274411c --- /dev/null +++ b/include/amuse/Envelope.hpp @@ -0,0 +1,13 @@ +#ifndef __AMUSE_ENVELOPE_HPP__ +#define __AMUSE_ENVELOPE_HPP__ + +namespace amuse +{ + +class Envelope +{ +}; + +} + +#endif // __AMUSE_ENVELOPE_HPP__ diff --git a/include/amuse/IBackendVoice.hpp b/include/amuse/IBackendVoice.hpp new file mode 100644 index 0000000..9671cae --- /dev/null +++ b/include/amuse/IBackendVoice.hpp @@ -0,0 +1,44 @@ +#ifndef __AMUSE_IBACKENDVOICE_HPP__ +#define __AMUSE_IBACKENDVOICE_HPP__ + +namespace amuse +{ + +/** Same channel enums from boo, used for matrix coefficient table index */ +enum class AudioChannel +{ + FrontLeft, + FrontRight, + RearLeft, + RearRight, + FrontCenter, + LFE, + SideLeft, + SideRight, + Unknown = 0xff +}; + +/** + * @brief Client-implemented voice instance + */ +class IBackendVoice +{ +public: + virtual ~IBackendVoice() = default; + + /** Set channel-gains for audio source (AudioChannel enum for array index) */ + virtual void setMatrixCoefficients(const float coefs[8])=0; + + /** Called by client to dynamically adjust the pitch of voices with dynamic pitch enabled */ + virtual void setPitchRatio(double ratio)=0; + + /** Instructs platform to begin consuming sample data; invoking callback as needed */ + virtual void start()=0; + + /** Instructs platform to stop consuming sample data */ + virtual void stop()=0; +}; + +} + +#endif // __AMUSE_IBACKENDVOICE_HPP__ diff --git a/include/amuse/IBackendVoiceAllocator.hpp b/include/amuse/IBackendVoiceAllocator.hpp new file mode 100644 index 0000000..1b8d5e9 --- /dev/null +++ b/include/amuse/IBackendVoiceAllocator.hpp @@ -0,0 +1,25 @@ +#ifndef __AMUSE_IBACKENDVOICEALLOCATOR_HPP__ +#define __AMUSE_IBACKENDVOICEALLOCATOR_HPP__ + +#include + +namespace amuse +{ +class IBackendVoice; +class Voice; + +/** + * @brief Client-implemented voice allocator + */ +class IBackendVoiceAllocator +{ +public: + virtual ~IBackendVoiceAllocator() = default; + + /** Amuse obtains a new voice from the platform this way */ + virtual std::unique_ptr allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch)=0; +}; + +} + +#endif // __AMUSE_IBACKENDVOICEALLOCATOR_HPP__ diff --git a/include/amuse/Listener.hpp b/include/amuse/Listener.hpp new file mode 100644 index 0000000..f7f280b --- /dev/null +++ b/include/amuse/Listener.hpp @@ -0,0 +1,15 @@ +#ifndef __AMUSE_LISTENER_HPP__ +#define __AMUSE_LISTENER_HPP__ + +#include "Entity.hpp" + +namespace amuse +{ + +class Listener : public Entity +{ +}; + +} + +#endif // __AMUSE_LISTENER_HPP__ diff --git a/include/amuse/Sequencer.hpp b/include/amuse/Sequencer.hpp new file mode 100644 index 0000000..12a2b11 --- /dev/null +++ b/include/amuse/Sequencer.hpp @@ -0,0 +1,15 @@ +#ifndef __AMUSE_SEQUENCER_HPP__ +#define __AMUSE_SEQUENCER_HPP__ + +#include "Entity.hpp" + +namespace amuse +{ + +class Sequencer : public Entity +{ +}; + +} + +#endif // __AMUSE_SEQUENCER_HPP__ diff --git a/include/amuse/SoundMacroState.hpp b/include/amuse/SoundMacroState.hpp new file mode 100644 index 0000000..51377ae --- /dev/null +++ b/include/amuse/SoundMacroState.hpp @@ -0,0 +1,13 @@ +#ifndef __AMUSE_SOUNDMACROSTATE_HPP__ +#define __AMUSE_SOUNDMACROSTATE_HPP__ + +namespace amuse +{ + +class SoundMacroState +{ +}; + +} + +#endif // __AMUSE_SOUNDMACROSTATE_HPP__ diff --git a/include/amuse/Voice.hpp b/include/amuse/Voice.hpp new file mode 100644 index 0000000..0abbf31 --- /dev/null +++ b/include/amuse/Voice.hpp @@ -0,0 +1,54 @@ +#ifndef __AMUSE_VOICE_HPP__ +#define __AMUSE_VOICE_HPP__ + +#include +#include +#include +#include "SoundMacroState.hpp" +#include "Entity.hpp" + +namespace amuse +{ +class IBackendVoice; + +/** State of voice over lifetime */ +enum class VoiceState +{ + Playing, + KeyOff, + Finished +}; + +/** Individual source of audio */ +class Voice : public Entity +{ + friend class Engine; + std::unique_ptr m_backendVoice; + SoundMacroState m_state; +public: + Voice(Engine& engine, int groupId); + + /** Request specified count of audio frames (samples) from voice, + * internally advancing the voice stream */ + size_t supplyAudio(size_t frames, int16_t* data); + + /** Get current state of voice */ + VoiceState state() const; + + /** Signals voice to begin fade-out, eventually reaching silence */ + void keyOff(); + + void setVolume(float vol); + void setPanning(float pan); + void setSurroundPanning(float span); + void setPitchBend(float pitch); + void setModulation(float mod); + void setPedal(bool pedal); + void setDoppler(float doppler); + void setReverbVol(float rvol); + +}; + +} + +#endif // __AMUSE_VOICE_HPP__ diff --git a/include/amuse/amuse.hpp b/include/amuse/amuse.hpp new file mode 100644 index 0000000..e701f90 --- /dev/null +++ b/include/amuse/amuse.hpp @@ -0,0 +1,10 @@ +#ifndef __AMUSE_AMUSE_HPP__ +#define __AMUSE_AMUSE_HPP__ + +namespace amuse +{ + + +} + +#endif // __AMUSE_AMUSE_HPP__ diff --git a/lib/AudioGroup.cpp b/lib/AudioGroup.cpp new file mode 100644 index 0000000..a7ee0bb --- /dev/null +++ b/lib/AudioGroup.cpp @@ -0,0 +1,31 @@ +#include "amuse/AudioGroup.hpp" +#include "amuse/AudioGroupData.hpp" + +namespace amuse +{ + +AudioGroup::AudioGroup(int groupId, const AudioGroupData& data) +: m_groupId(groupId), + m_pool(data.getPool()), + m_proj(data.getProj()), + m_sdir(data.getSdir()), + m_samp(data.getSamp()) +{} + +bool AudioGroup::sfxInGroup(int sfxId) const +{ +} + +bool AudioGroup::songInGroup(int songId) const +{ +} + +const AudioGroupSampleDirectory::Entry* AudioGroup::getSfxEntry(int sfxId) const +{ + for (const AudioGroupSampleDirectory::Entry& ent : m_sdir.m_entries) + if (ent.m_sfxId == sfxId) + return &ent; + return nullptr; +} + +} diff --git a/lib/AudioGroupData.cpp b/lib/AudioGroupData.cpp new file mode 100644 index 0000000..eff86f4 --- /dev/null +++ b/lib/AudioGroupData.cpp @@ -0,0 +1,14 @@ +#include "amuse/AudioGroupData.hpp" + +namespace amuse +{ + +IntrusiveAudioGroupData::~IntrusiveAudioGroupData() +{ + delete m_pool; + delete m_proj; + delete m_sdir; + delete m_samp; +} + +} diff --git a/lib/AudioGroupPool.cpp b/lib/AudioGroupPool.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/AudioGroupProject.cpp b/lib/AudioGroupProject.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/AudioGroupSampleDirectory.cpp b/lib/AudioGroupSampleDirectory.cpp new file mode 100644 index 0000000..e7c23a1 --- /dev/null +++ b/lib/AudioGroupSampleDirectory.cpp @@ -0,0 +1,28 @@ +#include "amuse/AudioGroupSampleDirectory.hpp" +#include "amuse/Common.hpp" + +namespace amuse +{ + +void AudioGroupSampleDirectory::Entry::swapBig() +{ + m_sfxId = SBig(m_sfxId); + m_sampleOff = SBig(m_sampleOff); + m_unk = SBig(m_unk); + m_sampleRate = SBig(m_sampleRate); + m_numSamples = SBig(m_numSamples); + m_loopStartSample = SBig(m_loopStartSample); + m_loopLengthSamples = SBig(m_loopLengthSamples); + m_adpcmParmOffset = SBig(m_adpcmParmOffset); +} + +void AudioGroupSampleDirectory::ADPCMParms::swapBig() +{ + m_bytesPerFrame = SBig(m_bytesPerFrame); + m_hist1 = SBig(m_hist1); + m_hist2 = SBig(m_hist2); + for (int i=0 ; i<16 ; ++i) + m_coefs[i] = SBig(m_coefs[i]); +} + +} diff --git a/lib/BooBackend.cpp b/lib/BooBackend.cpp new file mode 100644 index 0000000..f09193e --- /dev/null +++ b/lib/BooBackend.cpp @@ -0,0 +1,49 @@ +#include "amuse/BooBackend.hpp" +#include "amuse/Voice.hpp" + +namespace amuse +{ + +size_t BooBackendVoice::VoiceCallback::supplyAudio(boo::IAudioVoice&, + size_t frames, int16_t* data) +{ + return m_parent.m_clientVox.supplyAudio(frames, data); +} + +BooBackendVoice::BooBackendVoice(boo::IAudioVoiceEngine& engine, Voice& clientVox, + double sampleRate, bool dynamicPitch) +: m_clientVox(clientVox), m_cb(*this), + m_booVoice(engine.allocateNewMonoVoice(sampleRate, &m_cb, dynamicPitch)) +{} + +void BooBackendVoice::setMatrixCoefficients(const float coefs[8]) +{ + m_booVoice->setMonoMatrixCoefficients(coefs); +} + +void BooBackendVoice::setPitchRatio(double ratio) +{ + m_booVoice->setPitchRatio(ratio); +} + +void BooBackendVoice::start() +{ + m_booVoice->start(); +} + +void BooBackendVoice::stop() +{ + m_booVoice->stop(); +} + +BooBackendVoiceAllocator::BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) +: m_booEngine(booEngine) +{} + +std::unique_ptr +BooBackendVoiceAllocator::allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch) +{ + return std::make_unique(m_booEngine, clientVox, sampleRate, dynamicPitch); +} + +} diff --git a/lib/Emitter.cpp b/lib/Emitter.cpp new file mode 100644 index 0000000..2b9790a --- /dev/null +++ b/lib/Emitter.cpp @@ -0,0 +1,11 @@ +#include "amuse/Emitter.hpp" + +namespace amuse +{ + +Emitter::Emitter(Engine& engine, int groupId, Voice& vox) +: Entity(engine, groupId), m_vox(vox) +{ +} + +} diff --git a/lib/Engine.cpp b/lib/Engine.cpp new file mode 100644 index 0000000..6110b50 --- /dev/null +++ b/lib/Engine.cpp @@ -0,0 +1,137 @@ +#include "amuse/Engine.hpp" +#include "amuse/Voice.hpp" +#include "amuse/Sequencer.hpp" +#include "amuse/IBackendVoice.hpp" +#include "amuse/IBackendVoiceAllocator.hpp" +#include "amuse/AudioGroupData.hpp" +#include "amuse/AudioGroup.hpp" + +namespace amuse +{ + +Engine::Engine(IBackendVoiceAllocator& backend) +: m_backend(backend) +{} + +Voice* Engine::_allocateVoice(int groupId, double sampleRate, bool dynamicPitch) +{ + std::unique_ptr vox = std::make_unique(*this, groupId); + vox->m_backendVoice = m_backend.allocateVoice(*vox, sampleRate, dynamicPitch); + Voice* ret = vox.get(); + m_activeVoices.push_back(std::move(vox)); + return ret; +} + +AudioGroup* Engine::_findGroupFromSfxId(int sfxId, const AudioGroupSampleDirectory::Entry*& entOut) const +{ + for (const auto& grp : m_audioGroups) + { + entOut = grp.second->getSfxEntry(sfxId); + if (entOut) + return grp.second.get(); + } + return nullptr; +} + +AudioGroup* Engine::_findGroupFromSongId(int songId) const +{ + for (const auto& grp : m_audioGroups) + if (grp.second->songInGroup(songId)) + return grp.second.get(); + return nullptr; +} + +/** Update all active audio entities and fill OS audio buffers as needed */ +void Engine::pumpEngine() +{ +} + +/** Add audio group data pointers to engine; must remain resident! */ +bool Engine::addAudioGroup(int groupId, const AudioGroupData& data) +{ + std::unique_ptr grp = std::make_unique(groupId, data); + if (!grp) + return false; + m_audioGroups.emplace(std::make_pair(groupId, std::move(grp))); + return true; +} + +/** Remove audio group from engine */ +void Engine::removeAudioGroup(int groupId) +{ + for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ;) + { + if ((*it)->getGroupId() == groupId) + { + it = m_activeVoices.erase(it); + continue; + } + ++it; + } + + for (auto it = m_activeEmitters.begin() ; it != m_activeEmitters.end() ;) + { + if ((*it)->getGroupId() == groupId) + { + it = m_activeEmitters.erase(it); + continue; + } + ++it; + } + + for (auto it = m_activeSequencers.begin() ; it != m_activeSequencers.end() ;) + { + if ((*it)->getGroupId() == groupId) + { + it = m_activeSequencers.erase(it); + continue; + } + ++it; + } + + m_audioGroups.erase(groupId); +} + +/** Start soundFX playing from loaded audio groups */ +Voice* Engine::fxStart(int sfxId, float vol, float pan) +{ + const AudioGroupSampleDirectory::Entry* entry; + AudioGroup* grp = _findGroupFromSfxId(sfxId, entry); + if (!grp) + return nullptr; + + Voice* ret = _allocateVoice(grp->groupId(), entry->m_sampleRate, true); + ret->setVolume(vol); + ret->setPanning(pan); + return ret; +} + +/** Start soundFX playing from loaded audio groups, attach to positional emitter */ +Emitter* Engine::addEmitter(const Vector3f& pos, const Vector3f& dir, float maxDist, + float falloff, int sfxId, float minVol, float maxVol) +{ + const AudioGroupSampleDirectory::Entry* entry; + AudioGroup* grp = _findGroupFromSfxId(sfxId, entry); + if (!grp) + return nullptr; + + Voice* vox = _allocateVoice(grp->groupId(), entry->m_sampleRate, true); + std::unique_ptr emtr = std::make_unique(*this, grp->groupId(), *vox); + Emitter* ret = emtr.get(); + ret->setPos(pos); + ret->setDir(dir); + ret->setMaxDist(maxDist); + ret->setFalloff(falloff); + ret->setMinVol(minVol); + ret->setMaxVol(maxVol); + + m_activeEmitters.push_back(std::move(emtr)); + return ret; +} + +/** Start song playing from loaded audio groups */ +Sequencer* Engine::seqPlay(int songId, const unsigned char* arrData) +{ +} + +} diff --git a/lib/Envelope.cpp b/lib/Envelope.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/Listener.cpp b/lib/Listener.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/Sequencer.cpp b/lib/Sequencer.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/SoundMacroState.cpp b/lib/SoundMacroState.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/Voice.cpp b/lib/Voice.cpp new file mode 100644 index 0000000..230380c --- /dev/null +++ b/lib/Voice.cpp @@ -0,0 +1,16 @@ +#include "amuse/Voice.hpp" +#include "amuse/IBackendVoice.hpp" + +namespace amuse +{ + +Voice::Voice(Engine& engine, int groupId) +: Entity(engine, groupId) +{ +} + +size_t Voice::supplyAudio(size_t frames, int16_t* data) +{ +} + +}