#ifndef __AMUSE_AUDIOGROUPPOOL_HPP__
#define __AMUSE_AUDIOGROUPPOOL_HPP__

#include <cstdint>
#include <vector>
#include <cmath>
#include <unordered_map>
#include <array>
#include "Entity.hpp"
#include "Common.hpp"
#include "athena/MemoryReader.hpp"

namespace amuse
{
class AudioGroupData;
struct SoundMacroState;
class Voice;

/** Header at the top of the pool file */
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
PoolHeader : BigDNA
{
    AT_DECL_DNA
    Value<atUint32, DNAEn> soundMacrosOffset;
    Value<atUint32, DNAEn> tablesOffset;
    Value<atUint32, DNAEn> keymapsOffset;
    Value<atUint32, DNAEn> layersOffset;
};

/** Header present at the top of each pool object */
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
ObjectHeader : BigDNA
{
    AT_DECL_DNA
    Value<atUint32, DNAEn> size;
    ObjectIdDNA<DNAEn> objectId;
    Seek<2, athena::Current> pad;
};

struct SoundMacro
{
    /** SoundMacro command operations */
    enum class CmdOp : uint8_t
    {
        End,
        Stop,
        SplitKey,
        SplitVel,
        WaitTicks,
        Loop,
        Goto,
        WaitMs,
        PlayMacro,
        SendKeyOff,
        SplitMod,
        PianoPan,
        SetAdsr,
        ScaleVolume,
        Panning,
        Envelope,
        StartSample,
        StopSample,
        KeyOff,
        SplitRnd,
        FadeIn,
        Spanning,
        SetAdsrCtrl,
        RndNote,
        AddNote,
        SetNote,
        LastNote,
        Portamento,
        Vibrato,
        PitchSweep1,
        PitchSweep2,
        SetPitch,
        SetPitchAdsr,
        ScaleVolumeDLS,
        Mod2Vibrange,
        SetupTremolo,
        Return,
        GoSub,
        TrapEvent = 0x28,
        UntrapEvent,
        SendMessage,
        GetMessage,
        GetVid,
        AddAgeCount = 0x30, /* unimplemented */
        SetAgeCount,        /* unimplemented */
        SendFlag,           /* unimplemented */
        PitchWheelR,
        SetPriority = 0x36, /* unimplemented */
        AddPriority, /* unimplemented */
        AgeCntSpeed, /* unimplemented */
        AgeCntVel,   /* unimplemented */
        VolSelect = 0x40,
        PanSelect,
        PitchWheelSelect,
        ModWheelSelect,
        PedalSelect,
        PortamentoSelect,
        ReverbSelect, /* serves as PostASelect */
        SpanSelect,
        DopplerSelect,
        TremoloSelect,
        PreASelect,
        PreBSelect,
        PostBSelect,
        AuxAFXSelect, /* unimplemented */
        AuxBFXSelect, /* unimplemented */
        SetupLFO = 0x50,
        ModeSelect = 0x58,
        SetKeygroup,
        SRCmodeSelect, /* unimplemented */
        AddVars = 0x60,
        SubVars,
        MulVars,
        DivVars,
        AddIVars,
        SetVar,
        IfEqual = 0x70,
        IfLess,
        CmdOpMAX,
        Invalid = 0xff
    };

    enum class CmdType : uint8_t
    {
        Control,
        Pitch,
        Sample,
        Setup,
        Special,
        Structure,
        Volume,
        CmdTypeMAX
    };

    /** Introspection structure used by editors to define user interactivity per command */
    struct CmdIntrospection
    {
        struct Field
        {
            enum class Type
            {
                Invalid,
                Bool,
                Int8,
                UInt8,
                Int16,
                UInt16,
                Int32,
                UInt32,
                SoundMacroId,
                SoundMacroStep,
                TableId,
                SampleId,
                Choice
            };
            Type m_tp;
            size_t m_offset;
            std::string_view m_name;
            int64_t m_min, m_max, m_default;
            std::string_view m_choices[4];
        };
        CmdType m_tp;
        std::string_view m_name;
        std::string_view m_description;
        Field m_fields[7];
    };

