#pragma once #include #include #include #include #include "amuse/Common.hpp" #include namespace amuse { class AudioGroupData; class AudioGroupPool; class AudioGroupSampleDirectory; enum class GroupType : atUint16 { Song, SFX }; /** Header at top of project file */ template struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) GroupHeader : BigDNA { AT_DECL_DNA Value groupEndOff; GroupIdDNA groupId; Value type; Value soundMacroIdsOff; Value samplIdsOff; Value tableIdsOff; Value keymapIdsOff; Value layerIdsOff; Value pageTableOff; Value drumTableOff; Value midiSetupsOff; }; /** Common index members of SongGroups and SFXGroups */ struct AudioGroupIndex {}; /** Root index of SongGroup */ struct SongGroupIndex : AudioGroupIndex { /** Maps GM program numbers to sound entities */ template struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) PageEntryDNA : BigDNA { AT_DECL_DNA_YAML PageObjectIdDNA objId; Value priority; Value maxVoices; Value programNo; Seek<1, athena::SeekOrigin::Current> pad; }; template struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) MusyX1PageEntryDNA : BigDNA { AT_DECL_DNA PageObjectIdDNA objId; Value priority; Value maxVoices; Value unk; Value programNo; Seek<2, athena::SeekOrigin::Current> pad; }; struct PageEntry : BigDNA { AT_DECL_DNA_YAML PageObjectIdDNA objId; Value priority = 0; Value maxVoices = 255; PageEntry() = default; template PageEntry(const PageEntryDNA& in) : objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices) {} template PageEntry(const MusyX1PageEntryDNA& in) : objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices) {} template PageEntryDNA toDNA(uint8_t programNo) const { PageEntryDNA ret; ret.objId = objId; ret.priority = priority; ret.maxVoices = maxVoices; ret.programNo = programNo; return ret; } }; std::unordered_map m_normPages; std::unordered_map m_drumPages; /** Maps SongID to 16 MIDI channel numbers to GM program numbers and settings */ struct MusyX1MIDISetup : BigDNA { AT_DECL_DNA_YAML Value programNo; Value volume; Value panning; Value reverb; Value chorus; Seek<3, athena::SeekOrigin::Current> pad; }; struct MIDISetup : BigDNA { AT_DECL_DNA_YAML Value programNo = 0; Value volume = 127; Value panning = 64; Value reverb = 0; Value chorus = 0; MIDISetup() = default; MIDISetup(const MusyX1MIDISetup& setup) : programNo(setup.programNo) , volume(setup.volume) , panning(setup.panning) , reverb(setup.reverb) , chorus(setup.chorus) {} }; std::unordered_map> m_midiSetups; void toYAML(athena::io::YAMLDocWriter& w) const; void fromYAML(athena::io::YAMLDocReader& r); }; /** Root index of SFXGroup */ struct SFXGroupIndex : AudioGroupIndex { /** Maps game-side SFX define IDs to sound entities */ template struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) SFXEntryDNA : BigDNA { AT_DECL_DNA SFXIdDNA sfxId; PageObjectIdDNA objId; Value priority; Value maxVoices; Value defVel; Value panning; Value defKey; Seek<1, athena::SeekOrigin::Current> pad; }; struct SFXEntry : BigDNA { AT_DECL_DNA_YAML PageObjectIdDNA objId; Value priority = 0; Value maxVoices = 255; Value defVel = 127; Value panning = 64; Value defKey = 60; SFXEntry() = default; template SFXEntry(const SFXEntryDNA& in) : objId(in.objId.id) , priority(in.priority) , maxVoices(in.maxVoices) , defVel(in.defVel) , panning(in.panning) , defKey(in.defKey) {} template SFXEntryDNA toDNA(SFXId id) const { SFXEntryDNA ret; ret.sfxId.id = id; ret.objId = objId; ret.priority = priority; ret.maxVoices = maxVoices; ret.defVel = defVel; ret.panning = panning; ret.defKey = defKey; return ret; } }; std::unordered_map m_sfxEntries; SFXGroupIndex() = default; SFXGroupIndex(const SFXGroupIndex& other); void toYAML(athena::io::YAMLDocWriter& w) const; void fromYAML(athena::io::YAMLDocReader& r); }; /** Collection of SongGroup and SFXGroup indexes */ class AudioGroupProject { std::unordered_map> m_songGroups; std::unordered_map> m_sfxGroups; AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag); template static AudioGroupProject _AudioGroupProject(athena::io::IStreamReader& r, bool absOffs); static void BootstrapObjectIDs(athena::io::IStreamReader& r, GCNDataTag); template static void BootstrapObjectIDs(athena::io::IStreamReader& r, bool absOffs); public: AudioGroupProject() = default; static AudioGroupProject CreateAudioGroupProject(const AudioGroupData& data); static AudioGroupProject CreateAudioGroupProject(SystemStringView groupPath); static AudioGroupProject CreateAudioGroupProject(const AudioGroupProject& oldProj); static void BootstrapObjectIDs(const AudioGroupData& data); const SongGroupIndex* getSongGroupIndex(GroupId groupId) const; const SFXGroupIndex* getSFXGroupIndex(GroupId groupId) const; const std::unordered_map>& songGroups() const { return m_songGroups; } const std::unordered_map>& sfxGroups() const { return m_sfxGroups; } std::unordered_map>& songGroups() { return m_songGroups; } std::unordered_map>& sfxGroups() { return m_sfxGroups; } std::vector toYAML() const; std::vector toGCNData(const AudioGroupPool& pool, const AudioGroupSampleDirectory& sdir) const; AudioGroupProject(const AudioGroupProject&) = delete; AudioGroupProject& operator=(const AudioGroupProject&) = delete; AudioGroupProject(AudioGroupProject&&) = default; AudioGroupProject& operator=(AudioGroupProject&&) = default; }; } // namespace amuse