From 7a38fd06765f7111cb15c2e70b68dfb3eb74e684 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sun, 15 Jul 2018 21:41:15 -1000 Subject: [PATCH] Work on project file reading --- Editor/MainWindow.cpp | 8 +- Editor/ProjectModel.cpp | 50 +- Editor/ProjectModel.hpp | 3 +- include/amuse/AudioGroup.hpp | 16 +- include/amuse/AudioGroupPool.hpp | 37 +- include/amuse/AudioGroupProject.hpp | 3 +- include/amuse/AudioGroupSampleDirectory.hpp | 199 ++++++-- include/amuse/Common.hpp | 2 +- include/amuse/Voice.hpp | 13 +- lib/AudioGroup.cpp | 48 +- lib/AudioGroupPool.cpp | 526 +++++++++++++------- lib/AudioGroupProject.cpp | 90 +++- lib/AudioGroupSampleDirectory.cpp | 378 +++++++++++++- lib/Common.cpp | 2 +- lib/Engine.cpp | 19 +- lib/Voice.cpp | 65 ++- 16 files changed, 1108 insertions(+), 351 deletions(-) diff --git a/Editor/MainWindow.cpp b/Editor/MainWindow.cpp index 20093a9..8cf58c1 100644 --- a/Editor/MainWindow.cpp +++ b/Editor/MainWindow.cpp @@ -225,10 +225,10 @@ 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))) + if (!m_projectModel->importGroupData(SysStringToQString(p.first), std::move(p.second))) return; } + m_projectModel->extractSamples(ProjectModel::ImportMode(impMode), this); m_projectModel->saveToFile(this); return; } @@ -254,10 +254,10 @@ void MainWindow::importAction() /* Handle single container */ 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))) + if (!m_projectModel->importGroupData(SysStringToQString(p.first), std::move(p.second))) return; + m_projectModel->extractSamples(ProjectModel::ImportMode(impMode), this); m_projectModel->saveToFile(this); } diff --git a/Editor/ProjectModel.cpp b/Editor/ProjectModel.cpp index 489fb70..6e222a1 100644 --- a/Editor/ProjectModel.cpp +++ b/Editor/ProjectModel.cpp @@ -17,7 +17,7 @@ ProjectModel::ProjectGroup::ProjectGroup(amuse::IntrusiveAudioGroupData&& data) m_sdir(amuse::AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(m_data)) {} -bool ProjectModel::importGroupData(const QString& groupName, amuse::IntrusiveAudioGroupData&& data, ImportMode mode) +bool ProjectModel::importGroupData(const QString& groupName, amuse::IntrusiveAudioGroupData&& data) { setIdDatabases(); @@ -28,6 +28,42 @@ bool ProjectModel::importGroupData(const QString& groupName, amuse::IntrusiveAud return true; } +bool ProjectModel::extractSamples(ImportMode mode, QWidget* parent) +{ + setIdDatabases(); + + if (!MkPath(m_dir.path(), parent)) + return false; + + for (auto& g : m_groups) + { + g.second.setIdDatabases(); + + QDir dir(QFileInfo(m_dir, g.first).filePath()); + if (!MkPath(dir.path(), parent)) + return false; + + amuse::SystemString sysDir = QStringToSysString(dir.path()); + switch (mode) + { + case ImportMode::Original: + g.second.m_sdir.extractAllCompressed(sysDir, g.second.m_data.getSamp()); + break; + case ImportMode::WAVs: + g.second.m_sdir.extractAllWAV(sysDir, g.second.m_data.getSamp()); + break; + case ImportMode::Both: + g.second.m_sdir.extractAllCompressed(sysDir, g.second.m_data.getSamp()); + g.second.m_sdir.extractAllWAV(sysDir, g.second.m_data.getSamp()); + break; + default: + break; + } + } + + return true; +} + bool ProjectModel::saveToFile(QWidget* parent) { setIdDatabases(); @@ -42,15 +78,9 @@ bool ProjectModel::saveToFile(QWidget* parent) return false; g.second.setIdDatabases(); - { - athena::io::FileWriter fo(QStringToSysString(dir.filePath("!project.yaml"))); - g.second.m_proj.toYAML(fo); - } - { - athena::io::FileWriter fo(QStringToSysString(dir.filePath("!pool.yaml"))); - g.second.m_pool.toYAML(fo); - } - //g.second.m_sdir.sampleEntries() + amuse::SystemString groupPath = QStringToSysString(dir.path()); + g.second.m_proj.toYAML(groupPath); + g.second.m_pool.toYAML(groupPath); } return true; diff --git a/Editor/ProjectModel.hpp b/Editor/ProjectModel.hpp index c36e78b..2813ff7 100644 --- a/Editor/ProjectModel.hpp +++ b/Editor/ProjectModel.hpp @@ -58,7 +58,8 @@ private: public: explicit ProjectModel(const QString& path, QObject* parent = Q_NULLPTR); - bool importGroupData(const QString& groupName, amuse::IntrusiveAudioGroupData&& data, ImportMode mode); + bool importGroupData(const QString& groupName, amuse::IntrusiveAudioGroupData&& data); + bool extractSamples(ImportMode mode, QWidget* parent); bool saveToFile(QWidget* parent); QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; diff --git a/include/amuse/AudioGroup.hpp b/include/amuse/AudioGroup.hpp index 9822fe0..862a322 100644 --- a/include/amuse/AudioGroup.hpp +++ b/include/amuse/AudioGroup.hpp @@ -9,30 +9,26 @@ namespace amuse { class AudioGroupData; -using Sample = std::pair; - /** Runtime audio group index container */ class AudioGroup { AudioGroupProject m_proj; AudioGroupPool m_pool; AudioGroupSampleDirectory m_sdir; - const unsigned char* m_samp; - DataFormat m_fmt; + const unsigned char* m_samp = nullptr; + SystemString m_groupPath; bool m_valid; public: operator bool() const { return m_valid; } - AudioGroup(const AudioGroupData& data, GCNDataTag); - AudioGroup(const AudioGroupData& data, bool absOffs, N64DataTag); - AudioGroup(const AudioGroupData& data, bool absOffs, PCDataTag); + explicit AudioGroup(const AudioGroupData& data); + explicit AudioGroup(SystemStringView groupPath); - const Sample* getSample(int sfxId) const; - const unsigned char* getSampleData(uint32_t offset) const; + const AudioGroupSampleDirectory::Entry* getSample(SampleId sfxId) const; + const unsigned char* getSampleData(SampleId sfxId, const AudioGroupSampleDirectory::Entry* sample) const; const AudioGroupProject& getProj() const { return m_proj; } const AudioGroupPool& getPool() const { return m_pool; } const AudioGroupSampleDirectory& getSdir() const { return m_sdir; } - DataFormat getDataFormat() const { return m_fmt; } }; } diff --git a/include/amuse/AudioGroupPool.hpp b/include/amuse/AudioGroupPool.hpp index 8c769b8..4f059c4 100644 --- a/include/amuse/AudioGroupPool.hpp +++ b/include/amuse/AudioGroupPool.hpp @@ -7,6 +7,7 @@ #include #include "Entity.hpp" #include "Common.hpp" +#include "athena/MemoryReader.hpp" namespace amuse { @@ -122,9 +123,6 @@ struct SoundMacro 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 { @@ -955,6 +953,11 @@ struct SoundMacro CmdOp Isa() const { return CmdOp::IfLess; } }; + template + static std::unique_ptr MakeCmd(R& r); + static std::string_view CmdOpToStr(CmdOp op); + static CmdOp CmdStrToOp(std::string_view op); + std::vector> m_cmds; int assertPC(int pc) const; @@ -977,6 +980,13 @@ 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 */ @@ -993,6 +1003,8 @@ struct ADSR : ITable double getDecay() const { return (decay == 0x8000) ? 0.0 : (decay / 1000.0); } double getSustain() const { return sustain / double(0x1000); } double getRelease() const { return release / 1000.0; } + + Type Isa() const { return ITable::Type::ADSR; } }; /** Defines phase-based volume curve for macro volume control (modified DLS standard) */ @@ -1023,6 +1035,8 @@ struct ADSRDLS : ITable 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 */ @@ -1031,6 +1045,8 @@ struct Curve : ITable AT_DECL_EXPLICIT_DNA_YAML AT_DECL_DNAV std::vector data; + + Type Isa() const { return ITable::Type::Curve; } }; /** Maps individual MIDI keys to sound-entity as indexed in table @@ -1128,25 +1144,26 @@ struct LayerMapping : BigDNA /** Database of functional objects within Audio Group */ class AudioGroupPool { - std::unordered_map m_soundMacros; - std::unordered_map> m_tables; - std::unordered_map m_keymaps; - std::unordered_map> m_layers; + std::unordered_map m_soundMacros; + std::unordered_map> m_tables; + std::unordered_map m_keymaps; + std::unordered_map> m_layers; AudioGroupPool() = default; template static AudioGroupPool _AudioGroupPool(athena::io::IStreamReader& r); public: static AudioGroupPool CreateAudioGroupPool(const AudioGroupData& data); + static AudioGroupPool CreateAudioGroupPool(SystemStringView groupPath); const SoundMacro* soundMacro(ObjectId id) const; const Keymap* keymap(ObjectId id) const; const std::vector* layer(ObjectId id) const; 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)); } + const ADSRDLS* tableAsAdsrDLS(ObjectId id) const; + const Curve* tableAsCurves(ObjectId id) const; - bool toYAML(athena::io::IStreamWriter& w) const; + bool toYAML(SystemStringView groupPath) const; }; } diff --git a/include/amuse/AudioGroupProject.hpp b/include/amuse/AudioGroupProject.hpp index d5da3a6..6556743 100644 --- a/include/amuse/AudioGroupProject.hpp +++ b/include/amuse/AudioGroupProject.hpp @@ -193,6 +193,7 @@ class AudioGroupProject static void BootstrapObjectIDs(athena::io::IStreamReader& r, bool absOffs); public: static AudioGroupProject CreateAudioGroupProject(const AudioGroupData& data); + static AudioGroupProject CreateAudioGroupProject(SystemStringView groupPath); static void BootstrapObjectIDs(const AudioGroupData& data); const SongGroupIndex* getSongGroupIndex(int groupId) const; @@ -201,7 +202,7 @@ public: 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; + bool toYAML(SystemStringView groupPath) const; }; } diff --git a/include/amuse/AudioGroupSampleDirectory.hpp b/include/amuse/AudioGroupSampleDirectory.hpp index c021275..5847ebd 100644 --- a/include/amuse/AudioGroupSampleDirectory.hpp +++ b/include/amuse/AudioGroupSampleDirectory.hpp @@ -9,18 +9,135 @@ namespace amuse { class AudioGroupData; +struct DSPADPCMHeader : BigDNA +{ + AT_DECL_DNA + Value x0_num_samples; + Value x4_num_nibbles; + Value x8_sample_rate; + Value xc_loop_flag; + Value xe_format = 0; /* 0 for ADPCM */ + Value x10_loop_start_nibble = 0; + Value x14_loop_end_nibble = 0; + Value x18_ca = 0; + Value x1c_coef[8][2]; + Value x3c_gain = 0; + Value x3e_ps; + Value x40_hist1; + Value x42_hist2; + Value x44_loop_ps; + Value x46_loop_hist1 = 0; + Value x48_loop_hist2 = 0; + Value m_pitch = 0; // Stash this in the padding + Seek<21, athena::Current> pad; +}; + +struct WAVFormatChunk : LittleDNA +{ + AT_DECL_DNA + Value sampleFmt = 1; + Value numChannels = 1; + Value sampleRate; + Value byteRate; // sampleRate * numChannels * bitsPerSample/8 + Value blockAlign = 2; // numChannels * bitsPerSample/8 + Value bitsPerSample = 16; +}; + +struct WAVSampleChunk : LittleDNA +{ + AT_DECL_DNA + Value smplManufacturer = 0; + Value smplProduct = 0; + Value smplPeriod; // 1 / sampleRate in nanoseconds + Value midiNote; // native MIDI note of sample + Value midiPitchFrac = 0; + Value smpteFormat = 0; + Value smpteOffset = 0; + Value numSampleLoops = 0; + Value additionalDataSize = 0; +}; + +struct WAVSampleLoop : LittleDNA +{ + AT_DECL_DNA + Value cuePointId = 0; + Value loopType = 0; // 0: forward loop + Value start; // in bytes + Value end; // in bytes + Value fraction = 0; + Value playCount = 0; +}; + +struct WAVHeader : LittleDNA +{ + AT_DECL_DNA + Value riffMagic = SBIG('RIFF'); + Value wavChuckSize; // everything - 8 + Value wavMagic = SBIG('WAVE'); + + Value fmtMagic = SBIG('fmt '); + Value fmtChunkSize = 16; + WAVFormatChunk fmtChunk; + + Value smplMagic = SBIG('smpl'); + Value smplChunkSize = 36; // 36 + numSampleLoops*24 + WAVSampleChunk smplChunk; + + Value dataMagic = SBIG('data'); + Value dataChunkSize; // numSamples * numChannels * bitsPerSample/8 +}; + +struct WAVHeaderLoop : LittleDNA +{ + AT_DECL_DNA + Value riffMagic = SBIG('RIFF'); + Value wavChuckSize; // everything - 8 + Value wavMagic = SBIG('WAVE'); + + Value fmtMagic = SBIG('fmt '); + Value fmtChunkSize = 16; + WAVFormatChunk fmtChunk; + + Value smplMagic = SBIG('smpl'); + Value smplChunkSize = 60; // 36 + numSampleLoops*24 + WAVSampleChunk smplChunk; + WAVSampleLoop sampleLoop; + + Value dataMagic = SBIG('data'); + Value dataChunkSize; // numSamples * numChannels * bitsPerSample/8 +}; + +enum class SampleFormat : uint8_t +{ + DSP, /**< GCN DSP-ucode ADPCM (very common for GameCube games) */ + DSP_DRUM, /**< GCN DSP-ucode ADPCM (seems to be set into drum samples for expanding their amplitude appropriately) */ + PCM, /**< Big-endian PCM found in MusyX2 demo GM instruments */ + N64, /**< 2-stage VADPCM coding with SAMP-embedded codebooks */ + PCM_PC /**< Little-endian PCM found in PC Rogue Squadron (actually enum 0 which conflicts with DSP-ADPCM) */ +}; + /** Indexes individual samples in SAMP chunk */ class AudioGroupSampleDirectory { friend class AudioGroup; public: - enum class SampleFormat : atUint8 - { - DSP, - DSP2, - PCM, - N64 + union ADPCMParms { + struct DSPParms + { + uint16_t m_bytesPerFrame; + uint8_t m_ps; + uint8_t m_lps; + int16_t m_hist2; + int16_t m_hist1; + int16_t m_coefs[8][2]; + } dsp; + struct VADPCMParms + { + int16_t m_coefs[8][2][8]; + } vadpcm; + void swapBigDSP(); + void swapBigVADPCM(); }; template @@ -28,11 +145,12 @@ public: EntryDNA : BigDNA { AT_DECL_DNA - SFXIdDNA m_sfxId; + SampleIdDNA m_sfxId; + Seek<2, athena::Current> pad; Value m_sampleOff; Value m_unk; Value m_pitch; - Seek<1, athena::Current> pad; + Seek<1, athena::Current> pad2; Value m_sampleRate; Value m_numSamples; // Top 8 bits is SampleFormat Value m_loopStartSample; @@ -44,7 +162,8 @@ public: MusyX1SdirEntry : BigDNA { AT_DECL_DNA - SFXIdDNA m_sfxId; + SampleIdDNA m_sfxId; + Seek<2, athena::Current> pad; Value m_sampleOff; Value m_pitchSampleRate; Value m_numSamples; @@ -56,7 +175,8 @@ public: MusyX1AbsSdirEntry : BigDNA { AT_DECL_DNA - SFXIdDNA m_sfxId; + SampleIdDNA m_sfxId; + Seek<2, athena::Current> pad; Value m_sampleOff; Value m_unk; Value m_pitchSampleRate; @@ -66,14 +186,23 @@ public: }; struct Entry { - 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; + atUint32 m_sampleOff = 0; + atUint32 m_unk = 0; + atUint8 m_pitch = 0; + atUint16 m_sampleRate = 0; + atUint32 m_numSamples = 0; // Top 8 bits is SampleFormat + atUint32 m_loopStartSample = 0; + atUint32 m_loopLengthSamples = 0; + atUint32 m_adpcmParmOffset = 0; + + /* Stored out-of-band in a platform-dependent way */ + ADPCMParms m_ADPCMParms; + + /* In-memory storage of an individual sample. Editors use this structure + * to override the loaded sample with a file-backed version without repacking + * the sample data into a SAMP block. */ + time_t m_looseModTime = 0; + std::unique_ptr m_looseData; Entry() = default; @@ -113,36 +242,32 @@ public: ret.m_adpcmParmOffset = m_adpcmParmOffset; return ret; } - }; - union ADPCMParms { - struct DSPParms - { - uint16_t m_bytesPerFrame; - uint8_t m_ps; - uint8_t m_lps; - int16_t m_hist2; - int16_t m_hist1; - int16_t m_coefs[8][2]; - } dsp; - struct VADPCMParms - { - int16_t m_coefs[8][2][8]; - } vadpcm; - void swapBigDSP(); - void swapBigVADPCM(); + void loadLooseData(SystemStringView basePath); }; private: - std::unordered_map> m_entries; + std::unordered_map m_entries; + static void _extractWAV(SampleId id, const Entry& ent, amuse::SystemStringView destDir, + const unsigned char* samp); + static void _extractCompressed(SampleId id, const Entry& ent, amuse::SystemStringView destDir, + const unsigned char* samp); + + AudioGroupSampleDirectory() = default; public: 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); + static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(SystemStringView groupPath); - const std::unordered_map>& sampleEntries() const { return m_entries; } + const std::unordered_map& sampleEntries() const { return m_entries; } + + void extractWAV(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const; + void extractAllWAV(amuse::SystemStringView destDir, const unsigned char* samp) const; + void extractCompressed(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const; + void extractAllCompressed(amuse::SystemStringView destDir, const unsigned char* samp) const; }; } diff --git a/include/amuse/Common.hpp b/include/amuse/Common.hpp index 0e3dec8..8db53ba 100644 --- a/include/amuse/Common.hpp +++ b/include/amuse/Common.hpp @@ -464,7 +464,7 @@ struct NameDB std::unordered_map m_stringToId; std::unordered_map m_idToString; - ObjectId generateId(Type tp); + ObjectId generateId(Type tp) const; 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; diff --git a/include/amuse/Voice.hpp b/include/amuse/Voice.hpp index b439697..85e90a0 100644 --- a/include/amuse/Voice.hpp +++ b/include/amuse/Voice.hpp @@ -58,16 +58,7 @@ class Voice : public Entity std::list> m_childVoices; /**< Child voices for PLAYMACRO usage */ uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */ - enum class SampleFormat : uint8_t - { - DSP, /**< GCN DSP-ucode ADPCM (very common for GameCube games) */ - DSP_DRUM, /**< GCN DSP-ucode ADPCM (seems to be set into drum samples for expanding their amplitude - appropriately) */ - PCM, /**< Big-endian PCM found in MusyX2 demo GM instruments */ - N64, /**< 2-stage VADPCM coding with SAMP-embedded codebooks */ - PCM_PC /**< Little-endian PCM found in PC Rogue Squadron (actually enum 0 which conflicts with DSP-ADPCM) */ - }; - const Sample* m_curSample = nullptr; /**< Current sample entry playing */ + const AudioGroupSampleDirectory::Entry* m_curSample = nullptr; /**< Current sample entry playing */ const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */ SampleFormat m_curFormat; /**< Current sample format playing */ uint32_t m_curSamplePos = 0; /**< Current sample position */ @@ -236,7 +227,7 @@ public: void message(int32_t val); /** Start playing specified sample from within group, optionally by sample offset */ - void startSample(int16_t sampId, int32_t offset); + void startSample(SampleId sampId, int32_t offset); /** Stop playing current sample */ void stopSample(); diff --git a/lib/AudioGroup.cpp b/lib/AudioGroup.cpp index a077b42..751013d 100644 --- a/lib/AudioGroup.cpp +++ b/lib/AudioGroup.cpp @@ -4,34 +4,21 @@ namespace amuse { -AudioGroup::AudioGroup(const AudioGroupData& data, GCNDataTag) +AudioGroup::AudioGroup(const AudioGroupData& data) : m_proj(AudioGroupProject::CreateAudioGroupProject(data)) , m_pool(AudioGroupPool::CreateAudioGroupPool(data)) , m_sdir(AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(data)) , m_samp(data.getSamp()) -, m_fmt(DataFormat::GCN) -{ -} +{} -AudioGroup::AudioGroup(const AudioGroupData& data, bool absOffs, N64DataTag) -: m_proj(AudioGroupProject::CreateAudioGroupProject(data)) -, m_pool(AudioGroupPool::CreateAudioGroupPool(data)) -, m_sdir(AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(data)) -, m_samp(data.getSamp()) -, m_fmt(DataFormat::N64) -{ -} +AudioGroup::AudioGroup(SystemStringView groupPath) +: m_proj(AudioGroupProject::CreateAudioGroupProject(groupPath)) +, m_pool(AudioGroupPool::CreateAudioGroupPool(groupPath)) +, m_sdir(AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(groupPath)) +, m_groupPath(groupPath) +{} -AudioGroup::AudioGroup(const AudioGroupData& data, bool absOffs, PCDataTag) -: m_proj(AudioGroupProject::CreateAudioGroupProject(data)) -, m_pool(AudioGroupPool::CreateAudioGroupPool(data)) -, m_sdir(AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(data)) -, m_samp(data.getSamp()) -, m_fmt(DataFormat::PC) -{ -} - -const Sample* AudioGroup::getSample(int sfxId) const +const AudioGroupSampleDirectory::Entry* AudioGroup::getSample(SampleId sfxId) const { auto search = m_sdir.m_entries.find(sfxId); if (search == m_sdir.m_entries.cend()) @@ -39,5 +26,20 @@ const Sample* AudioGroup::getSample(int sfxId) const return &search->second; } -const unsigned char* AudioGroup::getSampleData(uint32_t offset) const { return m_samp + offset; } +const unsigned char* AudioGroup::getSampleData(SampleId sfxId, const AudioGroupSampleDirectory::Entry* sample) const +{ + if (sample->m_looseData) + { +#if _WIN32 + SystemString basePath = m_groupPath + _S('/') + + athena::utility::utf8ToWide(SampleId::CurNameDB->resolveNameFromId(sfxId)); +#else + SystemString basePath = m_groupPath + _S('/') + + SampleId::CurNameDB->resolveNameFromId(sfxId).data(); +#endif + const_cast(sample)->loadLooseData(basePath); + return sample->m_looseData.get(); + } + return m_samp + sample->m_sampleOff; +} } diff --git a/lib/AudioGroupPool.cpp b/lib/AudioGroupPool.cpp index 513f23f..3e1feb1 100644 --- a/lib/AudioGroupPool.cpp +++ b/lib/AudioGroupPool.cpp @@ -2,8 +2,9 @@ #include "amuse/Common.hpp" #include "amuse/Entity.hpp" #include "amuse/AudioGroupData.hpp" -#include "athena/MemoryReader.hpp" #include "logvisor/logvisor.hpp" +#include "athena/FileWriter.hpp" +#include "athena/FileReader.hpp" using namespace std::literals; @@ -51,11 +52,11 @@ AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r) auto& ptr = ret.m_tables[objHead.objectId.id]; switch (objHead.size) { - case 8: + case 0x10: ptr = std::make_unique(); static_cast(*ptr).read(r); break; - case 0x14: + case 0x1c: ptr = std::make_unique(); static_cast(*ptr).read(r); break; @@ -123,11 +124,147 @@ AudioGroupPool AudioGroupPool::CreateAudioGroupPool(const AudioGroupData& data) } } -template -static std::unique_ptr MakeCmd(athena::io::MemoryReader& r) +AudioGroupPool AudioGroupPool::CreateAudioGroupPool(SystemStringView groupPath) { - std::unique_ptr ret = std::make_unique(); - static_cast(*ret).read(r); + AudioGroupPool ret; + SystemString poolPath(groupPath); + poolPath += _S("/!pool.yaml"); + athena::io::FileReader fi(poolPath); + + if (!fi.hasError()) + { + athena::io::YAMLDocReader r; + if (r.parse(&fi) && r.ValidateClassType("amuse::Pool")) + { + if (auto __r = r.enterSubRecord("soundMacros")) + { + ret.m_soundMacros.reserve(r.getCurNode()->m_mapChildren.size()); + for (const auto& sm : r.getCurNode()->m_mapChildren) + { + ObjectId macroId = SoundMacroId::CurNameDB->generateId(NameDB::Type::SoundMacro); + SoundMacroId::CurNameDB->registerPair(sm.first, macroId); + } + } + + if (auto __r = r.enterSubRecord("tables")) + { + ret.m_tables.reserve(r.getCurNode()->m_mapChildren.size()); + for (const auto& t : r.getCurNode()->m_mapChildren) + { + if (auto __v = r.enterSubRecord(t.first.c_str())) + { + ObjectId tableId = TableId::CurNameDB->generateId(NameDB::Type::Table); + TableId::CurNameDB->registerPair(t.first, tableId); + } + } + } + + if (auto __r = r.enterSubRecord("keymaps")) + { + ret.m_keymaps.reserve(r.getCurNode()->m_mapChildren.size()); + for (const auto& k : r.getCurNode()->m_mapChildren) + if (auto __v = r.enterSubRecord(k.first.c_str())) + { + ObjectId keymapId = KeymapId::CurNameDB->generateId(NameDB::Type::Keymap); + KeymapId::CurNameDB->registerPair(k.first, keymapId); + } + } + + if (auto __r = r.enterSubRecord("layers")) + { + for (const auto& l : r.getCurNode()->m_mapChildren) + { + size_t mappingCount; + if (auto __v = r.enterSubVector(l.first.c_str(), mappingCount)) + { + ObjectId layersId = LayersId::CurNameDB->generateId(NameDB::Type::Layer); + LayersId::CurNameDB->registerPair(l.first, layersId); + } + } + } + + if (auto __r = r.enterSubRecord("soundMacros")) + { + ret.m_soundMacros.reserve(r.getCurNode()->m_mapChildren.size()); + for (const auto& sm : r.getCurNode()->m_mapChildren) + { + SoundMacro& smOut = ret.m_soundMacros[SoundMacroId::CurNameDB->resolveIdFromName(sm.first)]; + size_t cmdCount; + if (auto __v = r.enterSubVector(sm.first.c_str(), cmdCount)) + { + smOut.m_cmds.reserve(cmdCount); + for (int c = 0; c < cmdCount; ++c) + if (auto __r2 = r.enterSubRecord(nullptr)) + smOut.m_cmds.push_back(SoundMacro::MakeCmd(r)); + } + } + } + + if (auto __r = r.enterSubRecord("tables")) + { + ret.m_tables.reserve(r.getCurNode()->m_mapChildren.size()); + for (const auto& t : r.getCurNode()->m_mapChildren) + { + if (auto __v = r.enterSubRecord(t.first.c_str())) + { + std::unique_ptr& tableOut = ret.m_tables[TableId::CurNameDB->resolveIdFromName(t.first)]; + if (auto __att = r.enterSubRecord("attack")) + { + __att.leave(); + if (auto __vta = r.enterSubRecord("velToAttack")) + { + __vta.leave(); + tableOut = std::make_unique(); + static_cast(*tableOut).read(r); + } + else + { + tableOut = std::make_unique(); + static_cast(*tableOut).read(r); + } + } + else if (auto __dat = r.enterSubRecord("data")) + { + __dat.leave(); + tableOut = std::make_unique(); + static_cast(*tableOut).read(r); + } + } + } + } + + if (auto __r = r.enterSubRecord("keymaps")) + { + ret.m_keymaps.reserve(r.getCurNode()->m_mapChildren.size()); + for (const auto& k : r.getCurNode()->m_mapChildren) + if (auto __v = r.enterSubRecord(k.first.c_str())) + ret.m_keymaps[KeymapId::CurNameDB->resolveIdFromName(k.first)].read(r); + } + + if (auto __r = r.enterSubRecord("layers")) + { + ret.m_layers.reserve(r.getCurNode()->m_mapChildren.size()); + for (const auto& l : r.getCurNode()->m_mapChildren) + { + size_t mappingCount; + if (auto __v = r.enterSubVector(l.first.c_str(), mappingCount)) + { + std::vector& layOut = ret.m_layers[LayersId::CurNameDB->resolveIdFromName(l.first)]; + layOut.reserve(mappingCount); + for (int lm = 0; lm < mappingCount; ++lm) + { + if (auto __r2 = r.enterSubRecord(nullptr)) + { + layOut.emplace_back(); + layOut.back().read(r); + } + } + } + } + } + } + } + return ret; } @@ -153,167 +290,7 @@ void SoundMacro::readCmds(athena::io::IStreamReader& r, uint32_t size) uint32_t data[2]; athena::io::Read::Do({}, data, r); athena::io::MemoryReader r(data, 8); - std::unique_ptr cmd; - switch (CmdOp(r.readUByte())) - { - case CmdOp::End: - cmd = MakeCmd(r); break; - case CmdOp::Stop: - cmd = MakeCmd(r); break; - case CmdOp::SplitKey: - cmd = MakeCmd(r); break; - case CmdOp::SplitVel: - cmd = MakeCmd(r); break; - case CmdOp::WaitTicks: - cmd = MakeCmd(r); break; - case CmdOp::Loop: - cmd = MakeCmd(r); break; - case CmdOp::Goto: - cmd = MakeCmd(r); break; - case CmdOp::WaitMs: - cmd = MakeCmd(r); break; - case CmdOp::PlayMacro: - cmd = MakeCmd(r); break; - case CmdOp::SendKeyOff: - cmd = MakeCmd(r); break; - case CmdOp::SplitMod: - cmd = MakeCmd(r); break; - case CmdOp::PianoPan: - cmd = MakeCmd(r); break; - case CmdOp::SetAdsr: - cmd = MakeCmd(r); break; - case CmdOp::ScaleVolume: - cmd = MakeCmd(r); break; - case CmdOp::Panning: - cmd = MakeCmd(r); break; - case CmdOp::Envelope: - cmd = MakeCmd(r); break; - case CmdOp::StartSample: - cmd = MakeCmd(r); break; - case CmdOp::StopSample: - cmd = MakeCmd(r); break; - case CmdOp::KeyOff: - cmd = MakeCmd(r); break; - case CmdOp::SplitRnd: - cmd = MakeCmd(r); break; - case CmdOp::FadeIn: - cmd = MakeCmd(r); break; - case CmdOp::Spanning: - cmd = MakeCmd(r); break; - case CmdOp::SetAdsrCtrl: - cmd = MakeCmd(r); break; - case CmdOp::RndNote: - cmd = MakeCmd(r); break; - case CmdOp::AddNote: - cmd = MakeCmd(r); break; - case CmdOp::SetNote: - cmd = MakeCmd(r); break; - case CmdOp::LastNote: - cmd = MakeCmd(r); break; - case CmdOp::Portamento: - cmd = MakeCmd(r); break; - case CmdOp::Vibrato: - cmd = MakeCmd(r); break; - case CmdOp::PitchSweep1: - cmd = MakeCmd(r); break; - case CmdOp::PitchSweep2: - cmd = MakeCmd(r); break; - case CmdOp::SetPitch: - cmd = MakeCmd(r); break; - case CmdOp::SetPitchAdsr: - cmd = MakeCmd(r); break; - case CmdOp::ScaleVolumeDLS: - cmd = MakeCmd(r); break; - case CmdOp::Mod2Vibrange: - cmd = MakeCmd(r); break; - case CmdOp::SetupTremolo: - cmd = MakeCmd(r); break; - case CmdOp::Return: - cmd = MakeCmd(r); break; - case CmdOp::GoSub: - cmd = MakeCmd(r); break; - case CmdOp::TrapEvent: - cmd = MakeCmd(r); break; - case CmdOp::UntrapEvent: - cmd = MakeCmd(r); break; - case CmdOp::SendMessage: - cmd = MakeCmd(r); break; - case CmdOp::GetMessage: - cmd = MakeCmd(r); break; - case CmdOp::GetVid: - cmd = MakeCmd(r); break; - case CmdOp::AddAgeCount: - cmd = MakeCmd(r); break; - case CmdOp::SetAgeCount: - cmd = MakeCmd(r); break; - case CmdOp::SendFlag: - cmd = MakeCmd(r); break; - case CmdOp::PitchWheelR: - cmd = MakeCmd(r); break; - case CmdOp::SetPriority: - cmd = MakeCmd(r); break; - case CmdOp::AddPriority: - cmd = MakeCmd(r); break; - case CmdOp::AgeCntSpeed: - cmd = MakeCmd(r); break; - case CmdOp::AgeCntVel: - cmd = MakeCmd(r); break; - case CmdOp::VolSelect: - cmd = MakeCmd(r); break; - case CmdOp::PanSelect: - cmd = MakeCmd(r); break; - case CmdOp::PitchWheelSelect: - cmd = MakeCmd(r); break; - case CmdOp::ModWheelSelect: - cmd = MakeCmd(r); break; - case CmdOp::PedalSelect: - cmd = MakeCmd(r); break; - case CmdOp::PortamentoSelect: - cmd = MakeCmd(r); break; - case CmdOp::ReverbSelect: - cmd = MakeCmd(r); break; - case CmdOp::SpanSelect: - cmd = MakeCmd(r); break; - case CmdOp::DopplerSelect: - cmd = MakeCmd(r); break; - case CmdOp::TremoloSelect: - cmd = MakeCmd(r); break; - case CmdOp::PreASelect: - cmd = MakeCmd(r); break; - case CmdOp::PreBSelect: - cmd = MakeCmd(r); break; - case CmdOp::PostBSelect: - cmd = MakeCmd(r); break; - case CmdOp::AuxAFXSelect: - cmd = MakeCmd(r); break; - case CmdOp::AuxBFXSelect: - cmd = MakeCmd(r); break; - case CmdOp::SetupLFO: - cmd = MakeCmd(r); break; - case CmdOp::ModeSelect: - cmd = MakeCmd(r); break; - case CmdOp::SetKeygroup: - cmd = MakeCmd(r); break; - case CmdOp::SRCmodeSelect: - cmd = MakeCmd(r); break; - case CmdOp::AddVars: - cmd = MakeCmd(r); break; - case CmdOp::SubVars: - cmd = MakeCmd(r); break; - case CmdOp::MulVars: - cmd = MakeCmd(r); break; - case CmdOp::DivVars: - cmd = MakeCmd(r); break; - case CmdOp::AddIVars: - cmd = MakeCmd(r); break; - case CmdOp::IfEqual: - cmd = MakeCmd(r); break; - case CmdOp::IfLess: - cmd = MakeCmd(r); break; - default: - break; - } - m_cmds.push_back(std::move(cmd)); + m_cmds.push_back(MakeCmd(r)); } } template void SoundMacro::readCmds(athena::io::IStreamReader& r, uint32_t size); @@ -346,11 +323,213 @@ const std::vector* AudioGroupPool::layer(ObjectId id) const const ADSR* AudioGroupPool::tableAsAdsr(ObjectId id) const { auto search = m_tables.find(id); - if (search == m_tables.cend()) + if (search == m_tables.cend() || search->second->Isa() != ITable::Type::ADSR) return nullptr; return static_cast(search->second.get()); } +const ADSRDLS* AudioGroupPool::tableAsAdsrDLS(ObjectId id) const +{ + auto search = m_tables.find(id); + if (search == m_tables.cend() || search->second->Isa() != ITable::Type::ADSRDLS) + return nullptr; + return static_cast(search->second.get()); +} + +const Curve* AudioGroupPool::tableAsCurves(ObjectId id) const +{ + auto search = m_tables.find(id); + if (search == m_tables.cend() || search->second->Isa() != ITable::Type::Curve) + return nullptr; + return static_cast(search->second.get()); +} + +template +static std::unique_ptr _MakeCmd(R& r) +{ + std::unique_ptr ret = std::make_unique(); + static_cast(*ret).read(r); + return ret; +} + +static SoundMacro::CmdOp _ReadCmdOp(athena::io::MemoryReader& r) +{ + return SoundMacro::CmdOp(r.readUByte()); +} + +static SoundMacro::CmdOp _ReadCmdOp(athena::io::YAMLDocReader& r) +{ + return SoundMacro::CmdStrToOp(r.readString("cmdOp")); +} + +template +std::unique_ptr SoundMacro::MakeCmd(R& r) +{ + std::unique_ptr cmd; + switch (_ReadCmdOp(r)) + { + case CmdOp::End: + cmd = _MakeCmd(r); break; + case CmdOp::Stop: + cmd = _MakeCmd(r); break; + case CmdOp::SplitKey: + cmd = _MakeCmd(r); break; + case CmdOp::SplitVel: + cmd = _MakeCmd(r); break; + case CmdOp::WaitTicks: + cmd = _MakeCmd(r); break; + case CmdOp::Loop: + cmd = _MakeCmd(r); break; + case CmdOp::Goto: + cmd = _MakeCmd(r); break; + case CmdOp::WaitMs: + cmd = _MakeCmd(r); break; + case CmdOp::PlayMacro: + cmd = _MakeCmd(r); break; + case CmdOp::SendKeyOff: + cmd = _MakeCmd(r); break; + case CmdOp::SplitMod: + cmd = _MakeCmd(r); break; + case CmdOp::PianoPan: + cmd = _MakeCmd(r); break; + case CmdOp::SetAdsr: + cmd = _MakeCmd(r); break; + case CmdOp::ScaleVolume: + cmd = _MakeCmd(r); break; + case CmdOp::Panning: + cmd = _MakeCmd(r); break; + case CmdOp::Envelope: + cmd = _MakeCmd(r); break; + case CmdOp::StartSample: + cmd = _MakeCmd(r); break; + case CmdOp::StopSample: + cmd = _MakeCmd(r); break; + case CmdOp::KeyOff: + cmd = _MakeCmd(r); break; + case CmdOp::SplitRnd: + cmd = _MakeCmd(r); break; + case CmdOp::FadeIn: + cmd = _MakeCmd(r); break; + case CmdOp::Spanning: + cmd = _MakeCmd(r); break; + case CmdOp::SetAdsrCtrl: + cmd = _MakeCmd(r); break; + case CmdOp::RndNote: + cmd = _MakeCmd(r); break; + case CmdOp::AddNote: + cmd = _MakeCmd(r); break; + case CmdOp::SetNote: + cmd = _MakeCmd(r); break; + case CmdOp::LastNote: + cmd = _MakeCmd(r); break; + case CmdOp::Portamento: + cmd = _MakeCmd(r); break; + case CmdOp::Vibrato: + cmd = _MakeCmd(r); break; + case CmdOp::PitchSweep1: + cmd = _MakeCmd(r); break; + case CmdOp::PitchSweep2: + cmd = _MakeCmd(r); break; + case CmdOp::SetPitch: + cmd = _MakeCmd(r); break; + case CmdOp::SetPitchAdsr: + cmd = _MakeCmd(r); break; + case CmdOp::ScaleVolumeDLS: + cmd = _MakeCmd(r); break; + case CmdOp::Mod2Vibrange: + cmd = _MakeCmd(r); break; + case CmdOp::SetupTremolo: + cmd = _MakeCmd(r); break; + case CmdOp::Return: + cmd = _MakeCmd(r); break; + case CmdOp::GoSub: + cmd = _MakeCmd(r); break; + case CmdOp::TrapEvent: + cmd = _MakeCmd(r); break; + case CmdOp::UntrapEvent: + cmd = _MakeCmd(r); break; + case CmdOp::SendMessage: + cmd = _MakeCmd(r); break; + case CmdOp::GetMessage: + cmd = _MakeCmd(r); break; + case CmdOp::GetVid: + cmd = _MakeCmd(r); break; + case CmdOp::AddAgeCount: + cmd = _MakeCmd(r); break; + case CmdOp::SetAgeCount: + cmd = _MakeCmd(r); break; + case CmdOp::SendFlag: + cmd = _MakeCmd(r); break; + case CmdOp::PitchWheelR: + cmd = _MakeCmd(r); break; + case CmdOp::SetPriority: + cmd = _MakeCmd(r); break; + case CmdOp::AddPriority: + cmd = _MakeCmd(r); break; + case CmdOp::AgeCntSpeed: + cmd = _MakeCmd(r); break; + case CmdOp::AgeCntVel: + cmd = _MakeCmd(r); break; + case CmdOp::VolSelect: + cmd = _MakeCmd(r); break; + case CmdOp::PanSelect: + cmd = _MakeCmd(r); break; + case CmdOp::PitchWheelSelect: + cmd = _MakeCmd(r); break; + case CmdOp::ModWheelSelect: + cmd = _MakeCmd(r); break; + case CmdOp::PedalSelect: + cmd = _MakeCmd(r); break; + case CmdOp::PortamentoSelect: + cmd = _MakeCmd(r); break; + case CmdOp::ReverbSelect: + cmd = _MakeCmd(r); break; + case CmdOp::SpanSelect: + cmd = _MakeCmd(r); break; + case CmdOp::DopplerSelect: + cmd = _MakeCmd(r); break; + case CmdOp::TremoloSelect: + cmd = _MakeCmd(r); break; + case CmdOp::PreASelect: + cmd = _MakeCmd(r); break; + case CmdOp::PreBSelect: + cmd = _MakeCmd(r); break; + case CmdOp::PostBSelect: + cmd = _MakeCmd(r); break; + case CmdOp::AuxAFXSelect: + cmd = _MakeCmd(r); break; + case CmdOp::AuxBFXSelect: + cmd = _MakeCmd(r); break; + case CmdOp::SetupLFO: + cmd = _MakeCmd(r); break; + case CmdOp::ModeSelect: + cmd = _MakeCmd(r); break; + case CmdOp::SetKeygroup: + cmd = _MakeCmd(r); break; + case CmdOp::SRCmodeSelect: + cmd = _MakeCmd(r); break; + case CmdOp::AddVars: + cmd = _MakeCmd(r); break; + case CmdOp::SubVars: + cmd = _MakeCmd(r); break; + case CmdOp::MulVars: + cmd = _MakeCmd(r); break; + case CmdOp::DivVars: + cmd = _MakeCmd(r); break; + case CmdOp::AddIVars: + cmd = _MakeCmd(r); break; + case CmdOp::IfEqual: + cmd = _MakeCmd(r); break; + case CmdOp::IfLess: + cmd = _MakeCmd(r); break; + default: + break; + } + return cmd; +} +template std::unique_ptr SoundMacro::MakeCmd(athena::io::MemoryReader& r); +template std::unique_ptr SoundMacro::MakeCmd(athena::io::YAMLDocReader& r); + std::string_view SoundMacro::CmdOpToStr(CmdOp op) { switch (op) @@ -673,7 +852,7 @@ SoundMacro::CmdOp SoundMacro::CmdStrToOp(std::string_view op) return CmdOp::Invalid; } -bool AudioGroupPool::toYAML(athena::io::IStreamWriter& writer) const +bool AudioGroupPool::toYAML(SystemStringView groupPath) const { athena::io::YAMLDocWriter w("amuse::Pool"); @@ -750,7 +929,10 @@ bool AudioGroupPool::toYAML(athena::io::IStreamWriter& writer) const } } - return w.finish(&writer); + SystemString poolPath(groupPath); + poolPath += _S("/!pool.yaml"); + athena::io::FileWriter fo(poolPath); + return w.finish(&fo); } template <> @@ -785,7 +967,7 @@ void amuse::Curve::Enumerate(athena::io::YAMLDocWriter& w) const char* amuse::Curve::DNAType() { - return "amuse::ADSR"; + return "amuse::Curve"; } } diff --git a/lib/AudioGroupProject.cpp b/lib/AudioGroupProject.cpp index 7536177..fd0c2f9 100644 --- a/lib/AudioGroupProject.cpp +++ b/lib/AudioGroupProject.cpp @@ -1,6 +1,8 @@ #include "amuse/AudioGroupProject.hpp" #include "amuse/AudioGroupData.hpp" #include "athena/MemoryReader.hpp" +#include "athena/FileWriter.hpp" +#include "athena/FileReader.hpp" namespace amuse { @@ -208,6 +210,87 @@ AudioGroupProject AudioGroupProject::CreateAudioGroupProject(const AudioGroupDat } } +AudioGroupProject AudioGroupProject::CreateAudioGroupProject(SystemStringView groupPath) +{ + AudioGroupProject ret; + SystemString projPath(groupPath); + projPath += _S("/!project.yaml"); + athena::io::FileReader fi(projPath); + + if (!fi.hasError()) + { + athena::io::YAMLDocReader r; + if (r.parse(&fi) && r.ValidateClassType("amuse::Project")) + { + size_t songGroupCount; + if (auto __v = r.enterSubVector("songGroups", songGroupCount)) + { + ret.m_songGroups.reserve(songGroupCount); + for (int g = 0; g < songGroupCount; ++g) + { + if (auto __r = r.enterSubRecord(nullptr)) + { + SongGroupIndex& idx = ret.m_songGroups[g]; + if (auto __v2 = r.enterSubRecord("normPages")) + { + idx.m_normPages.reserve(r.getCurNode()->m_mapChildren.size()); + for (const auto& pg : r.getCurNode()->m_mapChildren) + if (auto __r2 = r.enterSubRecord(pg.first.c_str())) + idx.m_normPages[strtoul(pg.first.c_str(), nullptr, 0)].read(r); + } + if (auto __v2 = r.enterSubRecord("drumPages")) + { + idx.m_drumPages.reserve(r.getCurNode()->m_mapChildren.size()); + for (const auto& pg : r.getCurNode()->m_mapChildren) + if (auto __r2 = r.enterSubRecord(pg.first.c_str())) + idx.m_drumPages[strtoul(pg.first.c_str(), nullptr, 0)].read(r); + } + if (auto __v2 = r.enterSubRecord("songs")) + { + idx.m_midiSetups.reserve(r.getCurNode()->m_mapChildren.size()); + for (const auto& song : r.getCurNode()->m_mapChildren) + { + size_t chanCount; + if (auto __v3 = r.enterSubVector(song.first.c_str(), chanCount)) + { + ObjectId songId = SongId::CurNameDB->generateId(NameDB::Type::Song); + SongId::CurNameDB->registerPair(song.first, songId); + std::array& setup = idx.m_midiSetups[songId]; + for (int i = 0; i < 16 && i < chanCount; ++i) + if (auto __r2 = r.enterSubRecord(nullptr)) + setup[i].read(r); + } + } + } + } + } + } + + size_t sfxGroupCount; + if (auto __v = r.enterSubVector("sfxGroups", sfxGroupCount)) + { + ret.m_sfxGroups.reserve(sfxGroupCount); + for (int g = 0; g < sfxGroupCount; ++g) + { + if (auto __r = r.enterSubRecord(nullptr)) + { + SFXGroupIndex& idx = ret.m_sfxGroups[g]; + for (const auto& sfx : r.getCurNode()->m_mapChildren) + if (auto __r2 = r.enterSubRecord(sfx.first.c_str())) + { + ObjectId sfxId = SFXId::CurNameDB->generateId(NameDB::Type::SFX); + SFXId::CurNameDB->registerPair(sfx.first, sfxId); + idx.m_sfxEntries[sfxId].read(r); + } + } + } + } + } + } + + return ret; +} + template static void ReadRangedObjectIds(NameDB* db, athena::io::IStreamReader& r, NameDB::Type tp) { @@ -412,7 +495,7 @@ const SFXGroupIndex* AudioGroupProject::getSFXGroupIndex(int groupId) const return nullptr; } -bool AudioGroupProject::toYAML(athena::io::IStreamWriter& writer) const +bool AudioGroupProject::toYAML(SystemStringView groupPath) const { athena::io::YAMLDocWriter w("amuse::Project"); @@ -498,7 +581,10 @@ bool AudioGroupProject::toYAML(athena::io::IStreamWriter& writer) const } } - return w.finish(&writer); + SystemString projPath(groupPath); + projPath += _S("/!project.yaml"); + athena::io::FileWriter fo(projPath); + return w.finish(&fo); } } diff --git a/lib/AudioGroupSampleDirectory.cpp b/lib/AudioGroupSampleDirectory.cpp index dad8751..1e6f6f1 100644 --- a/lib/AudioGroupSampleDirectory.cpp +++ b/lib/AudioGroupSampleDirectory.cpp @@ -1,8 +1,13 @@ #include "amuse/AudioGroupSampleDirectory.hpp" #include "amuse/Common.hpp" #include "amuse/AudioGroupData.hpp" +#include "amuse/DSPCodec.hpp" +#include "amuse/N64MusyXCodec.hpp" +#include "amuse/DirectoryEnumerator.hpp" +#include "athena/MemoryReader.hpp" +#include "athena/FileWriter.hpp" +#include "athena/FileReader.hpp" #include -#include namespace amuse { @@ -39,17 +44,16 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader& { EntryDNA ent; ent.read(r); - std::pair& store = m_entries[ent.m_sfxId]; - store.first = ent; + m_entries[ent.m_sfxId] = ent; } for (auto& p : m_entries) { - if (p.second.first.m_adpcmParmOffset) + if (p.second.m_adpcmParmOffset) { - r.seek(p.second.first.m_adpcmParmOffset, athena::Begin); - r.readUBytesToBuf(&p.second.second, sizeof(ADPCMParms::DSPParms)); - p.second.second.swapBigDSP(); + r.seek(p.second.m_adpcmParmOffset, athena::Begin); + r.readUBytesToBuf(&p.second.m_ADPCMParms, sizeof(ADPCMParms::DSPParms)); + p.second.m_ADPCMParms.swapBigDSP(); } } } @@ -63,8 +67,7 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader& { MusyX1AbsSdirEntry ent; ent.read(r); - std::pair& store = m_entries[ent.m_sfxId]; - store.first = ent; + m_entries[ent.m_sfxId] = ent; } } else @@ -73,15 +76,14 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader& { MusyX1SdirEntry ent; ent.read(r); - std::pair& store = m_entries[ent.m_sfxId]; - store.first = ent; + m_entries[ent.m_sfxId] = ent; } } for (auto& p : m_entries) { - memcpy(&p.second.second, sampData + p.second.first.m_sampleOff, sizeof(ADPCMParms::VADPCMParms)); - p.second.second.swapBigVADPCM(); + memcpy(&p.second.m_ADPCMParms, sampData + p.second.m_sampleOff, sizeof(ADPCMParms::VADPCMParms)); + p.second.m_ADPCMParms.swapBigVADPCM(); } } @@ -93,8 +95,9 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader& { MusyX1AbsSdirEntry ent; ent.read(r); - std::pair& store = m_entries[ent.m_sfxId]; - store.first = ent; + Entry& store = m_entries[ent.m_sfxId]; + store = ent; + store.m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24; } } else @@ -103,8 +106,9 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader& { MusyX1SdirEntry ent; ent.read(r); - std::pair& store = m_entries[ent.m_sfxId]; - store.first = ent; + Entry& store = m_entries[ent.m_sfxId]; + store = ent; + store.m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24; } } } @@ -123,4 +127,344 @@ AudioGroupSampleDirectory AudioGroupSampleDirectory::CreateAudioGroupSampleDirec return AudioGroupSampleDirectory(r, data.getAbsoluteProjOffsets(), PCDataTag{}); } } + +static uint32_t DSPSampleToNibble(uint32_t sample) +{ + uint32_t ret = sample / 14 * 16; + if (sample % 14) + ret += (sample % 14) + 2; + return ret; +} + +static uint32_t DSPNibbleToSample(uint32_t nibble) +{ + uint32_t ret = nibble / 16 * 14; + if (nibble % 16) + ret += (nibble % 16) - 2; + return ret; +} + +void AudioGroupSampleDirectory::Entry::loadLooseData(SystemStringView basePath) +{ + Sstat theStat; + SystemString filePath = SystemString(basePath) + _S(".dsp"); + if (!Stat(filePath.c_str(), &theStat) && (!m_looseData || theStat.st_mtime > m_looseModTime)) + { + athena::io::FileReader r(filePath); + if (!r.hasError()) + { + DSPADPCMHeader header; + header.read(r); + m_pitch = header.m_pitch; + m_sampleRate = atUint16(header.x8_sample_rate); + m_numSamples = header.x0_num_samples; + if (header.xc_loop_flag) + { + m_loopStartSample = DSPNibbleToSample(header.x10_loop_start_nibble); + m_loopLengthSamples = DSPNibbleToSample(header.x14_loop_end_nibble) - m_loopStartSample; + } + m_ADPCMParms.dsp.m_ps = uint8_t(header.x3e_ps); + m_ADPCMParms.dsp.m_lps = uint8_t(header.x44_loop_ps); + m_ADPCMParms.dsp.m_hist1 = header.x40_hist1; + m_ADPCMParms.dsp.m_hist2 = header.x42_hist2; + for (int i = 0; i < 8; ++i) + for (int j = 0; j < 2; ++j) + m_ADPCMParms.dsp.m_coefs[i][j] = header.x1c_coef[i][j]; + + uint32_t dataLen = (header.x4_num_nibbles + 1) / 2; + m_looseData.reset(new uint8_t[dataLen]); + r.readUBytesToBuf(m_looseData.get(), dataLen); + + m_looseModTime = theStat.st_mtime; + return; + } + } + if (!Stat(filePath.c_str(), &theStat) && (!m_looseData || theStat.st_mtime > m_looseModTime)) + { + athena::io::FileReader r(filePath); + if (!r.hasError()) + { + atUint32 riffMagic = r.readUint32Little(); + if (riffMagic != SBIG('RIFF')) + return; + atUint32 wavChuckSize = r.readUint32Little(); + atUint32 wavMagic = r.readUint32Little(); + if (wavMagic != SBIG('WAVE')) + return; + + while (r.position() < wavChuckSize + 8) + { + atUint32 chunkMagic = r.readUint32Little(); + atUint32 chunkSize = r.readUint32Little(); + atUint64 startPos = r.position(); + if (chunkMagic == SBIG('fmt ')) + { + WAVFormatChunk fmt; + fmt.read(r); + m_sampleRate = atUint16(fmt.sampleRate); + } + else if (chunkMagic == SBIG('smpl')) + { + WAVSampleChunk smpl; + smpl.read(r); + m_pitch = atUint8(smpl.midiNote); + + if (smpl.numSampleLoops) + { + WAVSampleLoop loop; + loop.read(r); + m_loopStartSample = loop.start; + m_loopLengthSamples = loop.end - loop.start + 1; + } + + } + else if (chunkMagic == SBIG('data')) + { + m_numSamples = ((chunkSize / 2) & 0xffffff) | (atUint32(SampleFormat::PCM_PC) << 24); + m_looseData.reset(new uint8_t[chunkSize]); + r.readUBytesToBuf(m_looseData.get(), chunkSize); + } + r.seek(startPos + chunkSize, athena::Begin); + } + + m_looseModTime = theStat.st_mtime; + return; + } + } +} + +AudioGroupSampleDirectory AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(SystemStringView groupPath) +{ + AudioGroupSampleDirectory ret; + + DirectoryEnumerator de(groupPath, DirectoryEnumerator::Mode::FilesSorted); + for (const DirectoryEnumerator::Entry& ent : de) + { + if (ent.m_name.size() < 4) + continue; + SystemString baseName; + if (!CompareCaseInsensitive(ent.m_name.data() + ent.m_name.size() - 4, _S(".dsp")) || + !CompareCaseInsensitive(ent.m_name.data() + ent.m_name.size() - 4, _S(".wav"))) + baseName = SystemString(ent.m_name.begin(), ent.m_name.begin() + ent.m_name.size() - 4); + else + continue; + + ObjectId sampleId = SampleId::CurNameDB->generateId(NameDB::Type::Sample); +#ifdef _WIN32 + SampleId::CurNameDB->registerPair(athena::utility::wideToUtf8(baseName), sampleId); +#else + SampleId::CurNameDB->registerPair(baseName, sampleId); +#endif + + Entry& entry = ret.m_entries[sampleId]; + SystemString basePath = SystemString(ent.m_path.begin(), ent.m_path.begin() + ent.m_path.size() - 4); + entry.loadLooseData(basePath); + } + + return ret; +} + +void AudioGroupSampleDirectory::_extractWAV(SampleId id, const Entry& ent, + amuse::SystemStringView destDir, const unsigned char* samp) +{ + amuse::SystemString path(destDir); + path += _S("/"); +#ifdef _WIN32 + path += athena::utility::utf8ToWide(SampleId::CurNameDB->resolveNameFromId(id)); +#else + path += SampleId::CurNameDB->resolveNameFromId(id); +#endif + path += _S(".wav"); + athena::io::FileWriter w(path); + + SampleFormat fmt = SampleFormat(ent.m_numSamples >> 24); + uint32_t numSamples = ent.m_numSamples & 0xffffff; + if (ent.m_loopLengthSamples) + { + WAVHeaderLoop header; + header.fmtChunk.sampleRate = ent.m_sampleRate; + header.fmtChunk.byteRate = ent.m_sampleRate * 2u; + header.smplChunk.smplPeriod = 1000000000u / ent.m_sampleRate; + header.smplChunk.midiNote = ent.m_pitch; + header.smplChunk.numSampleLoops = 1; + header.sampleLoop.start = ent.m_loopStartSample; + header.sampleLoop.end = ent.m_loopStartSample + ent.m_loopLengthSamples - 1; + header.dataChunkSize = numSamples * 2u; + header.wavChuckSize = 36 + 68 + header.dataChunkSize; + header.write(w); + } + else + { + WAVHeader header; + header.fmtChunk.sampleRate = ent.m_sampleRate; + header.fmtChunk.byteRate = ent.m_sampleRate * 2u; + header.smplChunk.smplPeriod = 1000000000u / ent.m_sampleRate; + header.smplChunk.midiNote = ent.m_pitch; + header.dataChunkSize = numSamples * 2u; + header.wavChuckSize = 36 + 44 + header.dataChunkSize; + header.write(w); + } + + atUint64 dataLen; + if (fmt == SampleFormat::DSP || fmt == SampleFormat::DSP_DRUM) + { + uint32_t remSamples = numSamples; + uint32_t numFrames = (remSamples + 13) / 14; + const unsigned char* cur = samp + ent.m_sampleOff; + int16_t prev1 = ent.m_ADPCMParms.dsp.m_hist1; + int16_t prev2 = ent.m_ADPCMParms.dsp.m_hist2; + for (uint32_t i = 0; i < numFrames; ++i) + { + int16_t decomp[14] = {}; + unsigned thisSamples = std::min(remSamples, 14u); + DSPDecompressFrame(decomp, cur, ent.m_ADPCMParms.dsp.m_coefs, &prev1, &prev2, thisSamples); + remSamples -= thisSamples; + cur += 8; + w.writeBytes(decomp, thisSamples * 2); + } + + dataLen = (DSPSampleToNibble(numSamples) + 1) / 2; + } + else if (fmt == SampleFormat::N64) + { + uint32_t remSamples = numSamples; + uint32_t numFrames = (remSamples + 31) / 32; + const unsigned char* cur = samp + ent.m_sampleOff + sizeof(ADPCMParms::VADPCMParms); + for (uint32_t i = 0; i < numFrames; ++i) + { + int16_t decomp[32] = {}; + unsigned thisSamples = std::min(remSamples, 32u); + N64MusyXDecompressFrame(decomp, cur, ent.m_ADPCMParms.vadpcm.m_coefs, thisSamples); + remSamples -= thisSamples; + cur += 16; + w.writeBytes(decomp, thisSamples * 2); + } + + dataLen = sizeof(ADPCMParms::VADPCMParms) + (numSamples + 31) / 32 * 16; + } + else if (fmt == SampleFormat::PCM) + { + dataLen = numSamples * 2; + const int16_t* cur = reinterpret_cast(samp + ent.m_sampleOff); + for (int i = 0; i < numSamples; ++i) + w.writeInt16Big(cur[i]); + } + else // PCM_PC + { + dataLen = numSamples * 2; + w.writeBytes(samp + ent.m_sampleOff, dataLen); + } + + std::unique_ptr& ld = const_cast&>(ent.m_looseData); + if (!ld) + { + Sstat theStat; + Stat(path.c_str(), &theStat); + + const_cast(ent.m_looseModTime) = theStat.st_mtime; + ld.reset(new uint8_t[dataLen]); + memcpy(ld.get(), samp + ent.m_sampleOff, dataLen); + } +} + +void AudioGroupSampleDirectory::extractWAV(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const +{ + auto search = m_entries.find(id); + if (search == m_entries.cend()) + return; + _extractWAV(id, search->second, destDir, samp); +} + +void AudioGroupSampleDirectory::extractAllWAV(amuse::SystemStringView destDir, const unsigned char* samp) const +{ + for (const auto& ent : m_entries) + _extractWAV(ent.first, ent.second, destDir, samp); +} + +void AudioGroupSampleDirectory::_extractCompressed(SampleId id, const Entry& ent, + amuse::SystemStringView destDir, const unsigned char* samp) +{ + SampleFormat fmt = SampleFormat(ent.m_numSamples >> 24); + if (fmt == SampleFormat::PCM || fmt == SampleFormat::PCM_PC) + { + _extractWAV(id, ent, destDir, samp); + return; + } + + amuse::SystemString path(destDir); + path += _S("/"); +#ifdef _WIN32 + path += athena::utility::utf8ToWide(SampleId::CurNameDB->resolveNameFromId(id)); +#else + path += SampleId::CurNameDB->resolveNameFromId(id); +#endif + + uint32_t numSamples = ent.m_numSamples & 0xffffff; + atUint64 dataLen; + if (fmt == SampleFormat::DSP || fmt == SampleFormat::DSP_DRUM) + { + DSPADPCMHeader header; + header.x0_num_samples = numSamples; + header.x4_num_nibbles = DSPSampleToNibble(numSamples); + header.x8_sample_rate = ent.m_sampleRate; + header.xc_loop_flag = atUint16(ent.m_loopLengthSamples != 0); + if (header.xc_loop_flag) + { + header.x10_loop_start_nibble = DSPSampleToNibble(ent.m_loopStartSample); + header.x14_loop_end_nibble = DSPSampleToNibble(ent.m_loopStartSample + ent.m_loopLengthSamples); + } + for (int i = 0; i < 8; ++i) + for (int j = 0; j < 2; ++j) + header.x1c_coef[i][j] = ent.m_ADPCMParms.dsp.m_coefs[i][j]; + header.x3e_ps = ent.m_ADPCMParms.dsp.m_ps; + header.x40_hist1 = ent.m_ADPCMParms.dsp.m_hist1; + header.x42_hist2 = ent.m_ADPCMParms.dsp.m_hist2; + header.x44_loop_ps = ent.m_ADPCMParms.dsp.m_lps; + header.m_pitch = ent.m_pitch; + + path += _S(".dsp"); + athena::io::FileWriter w(path); + header.write(w); + dataLen = (header.x4_num_nibbles + 1) / 2; + w.writeUBytes(samp + ent.m_sampleOff, dataLen); + } + else if (fmt == SampleFormat::N64) + { + path += _S(".vadpcm"); + athena::io::FileWriter w(path); + dataLen = sizeof(ADPCMParms::VADPCMParms) + (numSamples + 31) / 32 * 16; + w.writeUBytes(samp + ent.m_sampleOff, dataLen); + } + else + { + return; + } + + std::unique_ptr& ld = const_cast&>(ent.m_looseData); + if (!ld) + { + Sstat theStat; + Stat(path.c_str(), &theStat); + + const_cast(ent.m_looseModTime) = theStat.st_mtime; + ld.reset(new uint8_t[dataLen]); + memcpy(ld.get(), samp + ent.m_sampleOff, dataLen); + } +} + +void AudioGroupSampleDirectory::extractCompressed(SampleId id, amuse::SystemStringView destDir, + const unsigned char* samp) const +{ + auto search = m_entries.find(id); + if (search == m_entries.cend()) + return; + _extractCompressed(id, search->second, destDir, samp); +} + +void AudioGroupSampleDirectory::extractAllCompressed(amuse::SystemStringView destDir, + const unsigned char* samp) const +{ + for (const auto& ent : m_entries) + _extractCompressed(ent.first, ent.second, destDir, samp); +} } diff --git a/lib/Common.cpp b/lib/Common.cpp index 75fb2b9..a5233d8 100644 --- a/lib/Common.cpp +++ b/lib/Common.cpp @@ -193,7 +193,7 @@ const char* PageObjectIdDNA::DNAType() template struct PageObjectIdDNA; template struct PageObjectIdDNA; -ObjectId NameDB::generateId(Type tp) +ObjectId NameDB::generateId(Type tp) const { uint16_t maxMatch = uint16_t(tp == Type::Layer ? 0x8000 : 0); for (const auto& p : m_idToString) diff --git a/lib/Engine.cpp b/lib/Engine.cpp index 249627c..21fc172 100644 --- a/lib/Engine.cpp +++ b/lib/Engine.cpp @@ -213,24 +213,7 @@ AudioGroup* Engine::_addAudioGroup(const AudioGroupData& data, std::unique_ptr grp; - switch (data.m_fmt) - { - case DataFormat::GCN: - grp = std::make_unique(data, GCNDataTag{}); - break; - case DataFormat::N64: - grp = std::make_unique(data, data.m_absOffs, N64DataTag{}); - break; - case DataFormat::PC: - grp = std::make_unique(data, data.m_absOffs, PCDataTag{}); - break; - } - if (!grp) - return nullptr; - - return _addAudioGroup(data, std::move(grp)); + return _addAudioGroup(data, std::make_unique(data)); } /** Remove audio group from engine */ diff --git a/lib/Voice.cpp b/lib/Voice.cpp index 5ec8cae..3198fee 100644 --- a/lib/Voice.cpp +++ b/lib/Voice.cpp @@ -64,14 +64,14 @@ bool Voice::_checkSamplePos(bool& looped) if (m_curSamplePos >= m_lastSamplePos) { - if (m_curSample->first.m_loopLengthSamples) + if (m_curSample->m_loopLengthSamples) { /* Turn over looped sample */ - m_curSamplePos = m_curSample->first.m_loopStartSample; + m_curSamplePos = m_curSample->m_loopStartSample; if (m_curFormat == SampleFormat::DSP) { - m_prev1 = m_curSample->second.dsp.m_hist1; - m_prev2 = m_curSample->second.dsp.m_hist2; + m_prev1 = m_curSample->m_ADPCMParms.dsp.m_hist1; + m_prev2 = m_curSample->m_ADPCMParms.dsp.m_hist2; } looped = true; } @@ -115,9 +115,9 @@ void Voice::_doKeyOff() void Voice::_setTotalPitch(int32_t cents, bool slew) { // fprintf(stderr, "PITCH %d %d \n", cents, slew); - int32_t interval = cents - m_curSample->first.m_pitch * 100; + int32_t interval = cents - m_curSample->m_pitch * 100; double ratio = std::exp2(interval / 1200.0) * m_dopplerRatio; - m_sampleRate = m_curSample->first.m_sampleRate * ratio; + m_sampleRate = m_curSample->m_sampleRate * ratio; m_backendVoice->setPitchRatio(ratio, slew); } @@ -346,9 +346,10 @@ uint32_t Voice::_GetBlockSampleCount(SampleFormat fmt) { default: return 1; - case Voice::SampleFormat::DSP: + case SampleFormat::DSP: + case SampleFormat::DSP_DRUM: return 14; - case Voice::SampleFormat::N64: + case SampleFormat::N64: return 64; } } @@ -514,14 +515,15 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data) case SampleFormat::DSP: { decSamples = - DSPDecompressFrameRanged(data, m_curSampleData + 8 * block, m_curSample->second.dsp.m_coefs, + DSPDecompressFrameRanged(data, m_curSampleData + 8 * block, + m_curSample->m_ADPCMParms.dsp.m_coefs, &m_prev1, &m_prev2, rem, remCount); break; } case SampleFormat::N64: { decSamples = N64MusyXDecompressFrameRanged(data, m_curSampleData + 256 + 40 * block, - m_curSample->second.vadpcm.m_coefs, rem, remCount); + m_curSample->m_ADPCMParms.vadpcm.m_coefs, rem, remCount); break; } case SampleFormat::PCM: @@ -576,14 +578,14 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data) { case SampleFormat::DSP: { - decSamples = DSPDecompressFrame(data, m_curSampleData + 8 * block, m_curSample->second.dsp.m_coefs, - &m_prev1, &m_prev2, remCount); + decSamples = DSPDecompressFrame(data, m_curSampleData + 8 * block, + m_curSample->m_ADPCMParms.dsp.m_coefs, &m_prev1, &m_prev2, remCount); break; } case SampleFormat::N64: { decSamples = N64MusyXDecompressFrame(data, m_curSampleData + 256 + 40 * block, - m_curSample->second.vadpcm.m_coefs, remCount); + m_curSample->m_ADPCMParms.vadpcm.m_coefs, remCount); break; } case SampleFormat::PCM: @@ -877,7 +879,7 @@ void Voice::keyOff() 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) + else if (!m_curSample || m_curSample->m_loopLengthSamples) _macroKeyOff(); for (const std::shared_ptr& vox : m_childVoices) @@ -901,7 +903,7 @@ void Voice::message(int32_t val) } } -void Voice::startSample(int16_t sampId, int32_t offset) +void Voice::startSample(SampleId sampId, int32_t offset) { if (m_destroyed) return; @@ -909,41 +911,38 @@ void Voice::startSample(int16_t sampId, int32_t offset) m_curSample = m_audioGroup.getSample(sampId); if (m_curSample) { - m_sampleRate = m_curSample->first.m_sampleRate; - m_curPitch = m_curSample->first.m_pitch; + m_curSampleData = m_audioGroup.getSampleData(sampId, m_curSample); + + m_sampleRate = m_curSample->m_sampleRate; + m_curPitch = m_curSample->m_pitch; m_pitchDirty = true; _setPitchWheel(m_curPitchWheel); - m_backendVoice->resetSampleRate(m_curSample->first.m_sampleRate); + m_backendVoice->resetSampleRate(m_curSample->m_sampleRate); m_needsSlew = false; - int32_t numSamples = m_curSample->first.m_numSamples & 0xffffff; + int32_t numSamples = m_curSample->m_numSamples & 0xffffff; if (offset) { - if (m_curSample->first.m_loopLengthSamples) + if (m_curSample->m_loopLengthSamples) { - if (offset > int32_t(m_curSample->first.m_loopStartSample)) + if (offset > int32_t(m_curSample->m_loopStartSample)) offset = - ((offset - m_curSample->first.m_loopStartSample) % m_curSample->first.m_loopLengthSamples) + - m_curSample->first.m_loopStartSample; + ((offset - m_curSample->m_loopStartSample) % m_curSample->m_loopLengthSamples) + + m_curSample->m_loopStartSample; } else offset = clamp(0, offset, numSamples); } m_curSamplePos = offset; - m_curSampleData = m_audioGroup.getSampleData(m_curSample->first.m_sampleOff); m_prev1 = 0; m_prev2 = 0; - if (m_audioGroup.getDataFormat() == DataFormat::PC) - m_curFormat = SampleFormat::PCM_PC; - else - m_curFormat = SampleFormat(m_curSample->first.m_numSamples >> 24); - + m_curFormat = SampleFormat(m_curSample->m_numSamples >> 24); if (m_curFormat == SampleFormat::DSP_DRUM) m_curFormat = SampleFormat::DSP; - m_lastSamplePos = m_curSample->first.m_loopLengthSamples - ? (m_curSample->first.m_loopStartSample + m_curSample->first.m_loopLengthSamples) + m_lastSamplePos = m_curSample->m_loopLengthSamples + ? (m_curSample->m_loopStartSample + m_curSample->m_loopLengthSamples) : numSamples; bool looped; @@ -955,11 +954,11 @@ void Voice::startSample(int16_t sampId, int32_t offset) uint32_t block = m_curSamplePos / 14; uint32_t rem = m_curSamplePos % 14; for (uint32_t b = 0; b < block; ++b) - DSPDecompressFrameStateOnly(m_curSampleData + 8 * b, m_curSample->second.dsp.m_coefs, &m_prev1, + DSPDecompressFrameStateOnly(m_curSampleData + 8 * b, m_curSample->m_ADPCMParms.dsp.m_coefs, &m_prev1, &m_prev2, 14); if (rem) - DSPDecompressFrameStateOnly(m_curSampleData + 8 * block, m_curSample->second.dsp.m_coefs, &m_prev1, + DSPDecompressFrameStateOnly(m_curSampleData + 8 * block, m_curSample->m_ADPCMParms.dsp.m_coefs, &m_prev1, &m_prev2, rem); } }