    /** Base command interface. All versions of MusyX encode little-endian parameters */
    struct ICmd : LittleDNAV
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        virtual bool Do(SoundMacroState& st, Voice& vox) const = 0;
        virtual CmdOp Isa() const = 0;
    };
    struct CmdEnd : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::End; }
    };
    struct CmdStop : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::Stop; }
    };
    struct CmdSplitKey : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt8> key;
        SoundMacroIdDNA<athena::Little> macro;
        SoundMacroStepDNA<athena::Little> macroStep;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SplitKey; }
    };
    struct CmdSplitVel : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt8> velocity;
        SoundMacroIdDNA<athena::Little> macro;
        SoundMacroStepDNA<athena::Little> macroStep;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SplitVel; }
    };
    struct CmdWaitTicks : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<bool> keyOff;
        Value<bool> random;
        Value<bool> sampleEnd;
        Value<bool> absolute;
        Value<bool> msSwitch;
        Value<atUint16> ticksOrMs;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::WaitTicks; }
    };
    struct CmdLoop : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<bool> keyOff;
        Value<bool> random;
        Value<bool> sampleEnd;
        SoundMacroStepDNA<athena::Little> macroStep;
        Value<atUint16> times;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::Loop; }
    };
    struct CmdGoto : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Seek<1, athena::SeekOrigin::Current> dummy;
        SoundMacroIdDNA<athena::Little> macro;
        SoundMacroStepDNA<athena::Little> macroStep;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::Goto; }
    };
    struct CmdWaitMs : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<bool> keyOff;
        Value<bool> random;
        Value<bool> sampleEnd;
        Value<bool> absolute;
        Seek<1, athena::SeekOrigin::Current> dummy;
        Value<atUint16> ms;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::WaitMs; }
    };
    struct CmdPlayMacro : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt8> addNote;
        SoundMacroIdDNA<athena::Little> macro;
        SoundMacroStepDNA<athena::Little> macroStep;
        Value<atUint8> priority;
        Value<atUint8> maxVoices;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::PlayMacro; }
    };
    struct CmdSendKeyOff : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> variable;
        Value<bool> lastStarted;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SendKeyOff; }
    };
    struct CmdSplitMod : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt8> modValue;
        SoundMacroIdDNA<athena::Little> macro;
        SoundMacroStepDNA<athena::Little> macroStep;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SplitMod; }
    };
    struct CmdPianoPan : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt8> scale;
        Value<atInt8> centerKey;
        Value<atInt8> centerPan;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::PianoPan; }
    };
    struct CmdSetAdsr : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        TableIdDNA<athena::Little> table;
        Value<bool> dlsMode;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SetAdsr; }
    };
    struct CmdScaleVolume : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt8> scale;
        Value<atInt8> add;
        TableIdDNA<athena::Little> table;
        Value<bool> originalVol;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::ScaleVolume; }
    };
    struct CmdPanning : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt8> panPosition;
        Value<atUint16> timeMs;
        Value<atInt8> width;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::Panning; }
    };
    struct CmdEnvelope : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt8> scale;
        Value<atInt8> add;
        TableIdDNA<athena::Little> table;
        Value<bool> msSwitch;
        Value<atUint16> ticksOrMs;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::Envelope; }
    };
    struct CmdStartSample : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        enum class Mode : atInt8
        {
            NoScale = 0,
            Negative = 1,
            Positive = 2
        };
        SampleIdDNA<athena::Little> sample;
        Value<Mode> mode;
        Value<atUint32> offset;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::StartSample; }
    };
    struct CmdStopSample : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::StopSample; }
    };
    struct CmdKeyOff : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::KeyOff; }
    };
    struct CmdSplitRnd : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> rnd;
        SoundMacroIdDNA<athena::Little> macro;
        SoundMacroStepDNA<athena::Little> macroStep;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SplitRnd; }
    };
    struct CmdFadeIn : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt8> scale;
        Value<atInt8> add;
        TableIdDNA<athena::Little> table;
        Value<bool> msSwitch;
        Value<atUint16> ticksOrMs;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::FadeIn; }
    };
    struct CmdSpanning : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt8> spanPosition;
        Value<atUint16> timeMs;
        Value<atInt8> width;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::Spanning; }
    };
    struct CmdSetAdsrCtrl : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> attack;
        Value<atUint8> decay;
        Value<atUint8> sustain;
        Value<atUint8> release;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SetAdsrCtrl; }
    };
    struct CmdRndNote : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt8> noteLo;
        Value<atInt8> detune;
        Value<atInt8> noteHi;
        Value<bool> fixedFree;
        Value<bool> absRel;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::RndNote; }
    };
    struct CmdAddNote : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt8> add;
        Value<atInt8> detune;
        Value<bool> originalKey;
        Seek<1, athena::SeekOrigin::Current> seek;
        Value<bool> msSwitch;
        Value<atUint16> ticksOrMs;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::AddNote; }
    };
    struct CmdSetNote : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt8> key;
        Value<atInt8> detune;
        Seek<2, athena::SeekOrigin::Current> seek;
        Value<bool> msSwitch;
        Value<atUint16> ticksOrMs;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SetNote; }
    };
    struct CmdLastNote : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt8> add;
        Value<atInt8> detune;
        Seek<2, athena::SeekOrigin::Current> seek;
        Value<bool> msSwitch;
        Value<atUint16> ticksOrMs;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::LastNote; }
    };
    struct CmdPortamento : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        enum class PortState : atInt8
        {
            Disable,
            Enable,
            MIDIControlled
        };
        Value<PortState> portState;
        enum class PortType : atInt8
        {
            LastPressed,
            Always
        };
        Value<PortType> portType;
        Seek<2, athena::SeekOrigin::Current> seek;
        Value<bool> msSwitch;
        Value<atUint16> ticksOrMs;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::Portamento; }
    };
    struct CmdVibrato : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> levelNote;
        Value<atUint8> levelFine;
        Value<bool> modwheelFlag;
        Seek<1, athena::SeekOrigin::Current> seek;
        Value<bool> msSwitch;
        Value<atUint16> ticksOrMs;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::Vibrato; }
    };
    struct CmdPitchSweep1 : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt8> times;
        Value<atInt16> add;
        Seek<1, athena::SeekOrigin::Current> seek;
        Value<bool> msSwitch;
        Value<atUint16> ticksOrMs;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::PitchSweep1; }
    };
    struct CmdPitchSweep2 : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt8> times;
        Value<atInt16> add;
        Seek<1, athena::SeekOrigin::Current> seek;
        Value<bool> msSwitch;
        Value<atUint16> ticksOrMs;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::PitchSweep2; }
    };
    struct CmdSetPitch : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        LittleUInt24 hz;
        Value<atUint16> fine;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SetPitch; }
    };
    struct CmdSetPitchAdsr : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        TableIdDNA<athena::Little> table;
        Seek<1, athena::SeekOrigin::Current> seek;
        Value<atInt8> keys;
        Value<atInt8> cents;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SetPitchAdsr; }
    };
    struct CmdScaleVolumeDLS : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt16> scale;
        Value<bool> originalVol;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::ScaleVolumeDLS; }
    };
    struct CmdMod2Vibrange : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt8> keys;
        Value<atInt8> cents;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::Mod2Vibrange; }
    };
    struct CmdSetupTremolo : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt16> scale;
        Seek<1, athena::SeekOrigin::Current> seek;
        Value<atInt16> modwAddScale;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SetupTremolo; }
    };
    struct CmdReturn : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::Return; }
    };
    struct CmdGoSub : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Seek<1, athena::SeekOrigin::Current> seek;
        SoundMacroIdDNA<athena::Little> macro;
        SoundMacroStepDNA<athena::Little> macroStep;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::GoSub; }
    };
    struct CmdTrapEvent : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        enum class EventType : atInt8
        {
            KeyOff,
            SampleEnd,
            MessageRecv
        };
        Value<EventType> event;
        SoundMacroIdDNA<athena::Little> macro;
        SoundMacroStepDNA<athena::Little> macroStep;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::TrapEvent; }
    };
    struct CmdUntrapEvent : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<CmdTrapEvent::EventType> event;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::UntrapEvent; }
    };
    struct CmdSendMessage : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<bool> isVar;
        SoundMacroIdDNA<athena::Little> macro;
        Value<atUint8> voiceVar;
        Value<atUint8> valueVar;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SendMessage; }
    };
    struct CmdGetMessage : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> variable;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::GetMessage; }
    };
    struct CmdGetVid : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> variable;
        Value<bool> playMacro;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::GetVid; }
    };
    struct CmdAddAgeCount : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Seek<1, athena::SeekOrigin::Current> seek;
        Value<atInt16> add;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::AddAgeCount; }
    };
    struct CmdSetAgeCount : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Seek<1, athena::SeekOrigin::Current> seek;
        Value<atUint16> counter;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SetAgeCount; }
    };
    struct CmdSendFlag : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> flagId;
        Value<atUint8> value;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SendFlag; }
    };
    struct CmdPitchWheelR : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atInt8> rangeUp;
        Value<atInt8> rangeDown;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::PitchWheelR; }
    };
    struct CmdSetPriority : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> prio;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SetPriority; }
    };
    struct CmdAddPriority : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Seek<1, athena::SeekOrigin::Current> seek;
        Value<atInt16> prio;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::AddPriority; }
    };
    struct CmdAgeCntSpeed : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Seek<3, athena::SeekOrigin::Current> seek;
        Value<atUint32> time;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::AgeCntSpeed; }
    };
    struct CmdAgeCntVel : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Seek<1, athena::SeekOrigin::Current> seek;
        Value<atUint16> ageBase;
        Value<atUint16> ageScale;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::AgeCntVel; }
    };
    enum class Combine : atInt8
    {
        Set,
        Add,
        Mult
    };
    struct CmdVolSelect : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> midiControl;
        Value<atInt16> scalingPercentage;
        Value<Combine> combine;
        Value<bool> isVar;
        Value<atInt8> fineScaling;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::VolSelect; }
    };
    struct CmdPanSelect : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> midiControl;
        Value<atInt16> scalingPercentage;
        Value<Combine> combine;
        Value<bool> isVar;
        Value<atInt8> fineScaling;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::PanSelect; }
    };
    struct CmdPitchWheelSelect : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> midiControl;
        Value<atInt16> scalingPercentage;
        Value<Combine> combine;
        Value<bool> isVar;
        Value<atInt8> fineScaling;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::PitchWheelSelect; }
    };
    struct CmdModWheelSelect : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> midiControl;
        Value<atInt16> scalingPercentage;
        Value<Combine> combine;
        Value<bool> isVar;
        Value<atInt8> fineScaling;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::ModWheelSelect; }
    };
    struct CmdPedalSelect : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> midiControl;
        Value<atInt16> scalingPercentage;
        Value<Combine> combine;
        Value<bool> isVar;
        Value<atInt8> fineScaling;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::PedalSelect; }
    };
    struct CmdPortamentoSelect : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> midiControl;
        Value<atInt16> scalingPercentage;
        Value<Combine> combine;
        Value<bool> isVar;
        Value<atInt8> fineScaling;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::PortamentoSelect; }
    };
    struct CmdReverbSelect : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> midiControl;
        Value<atInt16> scalingPercentage;
        Value<Combine> combine;
        Value<bool> isVar;
        Value<atInt8> fineScaling;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::ReverbSelect; }
    };
    struct CmdSpanSelect : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> midiControl;
        Value<atInt16> scalingPercentage;
        Value<Combine> combine;
        Value<bool> isVar;
        Value<atInt8> fineScaling;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SpanSelect; }
    };
    struct CmdDopplerSelect : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> midiControl;
        Value<atInt16> scalingPercentage;
        Value<Combine> combine;
        Value<bool> isVar;
        Value<atInt8> fineScaling;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::DopplerSelect; }
    };
    struct CmdTremoloSelect : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> midiControl;
        Value<atInt16> scalingPercentage;
        Value<Combine> combine;
        Value<bool> isVar;
        Value<atInt8> fineScaling;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::TremoloSelect; }
    };
    struct CmdPreASelect : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> midiControl;
        Value<atInt16> scalingPercentage;
        Value<Combine> combine;
        Value<bool> isVar;
        Value<atInt8> fineScaling;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::PreASelect; }
    };
    struct CmdPreBSelect : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> midiControl;
        Value<atInt16> scalingPercentage;
        Value<Combine> combine;
        Value<bool> isVar;
        Value<atInt8> fineScaling;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::PreBSelect; }
    };
    struct CmdPostBSelect : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> midiControl;
        Value<atInt16> scalingPercentage;
        Value<Combine> combine;
        Value<bool> isVar;
        Value<atInt8> fineScaling;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::PostBSelect; }
    };
    struct CmdAuxAFXSelect : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> midiControl;
        Value<atInt16> scalingPercentage;
        Value<Combine> combine;
        Value<bool> isVar;
        Value<atInt8> fineScaling;
        Value<atUint8> paramIndex;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::AuxAFXSelect; }
    };
    struct CmdAuxBFXSelect : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> midiControl;
        Value<atInt16> scalingPercentage;
        Value<Combine> combine;
        Value<bool> isVar;
        Value<atInt8> fineScaling;
        Value<atUint8> paramIndex;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::AuxBFXSelect; }
    };
    struct CmdSetupLFO : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> lfoNumber;
        Value<atInt16> periodInMs;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SetupLFO; }
    };
    struct CmdModeSelect : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<bool> dlsVol;
        Value<bool> itd;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::ModeSelect; }
    };
    struct CmdSetKeygroup : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> group;
        Value<bool> killNow;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SetKeygroup; }
    };
    struct CmdSRCmodeSelect : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<atUint8> srcType;
        Value<atUint8> type0SrcFilter;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SRCmodeSelect; }
    };
    struct CmdAddVars : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<bool> varCtrlA;
        Value<atUint8> a;
        Value<bool> varCtrlB;
        Value<atUint8> b;
        Value<bool> varCtrlC;
        Value<atUint8> c;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::AddVars; }
    };
    struct CmdSubVars : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<bool> varCtrlA;
        Value<atInt8> a;
        Value<bool> varCtrlB;
        Value<atInt8> b;
        Value<bool> varCtrlC;
        Value<atInt8> c;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SubVars; }
    };
    struct CmdMulVars : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<bool> varCtrlA;
        Value<atInt8> a;
        Value<bool> varCtrlB;
        Value<atInt8> b;
        Value<bool> varCtrlC;
        Value<atInt8> c;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::MulVars; }
    };
    struct CmdDivVars : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<bool> varCtrlA;
        Value<atInt8> a;
        Value<bool> varCtrlB;
        Value<atInt8> b;
        Value<bool> varCtrlC;
        Value<atInt8> c;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::DivVars; }
    };
    struct CmdAddIVars : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<bool> varCtrlA;
        Value<atInt8> a;
        Value<bool> varCtrlB;
        Value<atInt8> b;
        Value<atInt16> imm;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::AddIVars; }
    };
    struct CmdSetVar : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<bool> varCtrlA;
        Value<atInt8> a;
        Seek<1, athena::Current> pad;
        Value<atInt16> imm;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::SetVar; }
    };
    struct CmdIfEqual : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<bool> varCtrlA;
        Value<atInt8> a;
        Value<bool> varCtrlB;
        Value<atInt8> b;
        Value<bool> notEq;
        SoundMacroStepDNA<athena::Little> macroStep;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::IfEqual; }
    };
    struct CmdIfLess : ICmd
    {
        AT_DECL_DNA_YAML
        AT_DECL_DNAV
        static const CmdIntrospection Introspective;
        Value<bool> varCtrlA;
        Value<atInt8> a;
        Value<bool> varCtrlB;
        Value<atInt8> b;
        Value<bool> notLt;
        SoundMacroStepDNA<athena::Little> macroStep;
        bool Do(SoundMacroState& st, Voice& vox) const;
        CmdOp Isa() const { return CmdOp::IfLess; }
    };

    template <class Op, class O, class... _Args>
    static O CmdDo(_Args&&... args);
    static std::unique_ptr<SoundMacro::ICmd> MakeCmd(CmdOp op);
    static const CmdIntrospection* GetCmdIntrospection(CmdOp op);
    static std::string_view CmdOpToStr(CmdOp op);
    static CmdOp CmdStrToOp(std::string_view op);

    std::vector<std::unique_ptr<ICmd>> m_cmds;
    int assertPC(int pc) const;
    
    const ICmd& getCmd(int i) const { return *m_cmds[assertPC(i)]; }

    template <athena::Endian DNAE>
    void readCmds(athena::io::IStreamReader& r, uint32_t size);
    template <athena::Endian DNAE>
    void writeCmds(athena::io::IStreamWriter& w) const;

    ICmd* insertNewCmd(int idx, CmdOp op)
    {
        return m_cmds.insert(m_cmds.begin() + idx, MakeCmd(op))->get();
    }
    ICmd* insertCmd(int idx, std::unique_ptr<ICmd>&& cmd)
    {
        return m_cmds.insert(m_cmds.begin() + idx, std::move(cmd))->get();
    }
    std::unique_ptr<ICmd> deleteCmd(int idx)
    {
        std::unique_ptr<ICmd> ret = std::move(m_cmds[idx]);
        m_cmds.erase(m_cmds.begin() + idx);
        return ret;
    }
    void swapPositions(int a, int b)
    {
        if (a == b)
            return;
        std::swap(m_cmds[a], m_cmds[b]);
    }
    void buildFromPrototype(const SoundMacro& other);

    void toYAML(athena::io::YAMLDocWriter& w) const;
    void fromYAML(athena::io::YAMLDocReader& r, size_t cmdCount);
};

