diff --git a/CMakeLists.txt b/CMakeLists.txt index 60c97fd..c792c6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ endif() atdna(atdna_AudioGroupPool.cpp include/amuse/AudioGroupPool.hpp) atdna(atdna_AudioGroupProject.cpp include/amuse/AudioGroupProject.hpp) +atdna(atdna_AudioGroupSampleDirectory.cpp include/amuse/AudioGroupSampleDirectory.hpp) set(SOURCES lib/AudioGroup.cpp @@ -45,7 +46,8 @@ set(SOURCES lib/DSPCodec.cpp lib/N64MusyXCodec.cpp atdna_AudioGroupPool.cpp - atdna_AudioGroupProject.cpp) + atdna_AudioGroupProject.cpp + atdna_AudioGroupSampleDirectory.cpp) set(HEADERS include/amuse/AudioGroup.hpp diff --git a/Editor/MainWindow.cpp b/Editor/MainWindow.cpp index 52a097b..20093a9 100644 --- a/Editor/MainWindow.cpp +++ b/Editor/MainWindow.cpp @@ -211,7 +211,6 @@ void MainWindow::importAction() QFileInfo fInfo(path); QString newPath = QFileInfo(fInfo.dir(), newName).filePath(); - printf("%s\n", newPath.toUtf8().data()); if (!MkPath(fInfo.dir(), newName, this)) return; if (!setProjectPath(newPath)) @@ -227,7 +226,7 @@ void MainWindow::importAction() auto data = amuse::ContainerRegistry::LoadContainer(QStringToSysString(dir.filePath(fPath)).c_str()); for (auto& p : data) if (!m_projectModel->importGroupData(SysStringToQString(p.first), std::move(p.second), - ProjectModel::ImportMode(impMode), this)) + ProjectModel::ImportMode(impMode))) return; } m_projectModel->saveToFile(this); @@ -256,7 +255,7 @@ void MainWindow::importAction() auto data = amuse::ContainerRegistry::LoadContainer(QStringToSysString(path).c_str()); for (auto& p : data) if (!m_projectModel->importGroupData(SysStringToQString(p.first), std::move(p.second), - ProjectModel::ImportMode(impMode), this)) + ProjectModel::ImportMode(impMode))) return; m_projectModel->saveToFile(this); @@ -328,7 +327,7 @@ void MainWindow::onFocusChanged(QWidget* old, QWidget* now) if (QLineEdit* le = qobject_cast(now)) { m_undoConn = connect(m_ui.actionUndo, SIGNAL(triggered()), le, SLOT(undo())); - m_canUndoConn = connect(le, SIGNAL(textChanged()), this, SLOT(onTextEdited())); + m_canUndoConn = connect(le, SIGNAL(textChanged(const QString&)), this, SLOT(onTextEdited())); m_ui.actionUndo->setEnabled(le->isUndoAvailable()); m_redoConn = connect(m_ui.actionRedo, SIGNAL(triggered()), le, SLOT(redo())); m_ui.actionRedo->setEnabled(le->isRedoAvailable()); diff --git a/Editor/ProjectModel.cpp b/Editor/ProjectModel.cpp index 7add2ff..489fb70 100644 --- a/Editor/ProjectModel.cpp +++ b/Editor/ProjectModel.cpp @@ -17,111 +17,40 @@ ProjectModel::ProjectGroup::ProjectGroup(amuse::IntrusiveAudioGroupData&& data) m_sdir(amuse::AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(m_data)) {} -bool ProjectModel::importGroupData(const QString& groupName, - amuse::IntrusiveAudioGroupData&& data, - ImportMode mode, QWidget* parent) +bool ProjectModel::importGroupData(const QString& groupName, amuse::IntrusiveAudioGroupData&& data, ImportMode mode) { - amuse::SongId::CurNameDB = &m_songDb; - amuse::SongId::CurNameDB = &m_sfxDb; + setIdDatabases(); ProjectGroup& grp = m_groups.insert(std::make_pair(groupName, std::move(data))).first->second; - - for (const auto& p : grp.m_proj.songGroups()) - { - for (const auto& song : p.second.m_midiSetups) - { - char name[16]; - snprintf(name, 16, "song%d", song.first.id); - m_songDb.registerPair(name, song.first); - } - } - - for (const auto& p : grp.m_proj.sfxGroups()) - { - for (const auto& sfx : p.second.m_sfxEntries) - { - char name[16]; - snprintf(name, 16, "sfx%d", sfx.first.id); - m_sfxDb.registerPair(name, sfx.first); - } - } + grp.setIdDatabases(); + amuse::AudioGroupProject::BootstrapObjectIDs(grp.m_data); return true; } bool ProjectModel::saveToFile(QWidget* parent) { - amuse::SongId::CurNameDB = &m_songDb; - amuse::SongId::CurNameDB = &m_sfxDb; + setIdDatabases(); if (!MkPath(m_dir.path(), parent)) return false; for (auto& g : m_groups) { - athena::io::YAMLDocWriter w("amuse::Group"); - QDir dir(QFileInfo(m_dir, g.first).filePath()); if (!MkPath(dir.path(), parent)) return false; - if (auto __v = w.enterSubVector("songGroups")) + g.second.setIdDatabases(); { - for (const auto& p : g.second.m_proj.songGroups()) - { - if (auto __r = w.enterSubRecord(nullptr)) - { - if (auto __v2 = w.enterSubRecord("normPages")) - { - for (const auto& pg : p.second.m_normPages) - { - char name[16]; - snprintf(name, 16, "%d", pg.first); - if (auto __r2 = w.enterSubRecord(name)) - pg.second.toDNA(pg.first).write(w); - } - } - if (auto __v2 = w.enterSubRecord("drumPages")) - { - for (const auto& pg : p.second.m_drumPages) - { - char name[16]; - snprintf(name, 16, "%d", pg.first); - if (auto __r2 = w.enterSubRecord(name)) - pg.second.toDNA(pg.first).write(w); - } - } - if (auto __v2 = w.enterSubRecord("songs")) - { - for (const auto& song : p.second.m_midiSetups) - { - if (auto __v3 = w.enterSubVector(m_songDb.resolveNameFromId(song.first).data())) - for (int i = 0; i < 16; ++i) - if (auto __r2 = w.enterSubRecord(nullptr)) - song.second[i].write(w); - } - } - } - } + athena::io::FileWriter fo(QStringToSysString(dir.filePath("!project.yaml"))); + g.second.m_proj.toYAML(fo); } - - if (auto __v = w.enterSubVector("sfxGroups")) { - for (const auto& p : g.second.m_proj.sfxGroups()) - { - if (auto __r = w.enterSubRecord(nullptr)) - { - for (const auto& sfx : p.second.m_sfxEntries) - { - if (auto __r2 = w.enterSubRecord(m_sfxDb.resolveNameFromId(sfx.first).data())) - sfx.second.toDNA(sfx.first).write(w); - } - } - } + athena::io::FileWriter fo(QStringToSysString(dir.filePath("!pool.yaml"))); + g.second.m_pool.toYAML(fo); } - - athena::io::FileWriter fo(QStringToSysString(dir.filePath("project.yaml"))); - w.finish(&fo); + //g.second.m_sdir.sampleEntries() } return true; diff --git a/Editor/ProjectModel.hpp b/Editor/ProjectModel.hpp index abbb5e0..c36e78b 100644 --- a/Editor/ProjectModel.hpp +++ b/Editor/ProjectModel.hpp @@ -25,8 +25,21 @@ public: amuse::AudioGroupProject m_proj; amuse::AudioGroupPool m_pool; amuse::AudioGroupSampleDirectory m_sdir; + amuse::NameDB m_soundMacroDb; + amuse::NameDB m_sampleDb; + amuse::NameDB m_tableDb; + amuse::NameDB m_keymapDb; + amuse::NameDB m_layersDb; explicit ProjectGroup(amuse::IntrusiveAudioGroupData&& data); + void setIdDatabases() + { + amuse::SoundMacroId::CurNameDB = &m_soundMacroDb; + amuse::SampleId::CurNameDB = &m_sampleDb; + amuse::TableId::CurNameDB = &m_tableDb; + amuse::KeymapId::CurNameDB = &m_keymapDb; + amuse::LayersId::CurNameDB = &m_layersDb; + } }; private: @@ -36,11 +49,16 @@ private: amuse::NameDB m_sfxDb; std::map m_groups; + void setIdDatabases() + { + amuse::SongId::CurNameDB = &m_songDb; + amuse::SFXId::CurNameDB = &m_sfxDb; + } + public: explicit ProjectModel(const QString& path, QObject* parent = Q_NULLPTR); - bool importGroupData(const QString& groupName, amuse::IntrusiveAudioGroupData&& data, - ImportMode mode, QWidget* parent); + bool importGroupData(const QString& groupName, amuse::IntrusiveAudioGroupData&& data, ImportMode mode); bool saveToFile(QWidget* parent); QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; diff --git a/include/amuse/AudioGroupPool.hpp b/include/amuse/AudioGroupPool.hpp index 1a9c3c1..8c769b8 100644 --- a/include/amuse/AudioGroupPool.hpp +++ b/include/amuse/AudioGroupPool.hpp @@ -119,8 +119,12 @@ struct SoundMacro AddIVars, IfEqual = 0x70, IfLess, + Invalid = 0xff }; + static std::string_view CmdOpToStr(CmdOp op); + static CmdOp CmdStrToOp(std::string_view op); + /** Base command interface. All versions of MusyX encode little-endian parameters */ struct ICmd : LittleDNAV { @@ -148,7 +152,7 @@ struct SoundMacro AT_DECL_DNA_YAML AT_DECL_DNAV Value key; - ObjectIdDNA macro; + SoundMacroIdDNA macro; Value macroStep; bool Do(SoundMacroState& st, Voice& vox) const; CmdOp Isa() const { return CmdOp::SplitKey; } @@ -158,7 +162,7 @@ struct SoundMacro AT_DECL_DNA_YAML AT_DECL_DNAV Value velocity; - ObjectIdDNA macro; + SoundMacroIdDNA macro; Value macroStep; bool Do(SoundMacroState& st, Voice& vox) const; CmdOp Isa() const { return CmdOp::SplitVel; } @@ -193,7 +197,7 @@ struct SoundMacro AT_DECL_DNA_YAML AT_DECL_DNAV Seek<1, athena::SeekOrigin::Current> dummy; - ObjectIdDNA macro; + SoundMacroIdDNA macro; Value macroStep; bool Do(SoundMacroState& st, Voice& vox) const; CmdOp Isa() const { return CmdOp::Goto; } @@ -216,7 +220,7 @@ struct SoundMacro AT_DECL_DNA_YAML AT_DECL_DNAV Value addNote; - ObjectIdDNA macro; + SoundMacroIdDNA macro; Value macroStep; Value priority; Value maxVoices; @@ -237,7 +241,7 @@ struct SoundMacro AT_DECL_DNA_YAML AT_DECL_DNAV Value modValue; - ObjectIdDNA macro; + SoundMacroIdDNA macro; Value macroStep; Value priority; Value maxVoices; @@ -258,7 +262,7 @@ struct SoundMacro { AT_DECL_DNA_YAML AT_DECL_DNAV - ObjectIdDNA table; + TableIdDNA table; Value dlsMode; bool Do(SoundMacroState& st, Voice& vox) const; CmdOp Isa() const { return CmdOp::SetAdsr; } @@ -269,7 +273,7 @@ struct SoundMacro AT_DECL_DNAV Value scale; Value add; - ObjectIdDNA table; + TableIdDNA table; Value originalVol; bool Do(SoundMacroState& st, Voice& vox) const; CmdOp Isa() const { return CmdOp::ScaleVolume; } @@ -290,7 +294,7 @@ struct SoundMacro AT_DECL_DNAV Value scale; Value add; - ObjectIdDNA table; + TableIdDNA table; Value msSwitch; Value fadeTime; bool Do(SoundMacroState& st, Voice& vox) const; @@ -325,7 +329,7 @@ struct SoundMacro AT_DECL_DNA_YAML AT_DECL_DNAV Value rnd; - ObjectIdDNA macro; + SoundMacroIdDNA macro; Value macroStep; bool Do(SoundMacroState& st, Voice& vox) const; CmdOp Isa() const { return CmdOp::SplitRnd; } @@ -336,7 +340,7 @@ struct SoundMacro AT_DECL_DNAV Value scale; Value add; - ObjectIdDNA table; + TableIdDNA table; Value msSwitch; Value ticksPerMs; bool Do(SoundMacroState& st, Voice& vox) const; @@ -474,7 +478,7 @@ struct SoundMacro { AT_DECL_DNA_YAML AT_DECL_DNAV - ObjectIdDNA table; + TableIdDNA table; Seek<1, athena::SeekOrigin::Current> seek; Value keys; Value cents; @@ -521,7 +525,7 @@ struct SoundMacro AT_DECL_DNA_YAML AT_DECL_DNAV Seek<1, athena::SeekOrigin::Current> seek; - ObjectIdDNA macro; + SoundMacroIdDNA macro; Value macroStep; bool Do(SoundMacroState& st, Voice& vox) const; CmdOp Isa() const { return CmdOp::GoSub; } @@ -531,7 +535,7 @@ struct SoundMacro AT_DECL_DNA_YAML AT_DECL_DNAV Value event; - ObjectIdDNA macro; + SoundMacroIdDNA macro; Value macroStep; bool Do(SoundMacroState& st, Voice& vox) const; CmdOp Isa() const { return CmdOp::TrapEvent; } @@ -549,7 +553,7 @@ struct SoundMacro AT_DECL_DNA_YAML AT_DECL_DNAV Value isVar; - ObjectIdDNA macro; + SoundMacroIdDNA macro; Value vid; Value variable; bool Do(SoundMacroState& st, Voice& vox) const; @@ -1031,19 +1035,51 @@ struct Curve : ITable /** Maps individual MIDI keys to sound-entity as indexed in table * (macro-voice, keymap, layer) */ -struct Keymap : LittleDNA +template +struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) +KeymapDNA : BigDNA { - AT_DECL_DNA_YAML + AT_DECL_DNA + SoundMacroIdDNA macro; Value transpose; Value pan; /* -128 for surround-channel only */ Value prioOffset; Seek<3, athena::Current> pad; }; - -/** Maps ranges of MIDI keys to sound-entity (macro-voice, keymap, layer) */ -struct LayerMapping : LittleDNA +struct Keymap : BigDNA { AT_DECL_DNA_YAML + SoundMacroIdDNA macro; + Value transpose; + Value pan; /* -128 for surround-channel only */ + Value prioOffset; + + Keymap() = default; + + template + Keymap(const KeymapDNA& in) + : macro(in.macro.id), transpose(in.transpose), pan(in.pan), + prioOffset(in.prioOffset) {} + + template + KeymapDNA toDNA() const + { + KeymapDNA ret; + ret.macro.id = macro; + ret.transpose = transpose; + ret.pan = pan; + ret.prioOffset = prioOffset; + return ret; + } +}; + +/** Maps ranges of MIDI keys to sound-entity (macro-voice, keymap, layer) */ +template +struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) +LayerMappingDNA : BigDNA +{ + AT_DECL_DNA + SoundMacroIdDNA macro; Value keyLo; Value keyHi; Value transpose; @@ -1051,6 +1087,42 @@ struct LayerMapping : LittleDNA Value prioOffset; Value span; Value pan; + Seek<3, athena::Current> pad; +}; +struct LayerMapping : BigDNA +{ + AT_DECL_DNA_YAML + SoundMacroIdDNA macro; + Value keyLo; + Value keyHi; + Value transpose; + Value volume; + Value prioOffset; + Value span; + Value pan; + + LayerMapping() = default; + + template + LayerMapping(const LayerMappingDNA& 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 + LayerMappingDNA toDNA() const + { + LayerMappingDNA 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 */ @@ -1073,6 +1145,8 @@ public: const ADSR* tableAsAdsr(ObjectId id) const; const ADSRDLS* tableAsAdsrDLS(ObjectId id) const { return reinterpret_cast(tableAsAdsr(id)); } const Curve* tableAsCurves(ObjectId id) const { return reinterpret_cast(tableAsAdsr(id)); } + + bool toYAML(athena::io::IStreamWriter& w) const; }; } diff --git a/include/amuse/AudioGroupProject.hpp b/include/amuse/AudioGroupProject.hpp index 621dfea..d5da3a6 100644 --- a/include/amuse/AudioGroupProject.hpp +++ b/include/amuse/AudioGroupProject.hpp @@ -49,7 +49,7 @@ struct SongGroupIndex : AudioGroupIndex PageEntryDNA : BigDNA { AT_DECL_DNA_YAML - ObjectIdDNA objId; + PageObjectIdDNA objId; Value priority; Value maxVoices; Value programNo; @@ -59,19 +59,20 @@ struct SongGroupIndex : AudioGroupIndex struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) MusyX1PageEntryDNA : BigDNA { - AT_DECL_DNA_YAML - ObjectIdDNA objId; + AT_DECL_DNA + PageObjectIdDNA objId; Value priority; Value maxVoices; Value unk; Value programNo; Seek<2, athena::Current> pad; }; - struct PageEntry + struct PageEntry : BigDNA { - ObjectId objId; - atUint8 priority; - atUint8 maxVoices; + AT_DECL_DNA_YAML + PageObjectIdDNA objId; + Value priority; + Value maxVoices; PageEntry() = default; @@ -87,7 +88,7 @@ struct SongGroupIndex : AudioGroupIndex PageEntryDNA toDNA(uint8_t programNo) const { PageEntryDNA ret; - ret.objId.id = objId; + ret.objId = objId; ret.priority = priority; ret.maxVoices = maxVoices; ret.programNo = programNo; @@ -132,9 +133,9 @@ struct SFXGroupIndex : AudioGroupIndex struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) SFXEntryDNA : BigDNA { - AT_DECL_DNA_YAML - SFXIdDNA defineId; - ObjectIdDNA objId; + AT_DECL_DNA + SFXIdDNA sfxId; + SoundMacroIdDNA macro; Value priority; Value maxVoices; Value defVel; @@ -142,28 +143,29 @@ struct SFXGroupIndex : AudioGroupIndex Value defKey; Seek<1, athena::Current> pad; }; - struct SFXEntry + struct SFXEntry : BigDNA { - ObjectId objId; - atUint8 priority; - atUint8 maxVoices; - atUint8 defVel; - atUint8 panning; - atUint8 defKey; + AT_DECL_DNA_YAML + SoundMacroIdDNA macro; + Value priority; + Value maxVoices; + Value defVel; + Value panning; + Value defKey; SFXEntry() = default; template SFXEntry(const SFXEntryDNA& in) - : objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices), + : macro(in.macro.id), priority(in.priority), maxVoices(in.maxVoices), defVel(in.defVel), panning(in.panning), defKey(in.defKey) {} template - SFXEntryDNA toDNA(SFXId defineId) const + SFXEntryDNA toDNA(SFXId id) const { SFXEntryDNA ret; - ret.defineId.id = defineId; - ret.objId.id = objId; + ret.sfxId.id = id; + ret.macro = macro; ret.priority = priority; ret.maxVoices = maxVoices; ret.defVel = defVel; @@ -185,14 +187,21 @@ class AudioGroupProject 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: static AudioGroupProject CreateAudioGroupProject(const AudioGroupData& data); + static void BootstrapObjectIDs(const AudioGroupData& data); const SongGroupIndex* getSongGroupIndex(int groupId) const; const SFXGroupIndex* getSFXGroupIndex(int groupId) const; const std::unordered_map& songGroups() const { return m_songGroups; } const std::unordered_map& sfxGroups() const { return m_sfxGroups; } + + bool toYAML(athena::io::IStreamWriter& w) const; }; } diff --git a/include/amuse/AudioGroupSampleDirectory.hpp b/include/amuse/AudioGroupSampleDirectory.hpp index 9279743..c021275 100644 --- a/include/amuse/AudioGroupSampleDirectory.hpp +++ b/include/amuse/AudioGroupSampleDirectory.hpp @@ -15,19 +15,106 @@ class AudioGroupSampleDirectory friend class AudioGroup; public: + enum class SampleFormat : atUint8 + { + DSP, + DSP2, + PCM, + N64 + }; + + template + struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) + EntryDNA : BigDNA + { + AT_DECL_DNA + SFXIdDNA m_sfxId; + Value m_sampleOff; + Value m_unk; + Value m_pitch; + Seek<1, athena::Current> pad; + Value m_sampleRate; + Value m_numSamples; // Top 8 bits is SampleFormat + Value m_loopStartSample; + Value m_loopLengthSamples; + Value m_adpcmParmOffset; + }; + template + struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) + MusyX1SdirEntry : BigDNA + { + AT_DECL_DNA + SFXIdDNA m_sfxId; + Value m_sampleOff; + Value m_pitchSampleRate; + Value m_numSamples; + Value m_loopStartSample; + Value m_loopLengthSamples; + }; + template + struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) + MusyX1AbsSdirEntry : BigDNA + { + AT_DECL_DNA + SFXIdDNA m_sfxId; + Value m_sampleOff; + Value m_unk; + Value m_pitchSampleRate; + Value m_numSamples; + Value m_loopStartSample; + Value m_loopLengthSamples; + }; 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(); + atUint32 m_sampleOff; + atUint32 m_unk; + atUint8 m_pitch; + atUint16 m_sampleRate; + atUint32 m_numSamples; // Top 8 bits is SampleFormat + atUint32 m_loopStartSample; + atUint32 m_loopLengthSamples; + atUint32 m_adpcmParmOffset; + + Entry() = default; + + template + Entry(const EntryDNA& in) + : m_sampleOff(in.m_sampleOff), m_unk(in.m_unk), m_pitch(in.m_pitch), + m_sampleRate(in.m_sampleRate), m_numSamples(in.m_numSamples), + m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples), + m_adpcmParmOffset(in.m_adpcmParmOffset) {} + + template + Entry(const MusyX1SdirEntry& in) + : m_sampleOff(in.m_sampleOff), m_unk(0), m_pitch(in.m_pitchSampleRate >> 24), + m_sampleRate(in.m_pitchSampleRate & 0xffff), m_numSamples(in.m_numSamples), + m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples), + m_adpcmParmOffset(0) {} + + template + Entry(const MusyX1AbsSdirEntry& in) + : m_sampleOff(in.m_sampleOff), m_unk(in.m_unk), m_pitch(in.m_pitchSampleRate >> 24), + m_sampleRate(in.m_pitchSampleRate & 0xffff), m_numSamples(in.m_numSamples), + m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples), + m_adpcmParmOffset(0) {} + + template + EntryDNA toDNA(SFXId id) const + { + EntryDNA ret; + ret.m_sfxId.id = id; + ret.m_sampleOff = m_sampleOff; + ret.m_unk = m_unk; + ret.m_pitch = m_pitch; + ret.m_sampleRate = m_sampleRate; + ret.m_numSamples = m_numSamples; + ret.m_loopStartSample = m_loopStartSample; + ret.m_loopLengthSamples = m_loopLengthSamples; + ret.m_adpcmParmOffset = m_adpcmParmOffset; + return ret; + } }; + union ADPCMParms { struct DSPParms { @@ -47,15 +134,15 @@ public: }; private: - std::unordered_map> m_entries; + std::unordered_map> m_entries; public: - AudioGroupSampleDirectory(const unsigned char* data, GCNDataTag); - AudioGroupSampleDirectory(const unsigned char* data, const unsigned char* sampData, bool absOffs, N64DataTag); - AudioGroupSampleDirectory(const unsigned char* data, bool absOffs, PCDataTag); + AudioGroupSampleDirectory(athena::io::IStreamReader& r, GCNDataTag); + AudioGroupSampleDirectory(athena::io::IStreamReader& r, const unsigned char* sampData, bool absOffs, N64DataTag); + AudioGroupSampleDirectory(athena::io::IStreamReader& r, bool absOffs, PCDataTag); static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(const AudioGroupData& data); - const std::unordered_map>& sampleEntries() const { return m_entries; } + const std::unordered_map>& sampleEntries() const { return m_entries; } }; } diff --git a/include/amuse/Common.hpp b/include/amuse/Common.hpp index 9c400eb..0e3dec8 100644 --- a/include/amuse/Common.hpp +++ b/include/amuse/Common.hpp @@ -37,7 +37,7 @@ using BigDNAV = athena::io::DNAVYaml; using LittleDNAV = athena::io::DNAVYaml; /** Common ID structure statically tagging - * SoundMacros, Tables, Keymaps, Layers */ + * SoundMacros, Tables, Keymaps, Layers, Samples, SFX, Songs */ struct ObjectId { uint16_t id = 0xffff; @@ -47,7 +47,6 @@ struct ObjectId ObjectId& operator=(uint16_t idIn) { id = idIn; return *this; } static thread_local NameDB* CurNameDB; }; - template struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) ObjectIdDNA : BigDNA @@ -56,57 +55,52 @@ ObjectIdDNA : BigDNA void _read(athena::io::YAMLDocReader& r); void _write(athena::io::YAMLDocWriter& w); ObjectId id; + ObjectIdDNA() = default; + ObjectIdDNA(ObjectId idIn) : id(idIn) {} + operator ObjectId() const { return id; } }; -struct SampleId : ObjectId -{ - using ObjectId::ObjectId; - SampleId(const ObjectId& id) : ObjectId(id) {} - static thread_local NameDB* CurNameDB; +#define DECL_ID_TYPE(type) \ +struct type : ObjectId \ +{ \ + using ObjectId::ObjectId; \ + type(const ObjectId& id) : ObjectId(id) {} \ + static thread_local NameDB* CurNameDB; \ +}; \ +template \ +struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) \ +type##DNA : BigDNA \ +{ \ + AT_DECL_EXPLICIT_DNA_YAML \ + void _read(athena::io::YAMLDocReader& r); \ + void _write(athena::io::YAMLDocWriter& w); \ + type id; \ + type##DNA() = default; \ + type##DNA(type idIn) : id(idIn) {} \ + operator type() const { return id; } \ }; +DECL_ID_TYPE(SoundMacroId) +DECL_ID_TYPE(SampleId) +DECL_ID_TYPE(TableId) +DECL_ID_TYPE(KeymapId) +DECL_ID_TYPE(LayersId) +DECL_ID_TYPE(SongId) +DECL_ID_TYPE(SFXId) +/* MusyX has object polymorphism between Keymaps and Layers when + * referenced by a song group's page object. When the upper bit is set, + * this indicates a layer type. */ template struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) -SampleIdDNA : BigDNA +PageObjectIdDNA : BigDNA { AT_DECL_EXPLICIT_DNA_YAML void _read(athena::io::YAMLDocReader& r); void _write(athena::io::YAMLDocWriter& w); - SampleId id; -}; - -struct SongId : ObjectId -{ - using ObjectId::ObjectId; - SongId(const ObjectId& id) : ObjectId(id) {} - static thread_local NameDB* CurNameDB; -}; - -template -struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) -SongIdDNA : BigDNA -{ - AT_DECL_EXPLICIT_DNA_YAML - void _read(athena::io::YAMLDocReader& r); - void _write(athena::io::YAMLDocWriter& w); - SongId id; -}; - -struct SFXId : ObjectId -{ - using ObjectId::ObjectId; - SFXId(const ObjectId& id) : ObjectId(id) {} - static thread_local NameDB* CurNameDB; -}; - -template -struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) -SFXIdDNA : BigDNA -{ - AT_DECL_EXPLICIT_DNA_YAML - void _read(athena::io::YAMLDocReader& r); - void _write(athena::io::YAMLDocWriter& w); - SFXId id; + ObjectId id; + PageObjectIdDNA() = default; + PageObjectIdDNA(ObjectId idIn) : id(idIn) {} + operator ObjectId() const { return id; } }; struct LittleUInt24 : LittleDNA @@ -422,30 +416,34 @@ struct N64DataTag struct PCDataTag { }; + +template +static std::vector>> SortUnorderedMap(const T& um) +{ + std::vector>> ret(um.cbegin(), um.cend()); + std::sort(ret.begin(), ret.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); + return ret; +} } namespace std { -template<> -struct hash -{ - size_t operator()(const amuse::ObjectId& val) const noexcept { return val.id; } -}; -template<> -struct hash -{ - size_t operator()(const amuse::SampleId& val) const noexcept { return val.id; } -}; -template<> -struct hash -{ - size_t operator()(const amuse::SongId& val) const noexcept { return val.id; } -}; -template<> -struct hash -{ - size_t operator()(const amuse::SFXId& val) const noexcept { return val.id; } +#define DECL_ID_HASH(type) \ +template<> \ +struct hash \ +{ \ + size_t operator()(const amuse::type& val) const noexcept { return val.id; } \ }; +DECL_ID_HASH(ObjectId) +DECL_ID_HASH(SoundMacroId) +DECL_ID_HASH(SampleId) +DECL_ID_HASH(TableId) +DECL_ID_HASH(KeymapId) +DECL_ID_HASH(LayersId) +DECL_ID_HASH(SongId) +DECL_ID_HASH(SFXId) } namespace amuse @@ -454,17 +452,20 @@ struct NameDB { enum class Type { - SoundMacro = 0, - Table = 1, - Keymap = 4, - Layer = 8 + SoundMacro, + Table, + Keymap, + Layer, + Song, + SFX, + Sample }; std::unordered_map m_stringToId; std::unordered_map m_idToString; ObjectId generateId(Type tp); - static std::string generateName(ObjectId id); + static std::string generateName(ObjectId id, Type tp); std::string_view registerPair(std::string_view str, ObjectId id); std::string_view resolveNameFromId(ObjectId id) const; ObjectId resolveIdFromName(std::string_view str) const; diff --git a/include/amuse/Voice.hpp b/include/amuse/Voice.hpp index 30fd2e2..b439697 100644 --- a/include/amuse/Voice.hpp +++ b/include/amuse/Voice.hpp @@ -171,11 +171,11 @@ class Voice : public Entity std::list>::iterator _allocateVoice(double sampleRate, bool dynamicPitch); std::list>::iterator _destroyVoice(std::list>::iterator it); - bool _loadSoundMacro(ObjectId id, const SoundMacro* macroData, int macroStep, double ticksPerSec, + bool _loadSoundMacro(SoundMacroId id, const SoundMacro* macroData, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false); - bool _loadKeymap(ObjectId id, const Keymap* keymap, int macroStep, double ticksPerSec, uint8_t midiKey, + bool _loadKeymap(const Keymap* keymap, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false); - bool _loadLayer(ObjectId id, const std::vector& layer, int macroStep, double ticksPerSec, + bool _loadLayer(const std::vector& layer, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false); std::shared_ptr _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false); @@ -222,10 +222,13 @@ public: /** Allocate parallel macro and tie to voice for possible emitter influence */ std::shared_ptr startChildMacro(int8_t addNote, ObjectId macroId, int macroStep); - /** Load specified Sound Object from within group into voice */ - bool loadSoundObject(ObjectId objectId, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, + /** Load specified SoundMacro Object from within group into voice */ + bool loadMacroObject(SoundMacroId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false); + /** Load specified song page object (Keymap/Layer) from within group into voice */ + bool loadPageObject(ObjectId objectId, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod); + /** Signals voice to begin fade-out (or defer if sustained), eventually reaching silence */ void keyOff(); diff --git a/lib/AudioGroup.cpp b/lib/AudioGroup.cpp index 8ef6d97..a077b42 100644 --- a/lib/AudioGroup.cpp +++ b/lib/AudioGroup.cpp @@ -7,7 +7,7 @@ namespace amuse AudioGroup::AudioGroup(const AudioGroupData& data, GCNDataTag) : m_proj(AudioGroupProject::CreateAudioGroupProject(data)) , m_pool(AudioGroupPool::CreateAudioGroupPool(data)) -, m_sdir(data.getSdir(), GCNDataTag{}) +, m_sdir(AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(data)) , m_samp(data.getSamp()) , m_fmt(DataFormat::GCN) { @@ -16,7 +16,7 @@ AudioGroup::AudioGroup(const AudioGroupData& data, GCNDataTag) AudioGroup::AudioGroup(const AudioGroupData& data, bool absOffs, N64DataTag) : m_proj(AudioGroupProject::CreateAudioGroupProject(data)) , m_pool(AudioGroupPool::CreateAudioGroupPool(data)) -, m_sdir(data.getSdir(), data.getSamp(), absOffs, N64DataTag{}) +, m_sdir(AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(data)) , m_samp(data.getSamp()) , m_fmt(DataFormat::N64) { @@ -25,7 +25,7 @@ AudioGroup::AudioGroup(const AudioGroupData& data, bool absOffs, N64DataTag) AudioGroup::AudioGroup(const AudioGroupData& data, bool absOffs, PCDataTag) : m_proj(AudioGroupProject::CreateAudioGroupProject(data)) , m_pool(AudioGroupPool::CreateAudioGroupPool(data)) -, m_sdir(data.getSdir(), absOffs, PCDataTag{}) +, m_sdir(AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(data)) , m_samp(data.getSamp()) , m_fmt(DataFormat::PC) { diff --git a/lib/AudioGroupPool.cpp b/lib/AudioGroupPool.cpp index bcd0613..513f23f 100644 --- a/lib/AudioGroupPool.cpp +++ b/lib/AudioGroupPool.cpp @@ -5,6 +5,8 @@ #include "athena/MemoryReader.hpp" #include "logvisor/logvisor.hpp" +using namespace std::literals; + namespace amuse { static logvisor::Module Log("amuse::AudioGroupPool"); @@ -75,8 +77,9 @@ AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r) ObjectHeader objHead; atInt64 startPos = r.position(); objHead.read(r); - Keymap& km = ret.m_keymaps[objHead.objectId.id]; - km.read(r); + KeymapDNA kmData; + kmData.read(r); + ret.m_keymaps[objHead.objectId.id] = kmData; r.seek(startPos + objHead.size, athena::Begin); } } @@ -95,8 +98,9 @@ AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r) lm.reserve(count); for (uint32_t i = 0; i < count; ++i) { - lm.emplace_back(); - lm.back().read(r); + LayerMappingDNA lmData; + lmData.read(r); + lm.push_back(lmData); } r.seek(startPos + objHead.size, athena::Begin); } @@ -347,6 +351,408 @@ const ADSR* AudioGroupPool::tableAsAdsr(ObjectId id) const return static_cast(search->second.get()); } +std::string_view SoundMacro::CmdOpToStr(CmdOp op) +{ + switch (op) + { + case CmdOp::End: + return "End"sv; + case CmdOp::Stop: + return "Stop"sv; + case CmdOp::SplitKey: + return "SplitKey"sv; + case CmdOp::SplitVel: + return "SplitVel"sv; + case CmdOp::WaitTicks: + return "WaitTicks"sv; + case CmdOp::Loop: + return "Loop"sv; + case CmdOp::Goto: + return "Goto"sv; + case CmdOp::WaitMs: + return "WaitMs"sv; + case CmdOp::PlayMacro: + return "PlayMacro"sv; + case CmdOp::SendKeyOff: + return "SendKeyOff"sv; + case CmdOp::SplitMod: + return "SplitMod"sv; + case CmdOp::PianoPan: + return "PianoPan"sv; + case CmdOp::SetAdsr: + return "SetAdsr"sv; + case CmdOp::ScaleVolume: + return "ScaleVolume"sv; + case CmdOp::Panning: + return "Panning"sv; + case CmdOp::Envelope: + return "Envelope"sv; + case CmdOp::StartSample: + return "StartSample"sv; + case CmdOp::StopSample: + return "StopSample"sv; + case CmdOp::KeyOff: + return "KeyOff"sv; + case CmdOp::SplitRnd: + return "SplitRnd"sv; + case CmdOp::FadeIn: + return "FadeIn"sv; + case CmdOp::Spanning: + return "Spanning"sv; + case CmdOp::SetAdsrCtrl: + return "SetAdsrCtrl"sv; + case CmdOp::RndNote: + return "RndNote"sv; + case CmdOp::AddNote: + return "AddNote"sv; + case CmdOp::SetNote: + return "SetNote"sv; + case CmdOp::LastNote: + return "LastNote"sv; + case CmdOp::Portamento: + return "Portamento"sv; + case CmdOp::Vibrato: + return "Vibrato"sv; + case CmdOp::PitchSweep1: + return "PitchSweep1"sv; + case CmdOp::PitchSweep2: + return "PitchSweep2"sv; + case CmdOp::SetPitch: + return "SetPitch"sv; + case CmdOp::SetPitchAdsr: + return "SetPitchAdsr"sv; + case CmdOp::ScaleVolumeDLS: + return "ScaleVolumeDLS"sv; + case CmdOp::Mod2Vibrange: + return "Mod2Vibrange"sv; + case CmdOp::SetupTremolo: + return "SetupTremolo"sv; + case CmdOp::Return: + return "Return"sv; + case CmdOp::GoSub: + return "GoSub"sv; + case CmdOp::TrapEvent: + return "TrapEvent"sv; + case CmdOp::UntrapEvent: + return "UntrapEvent"sv; + case CmdOp::SendMessage: + return "SendMessage"sv; + case CmdOp::GetMessage: + return "GetMessage"sv; + case CmdOp::GetVid: + return "GetVid"sv; + case CmdOp::AddAgeCount: + return "AddAgeCount"sv; + case CmdOp::SetAgeCount: + return "SetAgeCount"sv; + case CmdOp::SendFlag: + return "SendFlag"sv; + case CmdOp::PitchWheelR: + return "PitchWheelR"sv; + case CmdOp::SetPriority: + return "SetPriority"sv; + case CmdOp::AddPriority: + return "AddPriority"sv; + case CmdOp::AgeCntSpeed: + return "AgeCntSpeed"sv; + case CmdOp::AgeCntVel: + return "AgeCntVel"sv; + case CmdOp::VolSelect: + return "VolSelect"sv; + case CmdOp::PanSelect: + return "PanSelect"sv; + case CmdOp::PitchWheelSelect: + return "PitchWheelSelect"sv; + case CmdOp::ModWheelSelect: + return "ModWheelSelect"sv; + case CmdOp::PedalSelect: + return "PedalSelect"sv; + case CmdOp::PortamentoSelect: + return "PortamentoSelect"sv; + case CmdOp::ReverbSelect: + return "ReverbSelect"sv; + case CmdOp::SpanSelect: + return "SpanSelect"sv; + case CmdOp::DopplerSelect: + return "DopplerSelect"sv; + case CmdOp::TremoloSelect: + return "TremoloSelect"sv; + case CmdOp::PreASelect: + return "PreASelect"sv; + case CmdOp::PreBSelect: + return "PreBSelect"sv; + case CmdOp::PostBSelect: + return "PostBSelect"sv; + case CmdOp::AuxAFXSelect: + return "AuxAFXSelect"sv; + case CmdOp::AuxBFXSelect: + return "AuxBFXSelect"sv; + case CmdOp::SetupLFO: + return "SetupLFO"sv; + case CmdOp::ModeSelect: + return "ModeSelect"sv; + case CmdOp::SetKeygroup: + return "SetKeygroup"sv; + case CmdOp::SRCmodeSelect: + return "SRCmodeSelect"sv; + case CmdOp::AddVars: + return "AddVars"sv; + case CmdOp::SubVars: + return "SubVars"sv; + case CmdOp::MulVars: + return "MulVars"sv; + case CmdOp::DivVars: + return "DivVars"sv; + case CmdOp::AddIVars: + return "AddIVars"sv; + case CmdOp::IfEqual: + return "IfEqual"sv; + case CmdOp::IfLess: + return "IfLess"sv; + default: + return ""sv; + } +} + +SoundMacro::CmdOp SoundMacro::CmdStrToOp(std::string_view op) +{ + if (!CompareCaseInsensitive(op.data(), "End")) + return CmdOp::End; + else if (!CompareCaseInsensitive(op.data(), "Stop")) + return CmdOp::Stop; + else if (!CompareCaseInsensitive(op.data(), "SplitKey")) + return CmdOp::SplitKey; + else if (!CompareCaseInsensitive(op.data(), "SplitVel")) + return CmdOp::SplitVel; + else if (!CompareCaseInsensitive(op.data(), "WaitTicks")) + return CmdOp::WaitTicks; + else if (!CompareCaseInsensitive(op.data(), "Loop")) + return CmdOp::Loop; + else if (!CompareCaseInsensitive(op.data(), "Goto")) + return CmdOp::Goto; + else if (!CompareCaseInsensitive(op.data(), "WaitMs")) + return CmdOp::WaitMs; + else if (!CompareCaseInsensitive(op.data(), "PlayMacro")) + return CmdOp::PlayMacro; + else if (!CompareCaseInsensitive(op.data(), "SendKeyOff")) + return CmdOp::SendKeyOff; + else if (!CompareCaseInsensitive(op.data(), "SplitMod")) + return CmdOp::SplitMod; + else if (!CompareCaseInsensitive(op.data(), "PianoPan")) + return CmdOp::PianoPan; + else if (!CompareCaseInsensitive(op.data(), "SetAdsr")) + return CmdOp::SetAdsr; + else if (!CompareCaseInsensitive(op.data(), "ScaleVolume")) + return CmdOp::ScaleVolume; + else if (!CompareCaseInsensitive(op.data(), "Panning")) + return CmdOp::Panning; + else if (!CompareCaseInsensitive(op.data(), "Envelope")) + return CmdOp::Envelope; + else if (!CompareCaseInsensitive(op.data(), "StartSample")) + return CmdOp::StartSample; + else if (!CompareCaseInsensitive(op.data(), "StopSample")) + return CmdOp::StopSample; + else if (!CompareCaseInsensitive(op.data(), "KeyOff")) + return CmdOp::KeyOff; + else if (!CompareCaseInsensitive(op.data(), "SplitRnd")) + return CmdOp::SplitRnd; + else if (!CompareCaseInsensitive(op.data(), "FadeIn")) + return CmdOp::FadeIn; + else if (!CompareCaseInsensitive(op.data(), "Spanning")) + return CmdOp::Spanning; + else if (!CompareCaseInsensitive(op.data(), "SetAdsrCtrl")) + return CmdOp::SetAdsrCtrl; + else if (!CompareCaseInsensitive(op.data(), "RndNote")) + return CmdOp::RndNote; + else if (!CompareCaseInsensitive(op.data(), "AddNote")) + return CmdOp::AddNote; + else if (!CompareCaseInsensitive(op.data(), "SetNote")) + return CmdOp::SetNote; + else if (!CompareCaseInsensitive(op.data(), "LastNote")) + return CmdOp::LastNote; + else if (!CompareCaseInsensitive(op.data(), "Portamento")) + return CmdOp::Portamento; + else if (!CompareCaseInsensitive(op.data(), "Vibrato")) + return CmdOp::Vibrato; + else if (!CompareCaseInsensitive(op.data(), "PitchSweep1")) + return CmdOp::PitchSweep1; + else if (!CompareCaseInsensitive(op.data(), "PitchSweep2")) + return CmdOp::PitchSweep2; + else if (!CompareCaseInsensitive(op.data(), "SetPitch")) + return CmdOp::SetPitch; + else if (!CompareCaseInsensitive(op.data(), "SetPitchAdsr")) + return CmdOp::SetPitchAdsr; + else if (!CompareCaseInsensitive(op.data(), "ScaleVolumeDLS")) + return CmdOp::ScaleVolumeDLS; + else if (!CompareCaseInsensitive(op.data(), "Mod2Vibrange")) + return CmdOp::Mod2Vibrange; + else if (!CompareCaseInsensitive(op.data(), "SetupTremolo")) + return CmdOp::SetupTremolo; + else if (!CompareCaseInsensitive(op.data(), "Return")) + return CmdOp::Return; + else if (!CompareCaseInsensitive(op.data(), "GoSub")) + return CmdOp::GoSub; + else if (!CompareCaseInsensitive(op.data(), "TrapEvent")) + return CmdOp::TrapEvent; + else if (!CompareCaseInsensitive(op.data(), "UntrapEvent")) + return CmdOp::UntrapEvent; + else if (!CompareCaseInsensitive(op.data(), "SendMessage")) + return CmdOp::SendMessage; + else if (!CompareCaseInsensitive(op.data(), "GetMessage")) + return CmdOp::GetMessage; + else if (!CompareCaseInsensitive(op.data(), "GetVid")) + return CmdOp::GetVid; + else if (!CompareCaseInsensitive(op.data(), "AddAgeCount")) + return CmdOp::AddAgeCount; + else if (!CompareCaseInsensitive(op.data(), "SetAgeCount")) + return CmdOp::SetAgeCount; + else if (!CompareCaseInsensitive(op.data(), "SendFlag")) + return CmdOp::SendFlag; + else if (!CompareCaseInsensitive(op.data(), "PitchWheelR")) + return CmdOp::PitchWheelR; + else if (!CompareCaseInsensitive(op.data(), "SetPriority")) + return CmdOp::SetPriority; + else if (!CompareCaseInsensitive(op.data(), "AddPriority")) + return CmdOp::AddPriority; + else if (!CompareCaseInsensitive(op.data(), "AgeCntSpeed")) + return CmdOp::AgeCntSpeed; + else if (!CompareCaseInsensitive(op.data(), "AgeCntVel")) + return CmdOp::AgeCntVel; + else if (!CompareCaseInsensitive(op.data(), "VolSelect")) + return CmdOp::VolSelect; + else if (!CompareCaseInsensitive(op.data(), "PanSelect")) + return CmdOp::PanSelect; + else if (!CompareCaseInsensitive(op.data(), "PitchWheelSelect")) + return CmdOp::PitchWheelSelect; + else if (!CompareCaseInsensitive(op.data(), "ModWheelSelect")) + return CmdOp::ModWheelSelect; + else if (!CompareCaseInsensitive(op.data(), "PedalSelect")) + return CmdOp::PedalSelect; + else if (!CompareCaseInsensitive(op.data(), "PortamentoSelect")) + return CmdOp::PortamentoSelect; + else if (!CompareCaseInsensitive(op.data(), "ReverbSelect")) + return CmdOp::ReverbSelect; + else if (!CompareCaseInsensitive(op.data(), "SpanSelect")) + return CmdOp::SpanSelect; + else if (!CompareCaseInsensitive(op.data(), "DopplerSelect")) + return CmdOp::DopplerSelect; + else if (!CompareCaseInsensitive(op.data(), "TremoloSelect")) + return CmdOp::TremoloSelect; + else if (!CompareCaseInsensitive(op.data(), "PreASelect")) + return CmdOp::PreASelect; + else if (!CompareCaseInsensitive(op.data(), "PreBSelect")) + return CmdOp::PreBSelect; + else if (!CompareCaseInsensitive(op.data(), "PostBSelect")) + return CmdOp::PostBSelect; + else if (!CompareCaseInsensitive(op.data(), "AuxAFXSelect")) + return CmdOp::AuxAFXSelect; + else if (!CompareCaseInsensitive(op.data(), "AuxBFXSelect")) + return CmdOp::AuxBFXSelect; + else if (!CompareCaseInsensitive(op.data(), "SetupLFO")) + return CmdOp::SetupLFO; + else if (!CompareCaseInsensitive(op.data(), "ModeSelect")) + return CmdOp::ModeSelect; + else if (!CompareCaseInsensitive(op.data(), "SetKeygroup")) + return CmdOp::SetKeygroup; + else if (!CompareCaseInsensitive(op.data(), "SRCmodeSelect")) + return CmdOp::SRCmodeSelect; + else if (!CompareCaseInsensitive(op.data(), "AddVars")) + return CmdOp::AddVars; + else if (!CompareCaseInsensitive(op.data(), "SubVars")) + return CmdOp::SubVars; + else if (!CompareCaseInsensitive(op.data(), "MulVars")) + return CmdOp::MulVars; + else if (!CompareCaseInsensitive(op.data(), "DivVars")) + return CmdOp::DivVars; + else if (!CompareCaseInsensitive(op.data(), "AddIVars")) + return CmdOp::AddIVars; + else if (!CompareCaseInsensitive(op.data(), "IfEqual")) + return CmdOp::IfEqual; + else if (!CompareCaseInsensitive(op.data(), "IfLess")) + return CmdOp::IfLess; + return CmdOp::Invalid; +} + +bool AudioGroupPool::toYAML(athena::io::IStreamWriter& writer) const +{ + athena::io::YAMLDocWriter w("amuse::Pool"); + + if (!m_soundMacros.empty()) + { + if (auto __r = w.enterSubRecord("soundMacros")) + { + for (const auto& p : SortUnorderedMap(m_soundMacros)) + { + if (auto __v = w.enterSubVector(SoundMacroId::CurNameDB->resolveNameFromId(p.first).data())) + { + for (const auto& c : p.second.get().m_cmds) + { + if (auto __r2 = w.enterSubRecord(nullptr)) + { + w.setStyle(athena::io::YAMLNodeStyle::Flow); + w.writeString("cmdOp", SoundMacro::CmdOpToStr(c->Isa())); + c->write(w); + } + } + } + } + } + } + + if (!m_tables.empty()) + { + if (auto __r = w.enterSubRecord("tables")) + { + for (const auto& p : SortUnorderedMap(m_tables)) + { + if (auto __v = w.enterSubRecord(TableId::CurNameDB->resolveNameFromId(p.first).data())) + { + w.setStyle(athena::io::YAMLNodeStyle::Flow); + p.second.get()->write(w); + } + } + } + } + + if (!m_keymaps.empty()) + { + if (auto __r = w.enterSubRecord("keymaps")) + { + for (const auto& p : SortUnorderedMap(m_keymaps)) + { + if (auto __v = w.enterSubRecord(KeymapId::CurNameDB->resolveNameFromId(p.first).data())) + { + w.setStyle(athena::io::YAMLNodeStyle::Flow); + p.second.get().write(w); + } + } + } + } + + if (!m_layers.empty()) + { + if (auto __r = w.enterSubRecord("layers")) + { + for (const auto& p : SortUnorderedMap(m_layers)) + { + if (auto __v = w.enterSubVector(LayersId::CurNameDB->resolveNameFromId(p.first).data())) + { + for (const auto& lm : p.second.get()) + { + if (auto __r2 = w.enterSubRecord(nullptr)) + { + w.setStyle(athena::io::YAMLNodeStyle::Flow); + lm.write(w); + } + } + } + } + } + } + + return w.finish(&writer); +} + template <> void amuse::Curve::Enumerate(athena::io::IStreamReader& r) { @@ -368,17 +774,18 @@ void amuse::Curve::Enumerate(size_t& sz) template <> void amuse::Curve::Enumerate(athena::io::YAMLDocReader& r) { - r.enumerate(nullptr, data); + r.enumerate("data", data); } template <> void amuse::Curve::Enumerate(athena::io::YAMLDocWriter& w) { - w.enumerate(nullptr, data); + w.enumerate("data", data); } const char* amuse::Curve::DNAType() { return "amuse::ADSR"; } + } diff --git a/lib/AudioGroupProject.cpp b/lib/AudioGroupProject.cpp index d82d686..7536177 100644 --- a/lib/AudioGroupProject.cpp +++ b/lib/AudioGroupProject.cpp @@ -72,7 +72,7 @@ AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag) { SFXGroupIndex::SFXEntryDNA entry; entry.read(r); - idx.m_sfxEntries[entry.defineId.id] = entry; + idx.m_sfxEntries[entry.sfxId.id] = entry; } } @@ -120,7 +120,8 @@ AudioGroupProject AudioGroupProject::_AudioGroupProject(athena::io::IStreamReade r.seek(header.midiSetupsOff, athena::Begin); while (r.position() < header.groupEndOff) { - uint16_t songId = r.readUint16Big(); + uint16_t songId; + athena::io::Read::Do({}, songId, r); r.seek(2, athena::Current); std::array& setup = idx.m_midiSetups[songId]; for (int i = 0; i < 16 ; ++i) @@ -151,7 +152,8 @@ AudioGroupProject AudioGroupProject::_AudioGroupProject(athena::io::IStreamReade r.seek(subDataOff + header.midiSetupsOff, athena::Begin); while (r.position() < groupBegin + header.groupEndOff) { - uint16_t songId = r.readUint16Big(); + uint16_t songId; + athena::io::Read::Do({}, songId, r); r.seek(2, athena::Current); std::array& setup = idx.m_midiSetups[songId]; for (int i = 0; i < 16 ; ++i) @@ -169,7 +171,8 @@ AudioGroupProject AudioGroupProject::_AudioGroupProject(athena::io::IStreamReade /* SFX entries */ r.seek(subDataOff + header.pageTableOff, athena::Begin); - uint16_t count = r.readUint16Big(); + uint16_t count; + athena::io::Read::Do({}, count, r); r.seek(2, athena::Current); idx.m_sfxEntries.reserve(count); for (int i = 0; i < count; ++i) @@ -177,7 +180,7 @@ AudioGroupProject AudioGroupProject::_AudioGroupProject(athena::io::IStreamReade SFXGroupIndex::SFXEntryDNA entry; entry.read(r); r.seek(2, athena::Current); - idx.m_sfxEntries[entry.defineId.id] = entry; + idx.m_sfxEntries[entry.sfxId.id] = entry; } } @@ -205,6 +208,194 @@ AudioGroupProject AudioGroupProject::CreateAudioGroupProject(const AudioGroupDat } } +template +static void ReadRangedObjectIds(NameDB* db, athena::io::IStreamReader& r, NameDB::Type tp) +{ + uint16_t id; + athena::io::Read::Do({}, id, r); + if ((id & 0x8000) == 0x8000) + { + uint16_t endId; + athena::io::Read::Do({}, endId, r); + for (uint16_t i = uint16_t(id & 0x7fff); i <= uint16_t(endId & 0x7fff); ++i) + { + ObjectId useId = i; + if (tp == NameDB::Type::Layer) + useId.id |= 0x8000; + db->registerPair(NameDB::generateName(useId, tp), useId); + } + } + else + { + db->registerPair(NameDB::generateName(id, tp), id); + } +} + +void AudioGroupProject::BootstrapObjectIDs(athena::io::IStreamReader& r, GCNDataTag) +{ + while (!AtEnd32(r)) + { + GroupHeader header; + header.read(r); + + /* Sound Macros */ + r.seek(header.soundMacroIdsOff, athena::Begin); + while (!AtEnd16(r)) + ReadRangedObjectIds(SoundMacroId::CurNameDB, r, NameDB::Type::SoundMacro); + + /* Samples */ + r.seek(header.samplIdsOff, athena::Begin); + while (!AtEnd16(r)) + ReadRangedObjectIds(SampleId::CurNameDB, r, NameDB::Type::Sample); + + /* Tables */ + r.seek(header.tableIdsOff, athena::Begin); + while (!AtEnd16(r)) + ReadRangedObjectIds(TableId::CurNameDB, r, NameDB::Type::Table); + + /* Keymaps */ + r.seek(header.keymapIdsOff, athena::Begin); + while (!AtEnd16(r)) + ReadRangedObjectIds(KeymapId::CurNameDB, r, NameDB::Type::Keymap); + + /* Layers */ + r.seek(header.layerIdsOff, athena::Begin); + while (!AtEnd16(r)) + ReadRangedObjectIds(LayersId::CurNameDB, r, NameDB::Type::Layer); + + if (header.type == GroupType::Song) + { + /* MIDI setups */ + r.seek(header.midiSetupsOff, athena::Begin); + while (r.position() < header.groupEndOff) + { + uint16_t id = r.readUint16Big(); + SongId::CurNameDB->registerPair(NameDB::generateName(id, NameDB::Type::Song), id); + r.seek(2 + 5 * 16, athena::Current); + } + } + else if (header.type == GroupType::SFX) + { + /* SFX entries */ + r.seek(header.pageTableOff, athena::Begin); + uint16_t count = r.readUint16Big(); + r.seek(2, athena::Current); + for (int i = 0; i < count; ++i) + { + SFXGroupIndex::SFXEntryDNA entry; + entry.read(r); + SFXId::CurNameDB->registerPair( + NameDB::generateName(entry.sfxId.id, NameDB::Type::SFX), entry.sfxId.id); + } + } + + r.seek(header.groupEndOff, athena::Begin); + } +} + +template +void AudioGroupProject::BootstrapObjectIDs(athena::io::IStreamReader& r, bool absOffs) +{ + while (!AtEnd32(r)) + { + atInt64 groupBegin = r.position(); + atInt64 subDataOff = absOffs ? 0 : groupBegin + 8; + GroupHeader header; + header.read(r); + + /* Sound Macros */ + r.seek(subDataOff + header.soundMacroIdsOff, athena::Begin); + while (!AtEnd16(r)) + ReadRangedObjectIds(SoundMacroId::CurNameDB, r, NameDB::Type::SoundMacro); + + /* Samples */ + r.seek(subDataOff + header.samplIdsOff, athena::Begin); + while (!AtEnd16(r)) + ReadRangedObjectIds(SampleId::CurNameDB, r, NameDB::Type::Sample); + + /* Tables */ + r.seek(subDataOff + header.tableIdsOff, athena::Begin); + while (!AtEnd16(r)) + ReadRangedObjectIds(TableId::CurNameDB, r, NameDB::Type::Table); + + /* Keymaps */ + r.seek(subDataOff + header.keymapIdsOff, athena::Begin); + while (!AtEnd16(r)) + ReadRangedObjectIds(KeymapId::CurNameDB, r, NameDB::Type::Keymap); + + /* Layers */ + r.seek(subDataOff + header.layerIdsOff, athena::Begin); + while (!AtEnd16(r)) + ReadRangedObjectIds(LayersId::CurNameDB, r, NameDB::Type::Layer); + + if (header.type == GroupType::Song) + { + /* MIDI setups */ + if (absOffs) + { + r.seek(header.midiSetupsOff, athena::Begin); + while (r.position() < header.groupEndOff) + { + uint16_t id; + athena::io::Read::Do({}, id, r); + SongId::CurNameDB->registerPair(NameDB::generateName(id, NameDB::Type::Song), id); + r.seek(2 + 5 * 16, athena::Current); + } + } + else + { + r.seek(subDataOff + header.midiSetupsOff, athena::Begin); + while (r.position() < groupBegin + header.groupEndOff) + { + uint16_t id; + athena::io::Read::Do({}, id, r); + SongId::CurNameDB->registerPair(NameDB::generateName(id, NameDB::Type::Song), id); + r.seek(2 + 8 * 16, athena::Current); + } + } + } + else if (header.type == GroupType::SFX) + { + /* SFX entries */ + r.seek(subDataOff + header.pageTableOff, athena::Begin); + uint16_t count; + athena::io::Read::Do({}, count, r); + r.seek(2, athena::Current); + for (int i = 0; i < count; ++i) + { + SFXGroupIndex::SFXEntryDNA entry; + entry.read(r); + r.seek(2, athena::Current); + SFXId::CurNameDB->registerPair( + NameDB::generateName(entry.sfxId.id, NameDB::Type::SFX), entry.sfxId.id); + } + } + + if (absOffs) + r.seek(header.groupEndOff, athena::Begin); + else + r.seek(groupBegin + header.groupEndOff, athena::Begin); + } +} + +void AudioGroupProject::BootstrapObjectIDs(const AudioGroupData& data) +{ + athena::io::MemoryReader r(data.getProj(), data.getProjSize()); + switch (data.getDataFormat()) + { + case DataFormat::GCN: + default: + BootstrapObjectIDs(r, GCNDataTag{}); + break; + case DataFormat::N64: + BootstrapObjectIDs(r, data.getAbsoluteProjOffsets()); + break; + case DataFormat::PC: + BootstrapObjectIDs(r, data.getAbsoluteProjOffsets()); + break; + } +} + const SongGroupIndex* AudioGroupProject::getSongGroupIndex(int groupId) const { auto search = m_songGroups.find(groupId); @@ -221,4 +412,93 @@ const SFXGroupIndex* AudioGroupProject::getSFXGroupIndex(int groupId) const return nullptr; } +bool AudioGroupProject::toYAML(athena::io::IStreamWriter& writer) const +{ + athena::io::YAMLDocWriter w("amuse::Project"); + + if (!m_songGroups.empty()) + { + if (auto __v = w.enterSubVector("songGroups")) + { + for (const auto& p : SortUnorderedMap(m_songGroups)) + { + if (auto __r = w.enterSubRecord(nullptr)) + { + if (!p.second.get().m_normPages.empty()) + { + if (auto __v2 = w.enterSubRecord("normPages")) + { + for (const auto& pg : SortUnorderedMap(p.second.get().m_normPages)) + { + char name[16]; + snprintf(name, 16, "%d", pg.first); + if (auto __r2 = w.enterSubRecord(name)) + { + w.setStyle(athena::io::YAMLNodeStyle::Flow); + pg.second.get().write(w); + } + } + } + } + if (!p.second.get().m_drumPages.empty()) + { + if (auto __v2 = w.enterSubRecord("drumPages")) + { + for (const auto& pg : SortUnorderedMap(p.second.get().m_drumPages)) + { + char name[16]; + snprintf(name, 16, "%d", pg.first); + if (auto __r2 = w.enterSubRecord(name)) + { + w.setStyle(athena::io::YAMLNodeStyle::Flow); + pg.second.get().write(w); + } + } + } + } + if (!p.second.get().m_midiSetups.empty()) + { + if (auto __v2 = w.enterSubRecord("songs")) + { + for (const auto& song : SortUnorderedMap(p.second.get().m_midiSetups)) + { + if (auto __v3 = w.enterSubVector(SongId::CurNameDB->resolveNameFromId(song.first).data())) + for (int i = 0; i < 16; ++i) + if (auto __r2 = w.enterSubRecord(nullptr)) + { + w.setStyle(athena::io::YAMLNodeStyle::Flow); + song.second.get()[i].write(w); + } + } + } + } + } + } + } + } + + if (!m_sfxGroups.empty()) + { + if (auto __v = w.enterSubVector("sfxGroups")) + { + for (const auto& p : SortUnorderedMap(m_sfxGroups)) + { + if (auto __r = w.enterSubRecord(nullptr)) + { + for (const auto& sfx : SortUnorderedMap(p.second.get().m_sfxEntries)) + { + if (auto __r2 = w.enterSubRecord(SFXId::CurNameDB->resolveNameFromId(sfx.first).data())) + { + w.setStyle(athena::io::YAMLNodeStyle::Flow); + sfx.second.get().write(w); + } + } + } + } + } + } + + return w.finish(&writer); +} + } diff --git a/lib/AudioGroupSampleDirectory.cpp b/lib/AudioGroupSampleDirectory.cpp index 107dd0e..dad8751 100644 --- a/lib/AudioGroupSampleDirectory.cpp +++ b/lib/AudioGroupSampleDirectory.cpp @@ -2,20 +2,16 @@ #include "amuse/Common.hpp" #include "amuse/AudioGroupData.hpp" #include +#include namespace amuse { -void AudioGroupSampleDirectory::Entry::swapBig() +static bool AtEnd32(athena::io::IStreamReader& r) { - 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); + uint32_t v = r.readUint32Big(); + r.seek(-4, athena::Current); + return v == 0xffffffff; } void AudioGroupSampleDirectory::ADPCMParms::swapBigDSP() @@ -37,177 +33,94 @@ void AudioGroupSampleDirectory::ADPCMParms::swapBigVADPCM() allCoefs[i] = SBig(allCoefs[i]); } -AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data, GCNDataTag) +AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader& r, GCNDataTag) { - const unsigned char* cur = data; - while (*reinterpret_cast(cur) != 0xffffffff) + while (!AtEnd32(r)) { - const AudioGroupSampleDirectory::Entry* ent = reinterpret_cast(cur); + EntryDNA ent; + ent.read(r); + std::pair& store = m_entries[ent.m_sfxId]; + store.first = ent; + } - std::pair& store = m_entries[SBig(ent->m_sfxId)]; - store.first = *ent; - store.first.swapBig(); - - if (store.first.m_adpcmParmOffset) + for (auto& p : m_entries) + { + if (p.second.first.m_adpcmParmOffset) { - const AudioGroupSampleDirectory::ADPCMParms* adpcm = - reinterpret_cast(data + store.first.m_adpcmParmOffset); - store.second.dsp = adpcm->dsp; - store.second.swapBigDSP(); + r.seek(p.second.first.m_adpcmParmOffset, athena::Begin); + r.readUBytesToBuf(&p.second.second, sizeof(ADPCMParms::DSPParms)); + p.second.second.swapBigDSP(); } - - cur += 32; } } -struct MusyX1SdirEntry -{ - uint16_t m_sfxId; - uint32_t m_sampleOff; - uint32_t m_pitchSampleRate; - uint32_t m_numSamples; - uint32_t m_loopStartSample; - uint32_t m_loopLengthSamples; - - void swapBig() - { - m_sfxId = SBig(m_sfxId); - m_sampleOff = SBig(m_sampleOff); - m_pitchSampleRate = SBig(m_pitchSampleRate); - m_numSamples = SBig(m_numSamples); - m_loopStartSample = SBig(m_loopStartSample); - m_loopLengthSamples = SBig(m_loopLengthSamples); - } - - void setIntoMusyX2(AudioGroupSampleDirectory::Entry& ent) const - { - ent.m_sfxId = m_sfxId; - ent.m_sampleOff = m_sampleOff; - ent.m_unk = 0; - ent.m_pitch = m_pitchSampleRate >> 24; - ent.m_sampleRate = m_pitchSampleRate & 0xffff; - ent.m_numSamples = m_numSamples; - ent.m_loopStartSample = m_loopStartSample; - ent.m_loopLengthSamples = m_loopLengthSamples; - ent.m_adpcmParmOffset = 0; - } -}; - -struct MusyX1AbsSdirEntry -{ - uint16_t m_sfxId; - uint32_t m_sampleOff; - uint32_t m_unk; - uint32_t m_pitchSampleRate; - uint32_t m_numSamples; - uint32_t m_loopStartSample; - uint32_t m_loopLengthSamples; - - void swapBig() - { - m_sfxId = SBig(m_sfxId); - m_sampleOff = SBig(m_sampleOff); - m_unk = SBig(m_unk); - m_pitchSampleRate = SBig(m_pitchSampleRate); - m_numSamples = SBig(m_numSamples); - m_loopStartSample = SBig(m_loopStartSample); - m_loopLengthSamples = SBig(m_loopLengthSamples); - } - - void setIntoMusyX2(AudioGroupSampleDirectory::Entry& ent) const - { - ent.m_sfxId = m_sfxId; - ent.m_sampleOff = m_sampleOff; - ent.m_unk = m_unk; - ent.m_pitch = m_pitchSampleRate >> 24; - ent.m_sampleRate = m_pitchSampleRate & 0xffff; - ent.m_numSamples = m_numSamples; - ent.m_loopStartSample = m_loopStartSample; - ent.m_loopLengthSamples = m_loopLengthSamples; - ent.m_adpcmParmOffset = 0; - } -}; - -AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data, const unsigned char* sampData, +AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader& r, const unsigned char* sampData, bool absOffs, N64DataTag) { - const unsigned char* cur = data; - if (absOffs) { - while (*reinterpret_cast(cur) != 0xffffffff) + while (!AtEnd32(r)) { - MusyX1AbsSdirEntry ent = *reinterpret_cast(cur); - ent.swapBig(); - + MusyX1AbsSdirEntry ent; + ent.read(r); std::pair& store = m_entries[ent.m_sfxId]; - ent.setIntoMusyX2(store.first); - - memmove(&store.second.vadpcm.m_coefs, sampData + ent.m_sampleOff, 256); - store.second.swapBigVADPCM(); - - cur += 28; + store.first = ent; } } else { - while (*reinterpret_cast(cur) != 0xffffffff) + while (!AtEnd32(r)) { - MusyX1SdirEntry ent = *reinterpret_cast(cur); - ent.swapBig(); - + MusyX1SdirEntry ent; + ent.read(r); std::pair& store = m_entries[ent.m_sfxId]; - ent.setIntoMusyX2(store.first); - - memmove(&store.second.vadpcm.m_coefs, sampData + ent.m_sampleOff, 256); - store.second.swapBigVADPCM(); - - cur += 24; + store.first = ent; } } + + for (auto& p : m_entries) + { + memcpy(&p.second.second, sampData + p.second.first.m_sampleOff, sizeof(ADPCMParms::VADPCMParms)); + p.second.second.swapBigVADPCM(); + } } -AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data, bool absOffs, PCDataTag) +AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader& r, bool absOffs, PCDataTag) { - const unsigned char* cur = data; - if (absOffs) { - while (*reinterpret_cast(cur) != 0xffffffff) + while (!AtEnd32(r)) { - const MusyX1AbsSdirEntry* ent = reinterpret_cast(cur); - - std::pair& store = m_entries[ent->m_sfxId]; - ent->setIntoMusyX2(store.first); - - cur += 28; + MusyX1AbsSdirEntry ent; + ent.read(r); + std::pair& store = m_entries[ent.m_sfxId]; + store.first = ent; } } else { - while (*reinterpret_cast(cur) != 0xffffffff) + while (!AtEnd32(r)) { - const MusyX1SdirEntry* ent = reinterpret_cast(cur); - - std::pair& store = m_entries[ent->m_sfxId]; - ent->setIntoMusyX2(store.first); - - cur += 24; + MusyX1SdirEntry ent; + ent.read(r); + std::pair& store = m_entries[ent.m_sfxId]; + store.first = ent; } } } AudioGroupSampleDirectory AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(const AudioGroupData& data) { + athena::io::MemoryReader r(data.getSdir(), data.getSdirSize()); switch (data.getDataFormat()) { case DataFormat::GCN: default: - return AudioGroupSampleDirectory(data.getSdir(), GCNDataTag{}); + return AudioGroupSampleDirectory(r, GCNDataTag{}); case DataFormat::N64: - return AudioGroupSampleDirectory(data.getSdir(), data.getSamp(), data.getAbsoluteProjOffsets(), N64DataTag{}); + return AudioGroupSampleDirectory(r, data.getSamp(), data.getAbsoluteProjOffsets(), N64DataTag{}); case DataFormat::PC: - return AudioGroupSampleDirectory(data.getSdir(), data.getAbsoluteProjOffsets(), PCDataTag{}); + return AudioGroupSampleDirectory(r, data.getAbsoluteProjOffsets(), PCDataTag{}); } } } diff --git a/lib/Common.cpp b/lib/Common.cpp index 8408cfd..75fb2b9 100644 --- a/lib/Common.cpp +++ b/lib/Common.cpp @@ -5,374 +5,228 @@ namespace amuse { static logvisor::Module Log("amuse"); -thread_local NameDB* ObjectId::CurNameDB = nullptr; -thread_local NameDB* SampleId::CurNameDB = nullptr; -thread_local NameDB* SongId::CurNameDB = nullptr; -thread_local NameDB* SFXId::CurNameDB = nullptr; +#define DEFINE_ID_TYPE(type, typeName) \ +thread_local NameDB* type::CurNameDB = nullptr; \ +template<> template<> \ +void type##DNA::Enumerate(athena::io::IStreamReader& reader) \ +{ \ + id = reader.readUint16Little(); \ +} \ +template<> template<> \ +void type##DNA::Enumerate(athena::io::IStreamWriter& writer) \ +{ \ + writer.writeUint16Little(id); \ +} \ +template<> template<> \ +void type##DNA::Enumerate(size_t& sz) \ +{ \ + sz += 2; \ +} \ +template<> template<> \ +void type##DNA::Enumerate(athena::io::YAMLDocReader& reader) \ +{ \ + _read(reader); \ +} \ +template<> template<> \ +void type##DNA::Enumerate(athena::io::YAMLDocWriter& writer) \ +{ \ + _write(writer); \ +} \ +template<> template<> \ +void type##DNA::Enumerate(athena::io::IStreamReader& reader) \ +{ \ + id = reader.readUint16Big(); \ +} \ +template<> template<> \ +void type##DNA::Enumerate(athena::io::IStreamWriter& writer) \ +{ \ + writer.writeUint16Big(id); \ +} \ +template<> template<> \ +void type##DNA::Enumerate(size_t& sz) \ +{ \ + sz += 2; \ +} \ +template<> template<> \ +void type##DNA::Enumerate(athena::io::YAMLDocReader& reader) \ +{ \ + _read(reader); \ +} \ +template<> template<> \ +void type##DNA::Enumerate(athena::io::YAMLDocWriter& writer) \ +{ \ + _write(writer); \ +} \ +template \ +void type##DNA::_read(athena::io::YAMLDocReader& r) \ +{ \ + std::string name = r.readString(nullptr); \ + if (!type::CurNameDB) \ + Log.report(logvisor::Fatal, "Unable to resolve " typeName " name %s, no database present", name.c_str()); \ + if (name.empty()) \ + { \ + id.id = 0xffff; \ + return; \ + } \ + id = type::CurNameDB->resolveIdFromName(name); \ +} \ +template \ +void type##DNA::_write(athena::io::YAMLDocWriter& w) \ +{ \ + if (!type::CurNameDB) \ + Log.report(logvisor::Fatal, "Unable to resolve " typeName " ID %d, no database present", id.id); \ + if (id.id == 0xffff) \ + return; \ + std::string_view name = type::CurNameDB->resolveNameFromId(id); \ + w.writeString(nullptr, name); \ +} \ +template \ +const char* type##DNA::DNAType() \ +{ \ + return "amuse::" #type "DNA"; \ +} \ +template struct type##DNA; \ +template struct type##DNA; + +DEFINE_ID_TYPE(ObjectId, "object") +DEFINE_ID_TYPE(SoundMacroId, "SoundMacro") +DEFINE_ID_TYPE(SampleId, "sample") +DEFINE_ID_TYPE(TableId, "table") +DEFINE_ID_TYPE(KeymapId, "keymap") +DEFINE_ID_TYPE(LayersId, "layers") +DEFINE_ID_TYPE(SongId, "song") +DEFINE_ID_TYPE(SFXId, "sfx") template<> template<> -void ObjectIdDNA::Enumerate(athena::io::IStreamReader& reader) +void PageObjectIdDNA::Enumerate(athena::io::IStreamReader& reader) { id = reader.readUint16Little(); } - template<> template<> -void ObjectIdDNA::Enumerate(athena::io::IStreamWriter& writer) +void PageObjectIdDNA::Enumerate(athena::io::IStreamWriter& writer) { writer.writeUint16Little(id); } - template<> template<> -void ObjectIdDNA::Enumerate(size_t& sz) +void PageObjectIdDNA::Enumerate(size_t& sz) { sz += 2; } - template<> template<> -void ObjectIdDNA::Enumerate(athena::io::YAMLDocReader& reader) +void PageObjectIdDNA::Enumerate(athena::io::YAMLDocReader& reader) { _read(reader); } - template<> template<> -void ObjectIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) +void PageObjectIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) { _write(writer); } - template<> template<> -void ObjectIdDNA::Enumerate(athena::io::IStreamReader& reader) +void PageObjectIdDNA::Enumerate(athena::io::IStreamReader& reader) { id = reader.readUint16Big(); } - template<> template<> -void ObjectIdDNA::Enumerate(athena::io::IStreamWriter& writer) +void PageObjectIdDNA::Enumerate(athena::io::IStreamWriter& writer) { writer.writeUint16Big(id); } - template<> template<> -void ObjectIdDNA::Enumerate(size_t& sz) +void PageObjectIdDNA::Enumerate(size_t& sz) { sz += 2; } - template<> template<> -void ObjectIdDNA::Enumerate(athena::io::YAMLDocReader& reader) +void PageObjectIdDNA::Enumerate(athena::io::YAMLDocReader& reader) { _read(reader); } - template<> template<> -void ObjectIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) +void PageObjectIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) { _write(writer); } - template -void ObjectIdDNA::_read(athena::io::YAMLDocReader& r) +void PageObjectIdDNA::_read(athena::io::YAMLDocReader& r) { std::string name = r.readString(nullptr); - if (!ObjectId::CurNameDB) - Log.report(logvisor::Fatal, "Unable to resolve object name %s, no database present", name.c_str()); - id = ObjectId::CurNameDB->resolveIdFromName(name); + if (!KeymapId::CurNameDB || !LayersId::CurNameDB) + Log.report(logvisor::Fatal, "Unable to resolve keymap or layers name %s, no database present", name.c_str()); + if (name.empty()) + { + id.id = 0xffff; + return; + } + auto search = KeymapId::CurNameDB->m_stringToId.find(name); + if (search == KeymapId::CurNameDB->m_stringToId.cend()) + { + search = LayersId::CurNameDB->m_stringToId.find(name); + if (search == LayersId::CurNameDB->m_stringToId.cend()) + Log.report(logvisor::Fatal, "Unable to resolve name %s", name.c_str()); + } + id = search->second; } - template -void ObjectIdDNA::_write(athena::io::YAMLDocWriter& w) +void PageObjectIdDNA::_write(athena::io::YAMLDocWriter& w) { - if (!ObjectId::CurNameDB) - Log.report(logvisor::Fatal, "Unable to resolve object ID %d, no database present", id.id); - std::string_view name = ObjectId::CurNameDB->resolveNameFromId(id); - w.writeString(nullptr, name); + if (!KeymapId::CurNameDB || !LayersId::CurNameDB) + Log.report(logvisor::Fatal, "Unable to resolve keymap or layers ID %d, no database present", id.id); + if (id.id == 0xffff) + return; + if (id.id & 0x8000) + { + std::string_view name = LayersId::CurNameDB->resolveNameFromId(id); + w.writeString(nullptr, name); + } + else + { + std::string_view name = KeymapId::CurNameDB->resolveNameFromId(id); + w.writeString(nullptr, name); + } } - template -const char* ObjectIdDNA::DNAType() +const char* PageObjectIdDNA::DNAType() { - return "amuse::ObjectId"; -} - -template<> template<> -void SampleIdDNA::Enumerate(athena::io::IStreamReader& reader) -{ - id = reader.readUint16Little(); -} - -template<> template<> -void SampleIdDNA::Enumerate(athena::io::IStreamWriter& writer) -{ - writer.writeUint16Little(id); -} - -template<> template<> -void SampleIdDNA::Enumerate(size_t& sz) -{ - sz += 2; -} - -template<> template<> -void SampleIdDNA::Enumerate(athena::io::YAMLDocReader& reader) -{ - _read(reader); -} - -template<> template<> -void SampleIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) -{ - _write(writer); -} - -template<> template<> -void SampleIdDNA::Enumerate(athena::io::IStreamReader& reader) -{ - id = reader.readUint16Big(); -} - -template<> template<> -void SampleIdDNA::Enumerate(athena::io::IStreamWriter& writer) -{ - writer.writeUint16Big(id); -} - -template<> template<> -void SampleIdDNA::Enumerate(size_t& sz) -{ - sz += 2; -} - -template<> template<> -void SampleIdDNA::Enumerate(athena::io::YAMLDocReader& reader) -{ - _read(reader); -} - -template<> template<> -void SampleIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) -{ - _write(writer); -} - -template -void SampleIdDNA::_read(athena::io::YAMLDocReader& r) -{ - std::string name = r.readString(nullptr); - if (!SampleId::CurNameDB) - Log.report(logvisor::Fatal, "Unable to resolve sample name %s, no database present", name.c_str()); - id = SampleId::CurNameDB->resolveIdFromName(name); -} - -template -void SampleIdDNA::_write(athena::io::YAMLDocWriter& w) -{ - if (!SampleId::CurNameDB) - Log.report(logvisor::Fatal, "Unable to resolve sample ID %d, no database present", id.id); - std::string_view name = SampleId::CurNameDB->resolveNameFromId(id); - w.writeString(nullptr, name); -} - -template -const char* SampleIdDNA::DNAType() -{ - return "amuse::SampleId"; -} - -template<> template<> -void SongIdDNA::Enumerate(athena::io::IStreamReader& reader) -{ - id = reader.readUint16Little(); -} - -template<> template<> -void SongIdDNA::Enumerate(athena::io::IStreamWriter& writer) -{ - writer.writeUint16Little(id); -} - -template<> template<> -void SongIdDNA::Enumerate(size_t& sz) -{ - sz += 2; -} - -template<> template<> -void SongIdDNA::Enumerate(athena::io::YAMLDocReader& reader) -{ - _read(reader); -} - -template<> template<> -void SongIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) -{ - _write(writer); -} - -template<> template<> -void SongIdDNA::Enumerate(athena::io::IStreamReader& reader) -{ - id = reader.readUint16Big(); -} - -template<> template<> -void SongIdDNA::Enumerate(athena::io::IStreamWriter& writer) -{ - writer.writeUint16Big(id); -} - -template<> template<> -void SongIdDNA::Enumerate(size_t& sz) -{ - sz += 2; -} - -template<> template<> -void SongIdDNA::Enumerate(athena::io::YAMLDocReader& reader) -{ - _read(reader); -} - -template<> template<> -void SongIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) -{ - _write(writer); -} - -template -void SongIdDNA::_read(athena::io::YAMLDocReader& r) -{ - std::string name = r.readString(nullptr); - if (!SongId::CurNameDB) - Log.report(logvisor::Fatal, "Unable to resolve song name %s, no database present", name.c_str()); - id = SongId::CurNameDB->resolveIdFromName(name); -} - -template -void SongIdDNA::_write(athena::io::YAMLDocWriter& w) -{ - if (!SongId::CurNameDB) - Log.report(logvisor::Fatal, "Unable to resolve song ID %d, no database present", id.id); - std::string_view name = SongId::CurNameDB->resolveNameFromId(id); - w.writeString(nullptr, name); -} - -template -const char* SongIdDNA::DNAType() -{ - return "amuse::SongId"; -} - -template<> template<> -void SFXIdDNA::Enumerate(athena::io::IStreamReader& reader) -{ - id = reader.readUint16Little(); -} - -template<> template<> -void SFXIdDNA::Enumerate(athena::io::IStreamWriter& writer) -{ - writer.writeUint16Little(id); -} - -template<> template<> -void SFXIdDNA::Enumerate(size_t& sz) -{ - sz += 2; -} - -template<> template<> -void SFXIdDNA::Enumerate(athena::io::YAMLDocReader& reader) -{ - _read(reader); -} - -template<> template<> -void SFXIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) -{ - _write(writer); -} - -template<> template<> -void SFXIdDNA::Enumerate(athena::io::IStreamReader& reader) -{ - id = reader.readUint16Big(); -} - -template<> template<> -void SFXIdDNA::Enumerate(athena::io::IStreamWriter& writer) -{ - writer.writeUint16Big(id); -} - -template<> template<> -void SFXIdDNA::Enumerate(size_t& sz) -{ - sz += 2; -} - -template<> template<> -void SFXIdDNA::Enumerate(athena::io::YAMLDocReader& reader) -{ - _read(reader); -} - -template<> template<> -void SFXIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) -{ - _write(writer); -} - -template -void SFXIdDNA::_read(athena::io::YAMLDocReader& r) -{ - std::string name = r.readString(nullptr); - if (!SFXId::CurNameDB) - Log.report(logvisor::Fatal, "Unable to resolve song name %s, no database present", name.c_str()); - id = SFXId::CurNameDB->resolveIdFromName(name); -} - -template -void SFXIdDNA::_write(athena::io::YAMLDocWriter& w) -{ - if (!SFXId::CurNameDB) - Log.report(logvisor::Fatal, "Unable to resolve song ID %d, no database present", id.id); - std::string_view name = SFXId::CurNameDB->resolveNameFromId(id); - w.writeString(nullptr, name); -} - -template -const char* SFXIdDNA::DNAType() -{ - return "amuse::SFXId"; + return "amuse::PageObjectIdDNA"; } +template struct PageObjectIdDNA; +template struct PageObjectIdDNA; ObjectId NameDB::generateId(Type tp) { - uint16_t upperMatch = uint16_t(tp) << 8; - uint16_t maxMatch = 0; + uint16_t maxMatch = uint16_t(tp == Type::Layer ? 0x8000 : 0); for (const auto& p : m_idToString) - if ((p.first & 0xff00) == upperMatch && (p.first & 0xff) >= maxMatch) - maxMatch = (p.first & 0xff) + 1; - return upperMatch | maxMatch; + if (p.first >= maxMatch) + maxMatch = p.first + 1; + return maxMatch; } -std::string NameDB::generateName(ObjectId id) +std::string NameDB::generateName(ObjectId id, Type tp) { - Type tp = Type(id.id >> 8); char name[32]; switch (tp) { case Type::SoundMacro: - snprintf(name, 32, "macro%d", id.id & 0xff); + snprintf(name, 32, "macro%04X", id.id); break; case Type::Table: - snprintf(name, 32, "table%d", id.id & 0xff); + snprintf(name, 32, "table%04X", id.id); break; case Type::Keymap: - snprintf(name, 32, "keymap%d", id.id & 0xff); + snprintf(name, 32, "keymap%04X", id.id); break; case Type::Layer: - snprintf(name, 32, "layers%d", id.id & 0xff); + snprintf(name, 32, "layers%04X", id.id); + break; + case Type::Song: + snprintf(name, 32, "song%04X", id.id); + break; + case Type::SFX: + snprintf(name, 32, "sfx%04X", id.id); + break; + case Type::Sample: + snprintf(name, 32, "sample%04X", id.id); break; default: snprintf(name, 32, "obj%04X", id.id); @@ -391,7 +245,7 @@ std::string_view NameDB::resolveNameFromId(ObjectId id) const { auto search = m_idToString.find(id); if (search == m_idToString.cend()) - Log.report(logvisor::Fatal, "Unable to resolve ID %d", id.id); + Log.report(logvisor::Fatal, "Unable to resolve ID 0x%04X", id.id); return search->second; } @@ -403,12 +257,6 @@ ObjectId NameDB::resolveIdFromName(std::string_view str) const return search->second; } -template struct ObjectIdDNA; -template struct ObjectIdDNA; - -template struct SampleIdDNA; -template struct SampleIdDNA; - template<> void LittleUInt24::Enumerate(athena::io::IStreamReader& reader) { diff --git a/lib/Engine.cpp b/lib/Engine.cpp index d253483..249627c 100644 --- a/lib/Engine.cpp +++ b/lib/Engine.cpp @@ -307,7 +307,7 @@ std::shared_ptr Engine::fxStart(int sfxId, float vol, float pan, std::wea std::list>::iterator ret = _allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, false, smx); - if (!(*ret)->loadSoundObject(entry->objId, 0, 1000.f, entry->defKey, entry->defVel, 0)) + if (!(*ret)->loadMacroObject(entry->macro.id, 0, 1000.f, entry->defKey, entry->defVel, 0)) { _destroyVoice(ret); return {}; @@ -335,7 +335,7 @@ std::shared_ptr Engine::addEmitter(const float* pos, const float* dir, std::list>::iterator vox = _allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, true, smx); - if (!(*vox)->loadSoundObject(entry->objId, 0, 1000.f, entry->defKey, entry->defVel, 0)) + if (!(*vox)->loadMacroObject(entry->macro, 0, 1000.f, entry->defKey, entry->defVel, 0)) { _destroyVoice(vox); return {}; diff --git a/lib/Sequencer.cpp b/lib/Sequencer.cpp index 5462582..046be9e 100644 --- a/lib/Sequencer.cpp +++ b/lib/Sequencer.cpp @@ -247,19 +247,24 @@ std::shared_ptr Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo (*ret)->installCtrlValues(m_ctrlVals); ObjectId oid; + bool res; if (m_parent->m_songGroup) + { oid = m_page->objId; + res = (*ret)->loadPageObject(oid, m_parent->m_ticksPerSec, note, velocity, m_ctrlVals[1]); + } else if (m_parent->m_sfxMappings.size()) { size_t lookupIdx = note % m_parent->m_sfxMappings.size(); const SFXGroupIndex::SFXEntry* sfxEntry = m_parent->m_sfxMappings[lookupIdx]; - oid = sfxEntry->objId; + oid = sfxEntry->macro; note = sfxEntry->defKey; + res = (*ret)->loadMacroObject(oid, 0, m_parent->m_ticksPerSec, note, velocity, m_ctrlVals[1]); } else return {}; - if (!(*ret)->loadSoundObject(oid, 0, m_parent->m_ticksPerSec, note, velocity, m_ctrlVals[1])) + if (!res) { m_parent->m_engine._destroyVoice(ret); return {}; diff --git a/lib/SoundMacroState.cpp b/lib/SoundMacroState.cpp index a556d4e..bb73a6a 100644 --- a/lib/SoundMacroState.cpp +++ b/lib/SoundMacroState.cpp @@ -152,7 +152,7 @@ bool SoundMacro::CmdSplitKey::Do(SoundMacroState& st, Voice& vox) const if (macro.id == std::get<0>(st.m_pc.back())) st._setPC(macroStep); else - vox.loadSoundObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod); + vox.loadMacroObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod); } return false; @@ -166,7 +166,7 @@ bool SoundMacro::CmdSplitVel::Do(SoundMacroState& st, Voice& vox) const if (macro.id == std::get<0>(st.m_pc.back())) st._setPC(macroStep); else - vox.loadSoundObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod); + vox.loadMacroObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod); } return false; @@ -238,7 +238,7 @@ bool SoundMacro::CmdGoto::Do(SoundMacroState& st, Voice& vox) const if (macro.id == std::get<0>(st.m_pc.back())) st._setPC(macroStep); else - vox.loadSoundObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod); + vox.loadMacroObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod); return false; } @@ -312,7 +312,7 @@ bool SoundMacro::CmdSplitMod::Do(SoundMacroState& st, Voice& vox) const if (macro.id == std::get<0>(st.m_pc.back())) st._setPC(macroStep); else - vox.loadSoundObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod); + vox.loadMacroObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod); } return false; @@ -420,7 +420,7 @@ bool SoundMacro::CmdSplitRnd::Do(SoundMacroState& st, Voice& vox) const if (macro.id == std::get<0>(st.m_pc.back())) st._setPC(macroStep); else - vox.loadSoundObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod); + vox.loadMacroObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod); } return false; @@ -641,8 +641,7 @@ bool SoundMacro::CmdGoSub::Do(SoundMacroState& st, Voice& vox) const st.m_pc.emplace_back(std::get<0>(st.m_pc.back()), std::get<1>(st.m_pc.back()), std::get<1>(st.m_pc.back())->assertPC(macroStep)); else - vox.loadSoundObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod, true); - + vox.loadMacroObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod, true); vox._setObjectId(std::get<0>(st.m_pc.back())); diff --git a/lib/Voice.cpp b/lib/Voice.cpp index 900ffc9..5ec8cae 100644 --- a/lib/Voice.cpp +++ b/lib/Voice.cpp @@ -49,7 +49,7 @@ void Voice::_macroSampleEnd() m_state.m_inWait = false; } else - loadSoundObject(m_sampleEndTrap.macroId, m_sampleEndTrap.macroStep, m_state.m_ticksPerSec, + loadMacroObject(m_sampleEndTrap.macroId, m_sampleEndTrap.macroStep, m_state.m_ticksPerSec, m_state.m_initKey, m_state.m_initVel, m_state.m_initMod); } else @@ -734,7 +734,7 @@ std::shared_ptr Voice::_startChildMacro(ObjectId macroId, int macroStep, uint8_t midiVel, uint8_t midiMod, bool pushPc) { std::list>::iterator vox = _allocateVoice(NativeSampleRate, true); - if (!(*vox)->loadSoundObject(macroId, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc)) + if (!(*vox)->loadMacroObject(macroId, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc)) { _destroyVoice(vox); return {}; @@ -751,9 +751,11 @@ std::shared_ptr Voice::startChildMacro(int8_t addNote, ObjectId macroId, m_state.m_initMod); } -bool Voice::_loadSoundMacro(ObjectId id, const SoundMacro* macroData, int macroStep, double ticksPerSec, +bool Voice::_loadSoundMacro(SoundMacroId id, const SoundMacro* macroData, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc) { + m_objectId = id; + if (m_state.m_pc.empty()) m_state.initialize(id, macroData, macroStep, ticksPerSec, midiKey, midiVel, midiMod); else @@ -768,19 +770,19 @@ bool Voice::_loadSoundMacro(ObjectId id, const SoundMacro* macroData, int macroS return true; } -bool Voice::_loadKeymap(ObjectId id, const Keymap* keymap, int macroStep, double ticksPerSec, +bool Voice::_loadKeymap(const Keymap* keymap, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc) { const Keymap& km = keymap[midiKey]; midiKey += km.transpose; - bool ret = loadSoundObject(id, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc); + bool ret = loadMacroObject(km.macro.id, 0, ticksPerSec, midiKey, midiVel, midiMod, pushPc); m_curVol = 1.f; _setPan((km.pan - 64) / 64.f); _setSurroundPan(-1.f); return ret; } -bool Voice::_loadLayer(ObjectId id, const std::vector& layer, int macroStep, double ticksPerSec, +bool Voice::_loadLayer(const std::vector& layer, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc) { bool ret = false; @@ -791,7 +793,7 @@ bool Voice::_loadLayer(ObjectId id, const std::vector& layer, int uint8_t mappingKey = midiKey + mapping.transpose; if (m_voxState != VoiceState::Playing) { - ret |= loadSoundObject(id, macroStep, ticksPerSec, mappingKey, midiVel, midiMod, pushPc); + ret |= loadMacroObject(mapping.macro.id, 0, ticksPerSec, mappingKey, midiVel, midiMod, pushPc); m_curVol = mapping.volume / 127.f; _setPan((mapping.pan - 64) / 64.f); _setSurroundPan((mapping.span - 64) / 64.f); @@ -799,7 +801,7 @@ bool Voice::_loadLayer(ObjectId id, const std::vector& layer, int else { std::shared_ptr vox = - _startChildMacro(id, macroStep, ticksPerSec, mappingKey, midiVel, midiMod, pushPc); + _startChildMacro(mapping.macro.id, 0, ticksPerSec, mappingKey, midiVel, midiMod, pushPc); if (vox) { vox->m_curVol = mapping.volume / 127.f; @@ -813,31 +815,35 @@ bool Voice::_loadLayer(ObjectId id, const std::vector& layer, int return ret; } -bool Voice::loadSoundObject(ObjectId objectId, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, +bool Voice::loadMacroObject(SoundMacroId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc) { if (m_destroyed) return false; - const SoundMacro* macroData = m_audioGroup.getPool().soundMacro(objectId); + const SoundMacro* macroData = m_audioGroup.getPool().soundMacro(macroId); if (macroData) - { - m_objectId = objectId; - return _loadSoundMacro(objectId, macroData, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc); - } + return _loadSoundMacro(macroId, macroData, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc); - const Keymap* keymap = m_audioGroup.getPool().keymap(objectId); - if (keymap) - { - m_objectId = objectId; - return _loadKeymap(objectId, keymap, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc); - } + return false; +} - const std::vector* layer = m_audioGroup.getPool().layer(objectId); - if (layer) +bool Voice::loadPageObject(ObjectId objectId, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod) +{ + if (m_destroyed) + return false; + + if (objectId.id & 0x8000) { - m_objectId = objectId; - return _loadLayer(objectId, *layer, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc); + const std::vector* layer = m_audioGroup.getPool().layer(objectId); + if (layer) + return _loadLayer(*layer, ticksPerSec, midiKey, midiVel, midiMod); + } + else + { + const Keymap* keymap = m_audioGroup.getPool().keymap(objectId); + if (keymap) + return _loadKeymap(keymap, ticksPerSec, midiKey, midiVel, midiMod); } return false; @@ -868,7 +874,7 @@ void Voice::keyOff() m_state.m_inWait = false; } else - loadSoundObject(m_keyoffTrap.macroId, m_keyoffTrap.macroStep, m_state.m_ticksPerSec, m_state.m_initKey, + loadMacroObject(m_keyoffTrap.macroId, m_keyoffTrap.macroStep, m_state.m_ticksPerSec, m_state.m_initKey, m_state.m_initVel, m_state.m_initMod); } else if (!m_curSample || m_curSample->first.m_loopLengthSamples) @@ -890,7 +896,7 @@ void Voice::message(int32_t val) if (m_messageTrap.macroId == std::get<0>(m_state.m_pc.back())) std::get<2>(m_state.m_pc.back()) = std::get<1>(m_state.m_pc.back())->assertPC(m_messageTrap.macroStep); else - loadSoundObject(m_messageTrap.macroId, m_messageTrap.macroStep, m_state.m_ticksPerSec, m_state.m_initKey, + loadMacroObject(m_messageTrap.macroId, m_messageTrap.macroStep, m_state.m_ticksPerSec, m_state.m_initKey, m_state.m_initVel, m_state.m_initMod); } }