template <typename T>
static inline T& AccessField(SoundMacro::ICmd* cmd, const SoundMacro::CmdIntrospection::Field& field)
{
    return *reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(std::addressof(*cmd)) + field.m_offset);
}

/** Converts time-cents representation to seconds */
static inline double TimeCentsToSeconds(int32_t tc)
{
    if (uint32_t(tc) == 0x80000000)
        return 0.0;
    return std::exp2(tc / (1200.0 * 65536.0));
}

/** Converts seconds representation to time-cents */
static inline int32_t SecondsToTimeCents(double sec)
{
    if (sec == 0.0)
        return 0x80000000;
    return int32_t(std::log2(sec) * (1200.0 * 65536.0));
}

/** Polymorphic interface for representing table data */
struct ITable : LittleDNAV
{
    AT_DECL_DNA_YAML
    AT_DECL_DNAV
    enum class Type
    {
        ADSR,
        ADSRDLS,
        Curve
    };
    virtual Type Isa() const = 0;
};

/** Defines phase-based volume curve for macro volume control */
struct ADSR : ITable
{
    AT_DECL_DNA_YAML
    AT_DECL_DNAV
    Value<atUint16> attack = 0;
    Value<atUint16> decay = 0x8000;
    Value<atUint16> sustain = 0;     /* 0x1000 == 100% */
    Value<atUint16> release = 0;     /* milliseconds */

    double getAttack() const { return attack / 1000.0; }
    void setAttack(double v) { attack = v * 1000.0; }
    double getDecay() const { return (decay == 0x8000) ? 0.0 : (decay / 1000.0); }
    void setDecay(double v) { decay = v == 0.0 ? 0x8000 : v * 1000.0; }
    double getSustain() const { return sustain / double(0x1000); }
    void setSustain(double v) { sustain = v * double(0x1000); }
    double getRelease() const { return release / 1000.0; }
    void setRelease(double v) { release = v * 1000.0; }

    Type Isa() const { return ITable::Type::ADSR; }
};

/** Defines phase-based volume curve for macro volume control (modified DLS standard) */
struct ADSRDLS : ITable
{
    AT_DECL_DNA_YAML
    AT_DECL_DNAV
    Value<atUint32> attack = 0x80000000; /* 16.16 Time-cents */
    Value<atUint32> decay = 0x80000000;  /* 16.16 Time-cents */
    Value<atUint16> sustain = 0;     /* 0x1000 == 100% */
    Value<atUint16> release = 0;     /* milliseconds */
    Value<atUint32> velToAttack = 0x80000000; /* 16.16, 1000.0 == 100%; attack = <attack> + (vel/128) * <velToAttack> */
    Value<atUint32> keyToDecay = 0x80000000;  /* 16.16, 1000.0 == 100%; decay = <decay> + (note/128) * <keyToDecay> */

    double getAttack() const { return TimeCentsToSeconds(attack); }
    void setAttack(double v) { attack = SecondsToTimeCents(v); }
    double getDecay() const { return TimeCentsToSeconds(decay); }
    void setDecay(double v) { decay = SecondsToTimeCents(v); }
    double getSustain() const { return sustain / double(0x1000); }
    void setSustain(double v) { sustain = v * double(0x1000); }
    double getRelease() const { return release / 1000.0; }
    void setRelease(double v) { release = v * 1000.0; }

    double _getVelToAttack() const
    {
        if (velToAttack == 0x80000000)
            return 0.0;
        else
            return velToAttack / 65536.0 / 1000.0;
    }

    void _setVelToAttack(double v)
    {
        if (v == 0.0)
            velToAttack = 0x80000000;
        else
            velToAttack = atUint32(v * 1000.0 * 65536.0);
    }

    double _getKeyToDecay() const
    {
        if (keyToDecay == 0x80000000)
            return 0.0;
        else
            return keyToDecay / 65536.0 / 1000.0;
    }

    void _setKeyToDecay(double v)
    {
        if (v == 0.0)
            keyToDecay = 0x80000000;
        else
            keyToDecay = atUint32(v * 1000.0 * 65536.0);
    }

    double getVelToAttack(int8_t vel) const
    {
        if (velToAttack == 0x80000000)
            return getAttack();
        return getAttack() + vel * (velToAttack / 65536.0 / 1000.0) / 128.0;
    }

    double getKeyToDecay(int8_t note) const
    {
        if (keyToDecay == 0x80000000)
            return getDecay();
        return getDecay() + note * (keyToDecay / 65536.0 / 1000.0) / 128.0;
    }

    Type Isa() const { return ITable::Type::ADSRDLS; }
};

/** Defines arbitrary data for use as volume curve */
struct Curve : ITable
{
    AT_DECL_EXPLICIT_DNA_YAML
    AT_DECL_DNAV
    std::vector<uint8_t> data;

    Type Isa() const { return ITable::Type::Curve; }
};

/** Maps individual MIDI keys to sound-entity as indexed in table
 *  (macro-voice, keymap, layer) */
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
KeymapDNA : BigDNA
{
    AT_DECL_DNA
    SoundMacroIdDNA<DNAEn> macro;
    Value<atInt8> transpose;
    Value<atInt8> pan; /* -128 for surround-channel only */
    Value<atInt8> prioOffset;
    Seek<3, athena::Current> pad;
};
struct Keymap : BigDNA
{
    AT_DECL_DNA_YAML
    SoundMacroIdDNA<athena::Big> macro;
    Value<atInt8> transpose = 0;
    Value<atInt8> pan = 64; /* -128 for surround-channel only */
    Value<atInt8> prioOffset = 0;

    Keymap() = default;

    template <athena::Endian DNAE>
    Keymap(const KeymapDNA<DNAE>& in)
        : macro(in.macro.id), transpose(in.transpose), pan(in.pan),
          prioOffset(in.prioOffset) {}

    template <athena::Endian DNAEn>
    KeymapDNA<DNAEn> toDNA() const
    {
        KeymapDNA<DNAEn> ret;
        ret.macro.id = macro;
        ret.transpose = transpose;
        ret.pan = pan;
        ret.prioOffset = prioOffset;
        return ret;
    }

    uint64_t configKey() const
    {
        return uint64_t(macro.id) | (uint64_t(transpose) << 16) | (uint64_t(pan) << 24) | (uint64_t(prioOffset) << 32);
    }
};

/** Maps ranges of MIDI keys to sound-entity (macro-voice, keymap, layer) */
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
LayerMappingDNA : BigDNA
{
    AT_DECL_DNA
    SoundMacroIdDNA<DNAEn> macro;
    Value<atInt8> keyLo;
    Value<atInt8> keyHi;
    Value<atInt8> transpose;
    Value<atInt8> volume;
    Value<atInt8> prioOffset;
    Value<atInt8> span;
    Value<atInt8> pan;
    Seek<3, athena::Current> pad;
};
struct LayerMapping : BigDNA
{
    AT_DECL_DNA_YAML
    SoundMacroIdDNA<athena::Big> macro;
    Value<atInt8> keyLo = 0;
    Value<atInt8> keyHi = 127;
    Value<atInt8> transpose = 0;
    Value<atInt8> volume = 127;
    Value<atInt8> prioOffset = 0;
    Value<atInt8> span = 0;
    Value<atInt8> pan = 64;

    LayerMapping() = default;

    template <athena::Endian DNAE>
    LayerMapping(const LayerMappingDNA<DNAE>& in)
        : macro(in.macro.id), keyLo(in.keyLo), keyHi(in.keyHi),
          transpose(in.transpose), volume(in.volume), prioOffset(in.prioOffset),
          span(in.span), pan(in.pan) {}

    template <athena::Endian DNAEn>
    LayerMappingDNA<DNAEn> toDNA() const
    {
        LayerMappingDNA<DNAEn> ret;
        ret.macro.id = macro;
        ret.keyLo = keyLo;
        ret.keyHi = keyHi;
        ret.transpose = transpose;
        ret.volume = volume;
        ret.prioOffset = prioOffset;
        ret.span = span;
        ret.pan = pan;
        return ret;
    }
};

/** Database of functional objects within Audio Group */
class AudioGroupPool
{
    std::unordered_map<SoundMacroId, ObjToken<SoundMacro>> m_soundMacros;
    std::unordered_map<TableId, ObjToken<std::unique_ptr<ITable>>> m_tables;
    std::unordered_map<KeymapId, ObjToken<std::array<Keymap, 128>>> m_keymaps;
    std::unordered_map<LayersId, ObjToken<std::vector<LayerMapping>>> m_layers;

    template <athena::Endian DNAE>
    static AudioGroupPool _AudioGroupPool(athena::io::IStreamReader& r);
public:
    AudioGroupPool() = default;
    static AudioGroupPool CreateAudioGroupPool(const AudioGroupData& data);
    static AudioGroupPool CreateAudioGroupPool(SystemStringView groupPath);

    const std::unordered_map<SoundMacroId, ObjToken<SoundMacro>>& soundMacros() const { return m_soundMacros; }
    const std::unordered_map<TableId, ObjToken<std::unique_ptr<ITable>>>& tables() const { return m_tables; }
    const std::unordered_map<KeymapId, ObjToken<std::array<Keymap, 128>>>& keymaps() const { return m_keymaps; }
    const std::unordered_map<LayersId, ObjToken<std::vector<LayerMapping>>>& layers() const { return m_layers; }
    std::unordered_map<SoundMacroId, ObjToken<SoundMacro>>& soundMacros() { return m_soundMacros; }
    std::unordered_map<TableId, ObjToken<std::unique_ptr<ITable>>>& tables() { return m_tables; }
    std::unordered_map<KeymapId, ObjToken<std::array<Keymap, 128>>>& keymaps() { return m_keymaps; }
    std::unordered_map<LayersId, ObjToken<std::vector<LayerMapping>>>& layers() { return m_layers; }

    const SoundMacro* soundMacro(ObjectId id) const;
    const Keymap* keymap(ObjectId id) const;
    const std::vector<LayerMapping>* layer(ObjectId id) const;
    const ADSR* tableAsAdsr(ObjectId id) const;
    const ADSRDLS* tableAsAdsrDLS(ObjectId id) const;
    const Curve* tableAsCurves(ObjectId id) const;

    bool toYAML(SystemStringView groupPath) const;
    template <athena::Endian DNAE>
    bool toData(SystemStringView groupPath) const;

    AudioGroupPool(const AudioGroupPool&) = delete;
    AudioGroupPool& operator=(const AudioGroupPool&) = delete;
    AudioGroupPool(AudioGroupPool&&) = default;
    AudioGroupPool& operator=(AudioGroupPool&&) = default;
};
}

#endif // __AMUSE_AUDIOGROUPPOOL_HPP__
