diff --git a/CMakeLists.txt b/CMakeLists.txt index 082adf0..0663f22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,8 @@ set(SOURCES lib/EffectDelay.cpp lib/SurroundProfiles.cpp lib/ContainerRegistry.cpp - lib/dsp.c) + lib/DSPCodec.c + lib/N64MusyXCodec.c) set(HEADERS include/amuse/AudioGroup.hpp @@ -55,7 +56,8 @@ set(HEADERS include/amuse/ContainerRegistry.hpp include/amuse/Common.hpp include/amuse/amuse.hpp - include/amuse/dsp.h) + include/amuse/DSPCodec.h + include/amuse/N64MusyXCodec.h) unset(EXTRAS) if(TARGET boo) diff --git a/driver/main.cpp b/driver/main.cpp index 7899986..bd6d9f7 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -586,7 +586,7 @@ struct AppCallback : boo::IApplicationCallback for (auto& grp : data) { /* Load project to assemble group list */ - m_projs.emplace_back(grp.second.getProj()); + m_projs.push_back(amuse::AudioGroupProject::CreateAudioGroupProject(grp.second)); amuse::AudioGroupProject& proj = m_projs.back(); totalGroups += proj.sfxGroups().size() + proj.songGroups().size(); diff --git a/include/amuse/AudioGroup.hpp b/include/amuse/AudioGroup.hpp index 0421fc6..62b0839 100644 --- a/include/amuse/AudioGroup.hpp +++ b/include/amuse/AudioGroup.hpp @@ -19,15 +19,19 @@ class AudioGroup AudioGroupPool m_pool; AudioGroupSampleDirectory m_sdir; const unsigned char* m_samp; + DataFormat m_fmt; bool m_valid; public: operator bool() const {return m_valid;} - AudioGroup(const AudioGroupData& data); + AudioGroup(const AudioGroupData& data, GCNDataTag); + AudioGroup(const AudioGroupData& data, N64DataTag); + AudioGroup(const AudioGroupData& data, PCDataTag); const Sample* getSample(int sfxId) const; const unsigned char* getSampleData(uint32_t offset) const; const AudioGroupProject& getProj() const {return m_proj;} const AudioGroupPool& getPool() const {return m_pool;} + DataFormat getDataFormat() const {return m_fmt;} }; } diff --git a/include/amuse/AudioGroupData.hpp b/include/amuse/AudioGroupData.hpp index d1cd617..e7941a8 100644 --- a/include/amuse/AudioGroupData.hpp +++ b/include/amuse/AudioGroupData.hpp @@ -1,21 +1,36 @@ #ifndef __AMUSE_AUDIOGROUPDATA_HPP__ #define __AMUSE_AUDIOGROUPDATA_HPP__ +#include "Common.hpp" + namespace amuse { /** Simple pointer-container of the four Audio Group chunks */ class AudioGroupData { + friend class Engine; + friend class AudioGroupProject; protected: unsigned char* m_proj; unsigned char* m_pool; unsigned char* m_sdir; unsigned char* m_samp; + DataFormat m_fmt; + + AudioGroupData(unsigned char* proj, unsigned char* pool, + unsigned char* sdir, unsigned char* samp, DataFormat fmt) + : m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp), m_fmt(fmt) {} public: AudioGroupData(unsigned char* proj, unsigned char* pool, - unsigned char* sdir, unsigned char* samp) - : m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp) {} + unsigned char* sdir, unsigned char* samp, GCNDataTag) + : m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp), m_fmt(DataFormat::GCN) {} + AudioGroupData(unsigned char* proj, unsigned char* pool, + unsigned char* sdir, unsigned char* samp, N64DataTag) + : m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp), m_fmt(DataFormat::N64) {} + AudioGroupData(unsigned char* proj, unsigned char* pool, + unsigned char* sdir, unsigned char* samp, PCDataTag) + : m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp), m_fmt(DataFormat::PC) {} const unsigned char* getProj() const {return m_proj;} const unsigned char* getPool() const {return m_pool;} diff --git a/include/amuse/AudioGroupPool.hpp b/include/amuse/AudioGroupPool.hpp index 913a44c..99101da 100644 --- a/include/amuse/AudioGroupPool.hpp +++ b/include/amuse/AudioGroupPool.hpp @@ -98,6 +98,7 @@ class AudioGroupPool std::unordered_map> m_layers; public: AudioGroupPool(const unsigned char* data); + AudioGroupPool(const unsigned char* data, PCDataTag); const unsigned char* soundMacro(ObjectId id) const; const Keymap* keymap(ObjectId id) const; const std::vector* layer(ObjectId id) const; diff --git a/include/amuse/AudioGroupProject.hpp b/include/amuse/AudioGroupProject.hpp index 3ac8218..1560f8d 100644 --- a/include/amuse/AudioGroupProject.hpp +++ b/include/amuse/AudioGroupProject.hpp @@ -2,12 +2,14 @@ #define __AMUSE_AUDIOGROUPPROJECT_HPP__ #include "Entity.hpp" +#include "Common.hpp" #include #include #include namespace amuse { +class AudioGroupData; /** Common index members of SongGroups and SFXGroups */ struct AudioGroupIndex @@ -68,8 +70,19 @@ class AudioGroupProject { std::unordered_map m_songGroups; std::unordered_map m_sfxGroups; + + /* MusyX 1.0 structures converted to MusyX 2.0 structures for pointer-compatible access */ + std::unique_ptr m_convNormalPages; + std::unique_ptr m_convDrumPages; + std::unique_ptr[]> m_convMidiSetups; + void _allocateConvBuffers(const unsigned char* data, N64DataTag); + void _allocateConvBuffers(const unsigned char* data, PCDataTag); + public: - AudioGroupProject(const unsigned char* data); + AudioGroupProject(const unsigned char* data, GCNDataTag); + AudioGroupProject(const unsigned char* data, N64DataTag); + AudioGroupProject(const unsigned char* data, PCDataTag); + static AudioGroupProject CreateAudioGroupProject(const AudioGroupData& data); const SongGroupIndex* getSongGroupIndex(int groupId) const; const SFXGroupIndex* getSFXGroupIndex(int groupId) const; diff --git a/include/amuse/AudioGroupSampleDirectory.hpp b/include/amuse/AudioGroupSampleDirectory.hpp index 7ea8c23..77ee43c 100644 --- a/include/amuse/AudioGroupSampleDirectory.hpp +++ b/include/amuse/AudioGroupSampleDirectory.hpp @@ -3,6 +3,7 @@ #include #include +#include "Common.hpp" namespace amuse { @@ -30,15 +31,17 @@ public: uint16_t m_bytesPerFrame; uint8_t m_ps; uint8_t m_lps; - int16_t m_hist1; int16_t m_hist2; + int16_t m_hist1; int16_t m_coefs[8][2]; void swapBig(); }; private: std::unordered_map> m_entries; public: - AudioGroupSampleDirectory(const unsigned char* data); + AudioGroupSampleDirectory(const unsigned char* data, GCNDataTag); + AudioGroupSampleDirectory(const unsigned char* data, N64DataTag); + AudioGroupSampleDirectory(const unsigned char* data, PCDataTag); }; } diff --git a/include/amuse/Common.hpp b/include/amuse/Common.hpp index 383c958..d20ed2a 100644 --- a/include/amuse/Common.hpp +++ b/include/amuse/Common.hpp @@ -204,6 +204,23 @@ static inline double SBig(double val) {return val;} #endif #endif +/** Versioned data format to interpret */ +enum class DataFormat +{ + GCN, + N64, + PC +}; + +/** Meta-type for selecting GameCube (MusyX 2.0) data formats */ +struct GCNDataTag {}; + +/** Meta-type for selecting N64 (MusyX 1.0) data formats */ +struct N64DataTag {}; + +/** Meta-type for selecting PC (MusyX 1.0) data formats */ +struct PCDataTag {}; + } #endif // __AMUSE_COMMON_HPP__ diff --git a/include/amuse/dsp.h b/include/amuse/DSPCodec.h similarity index 95% rename from include/amuse/dsp.h rename to include/amuse/DSPCodec.h index 8f0c03d..5485f9d 100644 --- a/include/amuse/dsp.h +++ b/include/amuse/DSPCodec.h @@ -1,5 +1,5 @@ -#ifndef _DSP_h -#define _DSP_h +#ifndef _DSPCODEC_h +#define _DSPCODEC_h #ifdef __cplusplus extern "C" { @@ -36,4 +36,4 @@ unsigned DSPDecompressFrameStateOnly(const uint8_t* in, } #endif -#endif // _DSP_h +#endif // _DSPCODEC_h diff --git a/include/amuse/Engine.hpp b/include/amuse/Engine.hpp index 596278b..a0b1a12 100644 --- a/include/amuse/Engine.hpp +++ b/include/amuse/Engine.hpp @@ -46,6 +46,7 @@ class Engine std::linear_congruential_engine m_random; int m_nextVid = 0; + AudioGroup* _addAudioGroup(const AudioGroupData& data, std::unique_ptr&& grp); std::pair _findSongGroup(int groupId) const; std::pair _findSFXGroup(int groupId) const; diff --git a/include/amuse/N64MusyXCodec.h b/include/amuse/N64MusyXCodec.h new file mode 100644 index 0000000..4282bec --- /dev/null +++ b/include/amuse/N64MusyXCodec.h @@ -0,0 +1,29 @@ +#ifndef _N64MUSYXCODEC_h +#define _N64MUSYXCODEC_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +static inline int16_t N64MusyXSampClamp(int32_t val) +{ + if (val < -32768) val = -32768; + else if (val > 32767) val = 32767; + return val; +} + +unsigned N64MusyXDecompressFrame(int16_t* out, const uint8_t* in, + const int16_t coefs[8][2][8], + unsigned lastSample); + +unsigned N64MusyXDecompressFrameRanged(int16_t* out, const uint8_t* in, + const int16_t coefs[8][2][8], + unsigned firstSample, unsigned lastSample); + +#ifdef __cplusplus +} +#endif + +#endif // _N64MUSYXCODEC_h diff --git a/include/amuse/SoundMacroState.hpp b/include/amuse/SoundMacroState.hpp index 2a230be..635e87f 100644 --- a/include/amuse/SoundMacroState.hpp +++ b/include/amuse/SoundMacroState.hpp @@ -215,9 +215,9 @@ class SoundMacroState public: /** initialize state for SoundMacro data at `ptr` */ - void initialize(const unsigned char* ptr, int step); + void initialize(const unsigned char* ptr, int step, bool swapData); void initialize(const unsigned char* ptr, int step, double ticksPerSec, - uint8_t midiKey, uint8_t midiVel, uint8_t midiMod); + uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool swapData); /** advances `dt` seconds worth of commands in the SoundMacro * @return `true` if END reached diff --git a/include/amuse/Voice.hpp b/include/amuse/Voice.hpp index 37aa1fe..db8c9c3 100644 --- a/include/amuse/Voice.hpp +++ b/include/amuse/Voice.hpp @@ -50,7 +50,8 @@ class Voice : public Entity { DSP, IMA, - PCM + PCM, + N64 }; const Sample* m_curSample = nullptr; /**< Current sample entry playing */ const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */ @@ -128,7 +129,7 @@ class Voice : public Entity void _destroy(); void _reset(); - bool _checkSamplePos(); + bool _checkSamplePos(bool& looped); void _doKeyOff(); void _macroKeyOff(); void _macroSampleEnd(); @@ -136,6 +137,7 @@ class Voice : public Entity void _setTotalPitch(int32_t cents, bool slew); bool _isRecursivelyDead(); void _bringOutYourDead(); + static uint32_t _GetBlockSampleCount(SampleFormat fmt); std::shared_ptr _findVoice(int vid, std::weak_ptr thisPtr); std::unique_ptr& _ensureCtrlVals(); diff --git a/lib/AudioGroup.cpp b/lib/AudioGroup.cpp index 6f4925c..e0c154d 100644 --- a/lib/AudioGroup.cpp +++ b/lib/AudioGroup.cpp @@ -4,11 +4,28 @@ namespace amuse { -AudioGroup::AudioGroup(const AudioGroupData& data) -: m_proj(data.getProj()), +AudioGroup::AudioGroup(const AudioGroupData& data, GCNDataTag) +: m_proj(data.getProj(), GCNDataTag{}), m_pool(data.getPool()), - m_sdir(data.getSdir()), - m_samp(data.getSamp()) + m_sdir(data.getSdir(), GCNDataTag{}), + m_samp(data.getSamp()), + m_fmt(DataFormat::GCN) +{} + +AudioGroup::AudioGroup(const AudioGroupData& data, N64DataTag) +: m_proj(data.getProj(), N64DataTag{}), + m_pool(data.getPool()), + m_sdir(data.getSdir(), N64DataTag{}), + m_samp(data.getSamp()), + m_fmt(DataFormat::N64) +{} + +AudioGroup::AudioGroup(const AudioGroupData& data, PCDataTag) +: m_proj(data.getProj(), PCDataTag{}), + m_pool(data.getPool(), PCDataTag{}), + m_sdir(data.getSdir(), PCDataTag{}), + m_samp(data.getSamp()), + m_fmt(DataFormat::PC) {} const Sample* AudioGroup::getSample(int sfxId) const diff --git a/lib/AudioGroupData.cpp b/lib/AudioGroupData.cpp index 4aa3e5b..18c5e08 100644 --- a/lib/AudioGroupData.cpp +++ b/lib/AudioGroupData.cpp @@ -15,7 +15,7 @@ IntrusiveAudioGroupData::~IntrusiveAudioGroupData() } IntrusiveAudioGroupData::IntrusiveAudioGroupData(IntrusiveAudioGroupData&& other) -: AudioGroupData(other.m_proj, other.m_pool, other.m_sdir, other.m_samp) +: AudioGroupData(other.m_proj, other.m_pool, other.m_sdir, other.m_samp, other.m_fmt) { m_owns = other.m_owns; other.m_owns = false; diff --git a/lib/AudioGroupPool.cpp b/lib/AudioGroupPool.cpp index 8047506..118922e 100644 --- a/lib/AudioGroupPool.cpp +++ b/lib/AudioGroupPool.cpp @@ -81,6 +81,66 @@ AudioGroupPool::AudioGroupPool(const unsigned char* data) } } +AudioGroupPool::AudioGroupPool(const unsigned char* data, PCDataTag) +{ + const Header* head = reinterpret_cast(data); + + if (head->soundMacrosOffset) + { + const unsigned char* cur = data + head->soundMacrosOffset; + while (*reinterpret_cast(cur) != 0xffffffff) + { + uint32_t size = *reinterpret_cast(cur); + ObjectId id = *reinterpret_cast(cur + 4); + m_soundMacros[id] = cur; + cur += size; + } + } + + if (head->tablesOffset) + { + const unsigned char* cur = data + head->tablesOffset; + while (*reinterpret_cast(cur) != 0xffffffff) + { + uint32_t size = *reinterpret_cast(cur); + ObjectId id = *reinterpret_cast(cur + 4); + m_tables[id] = cur + 8; + cur += size; + } + } + + if (head->keymapsOffset) + { + const unsigned char* cur = data + head->keymapsOffset; + while (*reinterpret_cast(cur) != 0xffffffff) + { + uint32_t size = *reinterpret_cast(cur); + ObjectId id = *reinterpret_cast(cur + 4); + m_keymaps[id] = reinterpret_cast(cur + 8); + cur += size; + } + } + + if (head->layersOffset) + { + const unsigned char* cur = data + head->layersOffset; + while (*reinterpret_cast(cur) != 0xffffffff) + { + uint32_t size = *reinterpret_cast(cur); + ObjectId id = *reinterpret_cast(cur + 4); + std::vector& mappingsOut = m_layers[id]; + + uint32_t count = *reinterpret_cast(cur+8); + mappingsOut.reserve(count); + const unsigned char* subcur = cur + 12; + for (int i=0 ; i(subcur + i * 12)); + + cur += size; + } + } +} + const unsigned char* AudioGroupPool::soundMacro(ObjectId id) const { auto search = m_soundMacros.find(id); diff --git a/lib/AudioGroupProject.cpp b/lib/AudioGroupProject.cpp index 19f7b41..984d682 100644 --- a/lib/AudioGroupProject.cpp +++ b/lib/AudioGroupProject.cpp @@ -1,4 +1,5 @@ #include "amuse/AudioGroupProject.hpp" +#include "amuse/AudioGroupData.hpp" #include "amuse/Common.hpp" #include @@ -41,7 +42,7 @@ struct GroupHeader } }; -AudioGroupProject::AudioGroupProject(const unsigned char* data) +AudioGroupProject::AudioGroupProject(const unsigned char* data, GCNDataTag) { const GroupHeader* group = reinterpret_cast(data); while (group->groupEndOff != 0xffffffff) @@ -114,6 +115,340 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data) } } +struct MusyX1PageEntry +{ + ObjectId objId; + uint8_t priority; + uint8_t maxVoices; + uint8_t unk; + uint8_t programNo; + uint8_t pad[2]; + + void setIntoMusyX2(SongGroupIndex::PageEntry& ent) const + { + ent.objId = objId; + ent.priority = priority; + ent.maxVoices = maxVoices; + ent.programNo = programNo; + } +}; + +struct MusyX1MIDISetup +{ + uint8_t programNo; + uint8_t volume; + uint8_t panning; + uint8_t reverb; + uint8_t chorus; + uint8_t pad[3]; + + void setIntoMusyX2(SongGroupIndex::MIDISetup& ent) const + { + ent.programNo = programNo; + ent.volume = volume; + ent.panning = panning; + ent.reverb = reverb; + ent.chorus = chorus; + } +}; + +void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, N64DataTag) +{ + size_t normPageCount = 0; + size_t drumPageCount = 0; + size_t midiSetupCount = 0; + + const GroupHeader* group = reinterpret_cast(data); + while (group->groupEndOff != 0xffffffff) + { + const unsigned char* subData = data + 8; + GroupHeader header = *group; + header.swapBig(); + + if (header.type == GroupType::Song) + { + /* Normal pages */ + const MusyX1PageEntry* normEntries = + reinterpret_cast(subData + header.pageTableOff); + while (normEntries->objId != 0xffff) + { + ++normPageCount; + ++normEntries; + } + + /* Drum pages */ + const MusyX1PageEntry* drumEntries = + reinterpret_cast(subData + header.drumTableOff); + while (drumEntries->objId != 0xffff) + { + ++drumPageCount; + ++drumEntries; + } + + /* MIDI setups */ + const uint8_t* setupData = subData + header.midiSetupsOff; + const uint8_t* setupEnd = subData + header.groupEndOff; + while (setupData < setupEnd) + { + ++midiSetupCount; + setupData += 8 * 16 + 4; + } + } + + data += header.groupEndOff; + group = reinterpret_cast(data); + } + + if (normPageCount) + m_convNormalPages.reset(new SongGroupIndex::PageEntry[normPageCount]); + if (drumPageCount) + m_convDrumPages.reset(new SongGroupIndex::PageEntry[drumPageCount]); + if (midiSetupCount) + m_convMidiSetups.reset(new std::array[midiSetupCount]); +} + +AudioGroupProject::AudioGroupProject(const unsigned char* data, N64DataTag) +{ + _allocateConvBuffers(data, N64DataTag{}); + SongGroupIndex::PageEntry* normPagesBuf = m_convNormalPages.get(); + SongGroupIndex::PageEntry* drumPagesBuf = m_convDrumPages.get(); + std::array* midiSetupsBuf = m_convMidiSetups.get(); + + const GroupHeader* group = reinterpret_cast(data); + while (group->groupEndOff != 0xffffffff) + { + const unsigned char* subData = data + 8; + GroupHeader header = *group; + header.swapBig(); + + AudioGroupIndex* bIdx = nullptr; + + if (header.type == GroupType::Song) + { + SongGroupIndex& idx = m_songGroups[header.groupId]; + bIdx = &idx; + + /* Normal pages */ + const MusyX1PageEntry* normEntries = + reinterpret_cast(subData + header.pageTableOff); + while (normEntries->objId != 0xffff) + { + normEntries->setIntoMusyX2(*normPagesBuf); + idx.m_normPages[normEntries->programNo] = normPagesBuf; + ++normEntries; + ++normPagesBuf; + } + + /* Drum pages */ + const MusyX1PageEntry* drumEntries = + reinterpret_cast(subData + header.drumTableOff); + while (drumEntries->objId != 0xffff) + { + drumEntries->setIntoMusyX2(*drumPagesBuf); + idx.m_drumPages[drumEntries->programNo] = drumPagesBuf; + ++drumEntries; + ++drumPagesBuf; + } + + /* MIDI setups */ + const uint8_t* setupData = subData + header.midiSetupsOff; + const uint8_t* setupEnd = subData + header.groupEndOff; + while (setupData < setupEnd) + { + uint16_t songId = SBig(*reinterpret_cast(setupData)); + const std::array* midiSetups = + reinterpret_cast*>(setupData + 4); + + for (int i=0 ; i<16 ; ++i) + (*midiSetups)[i].setIntoMusyX2((*midiSetupsBuf)[i]); + + idx.m_midiSetups[songId] = midiSetupsBuf; + setupData += 8 * 16 + 4; + ++midiSetupsBuf; + } + } + else if (header.type == GroupType::SFX) + { + SFXGroupIndex& idx = m_sfxGroups[header.groupId]; + bIdx = &idx; + + /* SFX entries */ + uint16_t count = SBig(*reinterpret_cast(subData + header.pageTableOff)); + idx.m_sfxEntries.reserve(count); + for (int i=0 ; i(subData + header.pageTableOff + 4 + i * 12); + idx.m_sfxEntries[SBig(entries->defineId)] = entries; + } + } + + if (bIdx) + { + bIdx->m_soundMacroIndex = reinterpret_cast(subData + header.soundMacroIdsOff); + bIdx->m_tablesIndex = reinterpret_cast(subData + header.tableIdsOff); + bIdx->m_keymapsIndex = reinterpret_cast(subData + header.keymapIdsOff); + bIdx->m_layersIndex = reinterpret_cast(subData + header.layerIdsOff); + } + + data += header.groupEndOff; + group = reinterpret_cast(data); + } +} + +void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, PCDataTag) +{ + size_t normPageCount = 0; + size_t drumPageCount = 0; + size_t midiSetupCount = 0; + + const GroupHeader* group = reinterpret_cast(data); + while (group->groupEndOff != 0xffffffff) + { + const unsigned char* subData = data + 8; + + if (group->type == GroupType::Song) + { + /* Normal pages */ + const MusyX1PageEntry* normEntries = + reinterpret_cast(subData + group->pageTableOff); + while (normEntries->objId != 0xffff) + { + ++normPageCount; + ++normEntries; + } + + /* Drum pages */ + const MusyX1PageEntry* drumEntries = + reinterpret_cast(subData + group->drumTableOff); + while (drumEntries->objId != 0xffff) + { + ++drumPageCount; + ++drumEntries; + } + + /* MIDI setups */ + const uint8_t* setupData = subData + group->midiSetupsOff; + const uint8_t* setupEnd = subData + group->groupEndOff; + while (setupData < setupEnd) + { + ++midiSetupCount; + setupData += 8 * 16 + 4; + } + } + + data += group->groupEndOff; + group = reinterpret_cast(data); + } + + if (normPageCount) + m_convNormalPages.reset(new SongGroupIndex::PageEntry[normPageCount]); + if (drumPageCount) + m_convDrumPages.reset(new SongGroupIndex::PageEntry[drumPageCount]); + if (midiSetupCount) + m_convMidiSetups.reset(new std::array[midiSetupCount]); +} + +AudioGroupProject::AudioGroupProject(const unsigned char* data, PCDataTag) +{ + _allocateConvBuffers(data, PCDataTag{}); + SongGroupIndex::PageEntry* normPagesBuf = m_convNormalPages.get(); + SongGroupIndex::PageEntry* drumPagesBuf = m_convDrumPages.get(); + std::array* midiSetupsBuf = m_convMidiSetups.get(); + + const GroupHeader* group = reinterpret_cast(data); + while (group->groupEndOff != 0xffffffff) + { + const unsigned char* subData = data + 8; + + AudioGroupIndex* bIdx = nullptr; + + if (group->type == GroupType::Song) + { + SongGroupIndex& idx = m_songGroups[group->groupId]; + bIdx = &idx; + + /* Normal pages */ + const MusyX1PageEntry* normEntries = + reinterpret_cast(subData + group->pageTableOff); + while (normEntries->objId != 0xffff) + { + normEntries->setIntoMusyX2(*normPagesBuf); + idx.m_normPages[normEntries->programNo] = normPagesBuf; + ++normEntries; + ++normPagesBuf; + } + + /* Drum pages */ + const MusyX1PageEntry* drumEntries = + reinterpret_cast(subData + group->drumTableOff); + while (drumEntries->objId != 0xffff) + { + drumEntries->setIntoMusyX2(*drumPagesBuf); + idx.m_drumPages[drumEntries->programNo] = drumPagesBuf; + ++drumEntries; + ++drumPagesBuf; + } + + /* MIDI setups */ + const uint8_t* setupData = subData + group->midiSetupsOff; + const uint8_t* setupEnd = subData + group->groupEndOff; + while (setupData < setupEnd) + { + uint16_t songId = SBig(*reinterpret_cast(setupData)); + const std::array* midiSetups = + reinterpret_cast*>(setupData + 4); + + for (int i=0 ; i<16 ; ++i) + (*midiSetups)[i].setIntoMusyX2((*midiSetupsBuf)[i]); + + idx.m_midiSetups[songId] = midiSetupsBuf; + setupData += 8 * 16 + 4; + ++midiSetupsBuf; + } + } + else if (group->type == GroupType::SFX) + { + SFXGroupIndex& idx = m_sfxGroups[group->groupId]; + bIdx = &idx; + + /* SFX entries */ + uint16_t count = *reinterpret_cast(subData + group->pageTableOff); + idx.m_sfxEntries.reserve(count); + for (int i=0 ; i(subData + group->pageTableOff + 4 + i * 12); + idx.m_sfxEntries[entries->defineId] = entries; + } + } + + if (bIdx) + { + bIdx->m_soundMacroIndex = reinterpret_cast(subData + group->soundMacroIdsOff); + bIdx->m_tablesIndex = reinterpret_cast(subData + group->tableIdsOff); + bIdx->m_keymapsIndex = reinterpret_cast(subData + group->keymapIdsOff); + bIdx->m_layersIndex = reinterpret_cast(subData + group->layerIdsOff); + } + + data += group->groupEndOff; + group = reinterpret_cast(data); + } +} + +AudioGroupProject AudioGroupProject::CreateAudioGroupProject(const AudioGroupData& data) +{ + switch (data.m_fmt) + { + case DataFormat::GCN: + return AudioGroupProject(data.getProj(), GCNDataTag{}); + case DataFormat::N64: + return AudioGroupProject(data.getProj(), N64DataTag{}); + case DataFormat::PC: + return AudioGroupProject(data.getProj(), PCDataTag{}); + } +} + const SongGroupIndex* AudioGroupProject::getSongGroupIndex(int groupId) const { auto search = m_songGroups.find(groupId); diff --git a/lib/AudioGroupSampleDirectory.cpp b/lib/AudioGroupSampleDirectory.cpp index 72f3b43..0f43903 100644 --- a/lib/AudioGroupSampleDirectory.cpp +++ b/lib/AudioGroupSampleDirectory.cpp @@ -19,8 +19,8 @@ void AudioGroupSampleDirectory::Entry::swapBig() void AudioGroupSampleDirectory::ADPCMParms::swapBig() { m_bytesPerFrame = SBig(m_bytesPerFrame); - m_hist1 = SBig(m_hist1); m_hist2 = SBig(m_hist2); + m_hist1 = SBig(m_hist1); for (int i=0 ; i<8 ; ++i) { m_coefs[i][0] = SBig(m_coefs[i][0]); @@ -28,7 +28,7 @@ void AudioGroupSampleDirectory::ADPCMParms::swapBig() } } -AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data) +AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data, GCNDataTag) { const unsigned char* cur = data; while (*reinterpret_cast(cur) != 0xffffffff) @@ -53,4 +53,66 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data) } } +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; + } +}; + +AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data, N64DataTag) +{ + const unsigned char* cur = data; + while (*reinterpret_cast(cur) != 0xffffffff) + { + MusyX1SdirEntry ent = *reinterpret_cast(cur); + ent.swapBig(); + + std::pair& store = m_entries[ent.m_sfxId]; + ent.setIntoMusyX2(store.first); + + cur += 24; + } +} + +AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data, PCDataTag) +{ + const unsigned char* cur = data; + while (*reinterpret_cast(cur) != 0xffffffff) + { + const MusyX1SdirEntry* ent = reinterpret_cast(cur); + + std::pair& store = m_entries[ent->m_sfxId]; + ent->setIntoMusyX2(store.first); + + cur += 24; + } +} + } diff --git a/lib/ContainerRegistry.cpp b/lib/ContainerRegistry.cpp index ad66590..4edeff2 100644 --- a/lib/ContainerRegistry.cpp +++ b/lib/ContainerRegistry.cpp @@ -228,7 +228,7 @@ static std::vector> LoadMP1(FILE fread(sdir.get(), 1, len, fp); ret.emplace_back(std::move(name), IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release()}); + sdir.release(), samp.release(), GCNDataTag{}}); } } FSeek(fp, origPos, SEEK_SET); @@ -379,7 +379,8 @@ static std::vector> LoadMP2(FILE fread(pool.get(), 1, sampSz, fp); ret.emplace_back(std::move(name), IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release()}); } + sdir.release(), samp.release(), GCNDataTag{}}); + } } FSeek(fp, origPos, SEEK_SET); } @@ -503,7 +504,7 @@ static std::vector> LoadRS1PC(FI } ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release()}); + sdir.release(), samp.release(), PCDataTag{}}); } } @@ -513,6 +514,15 @@ static std::vector> LoadRS1PC(FI static bool ValidateRS1N64(FILE* fp) { size_t endPos = FileLength(fp); + if (endPos > 32 * 1024 * 1024) + return false; /* N64 ROM definitely won't exceed 32MB */ + + FSeek(fp, 59, SEEK_SET); + uint32_t gameId; + fread(&gameId, 1, 4, fp); + if (gameId != 0x4e525345 && gameId != 0x4553524e) + return false; /* GameId not 'NRSE' */ + FSeek(fp, 0, SEEK_SET); std::unique_ptr data(new uint8_t[endPos]); fread(data.get(), 1, endPos, fp); @@ -646,6 +656,9 @@ static std::vector> LoadRS1N64(F } } } + + ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), + sdir.release(), samp.release(), N64DataTag{}}); } return ret; @@ -773,7 +786,7 @@ static std::vector> LoadRS2(FILE char name[128]; snprintf(name, 128, "GroupFile%u", j); ret.emplace_back(name, IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release()}); + sdir.release(), samp.release(), GCNDataTag{}}); } break; @@ -879,7 +892,7 @@ static std::vector> LoadRS3(FILE char name[128]; snprintf(name, 128, "GroupFile%u", j); ret.emplace_back(name, IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release()}); + sdir.release(), samp.release(), GCNDataTag{}}); } break; @@ -1086,8 +1099,18 @@ ContainerRegistry::LoadContainer(const char* path) fread(samp.get(), 1, fLen, fp); fclose(fp); - ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release()}); + + /* SDIR-based format detection */ + if (*reinterpret_cast(sdir.get() + 8) == 0x0) + ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), + sdir.release(), samp.release(), GCNDataTag{}}); + else if (sdir[9] == 0x0) + ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), + sdir.release(), samp.release(), N64DataTag{}}); + else + ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), + sdir.release(), samp.release(), PCDataTag{}}); + return ret; } diff --git a/lib/dsp.c b/lib/DSPCodec.c similarity index 99% rename from lib/dsp.c rename to lib/DSPCodec.c index f3f678b..bbe106f 100644 --- a/lib/dsp.c +++ b/lib/DSPCodec.c @@ -1,4 +1,4 @@ -#include "amuse/dsp.h" +#include "amuse/DSPCodec.h" static const int32_t NibbleToInt[16] = {0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1}; diff --git a/lib/Engine.cpp b/lib/Engine.cpp index 9a34a44..e420b85 100644 --- a/lib/Engine.cpp +++ b/lib/Engine.cpp @@ -176,14 +176,8 @@ void Engine::pumpEngine() m_nextVid = maxVid + 1; } -/** Add audio group data pointers to engine; must remain resident! */ -const AudioGroup* Engine::addAudioGroup(const AudioGroupData& data) +AudioGroup* Engine::_addAudioGroup(const AudioGroupData& data, std::unique_ptr&& grp) { - removeAudioGroup(data); - - std::unique_ptr grp = std::make_unique(data); - if (!grp) - return nullptr; AudioGroup* ret = grp.get(); m_audioGroups.emplace(std::make_pair(&data, std::move(grp))); @@ -199,6 +193,30 @@ const AudioGroup* Engine::addAudioGroup(const AudioGroupData& data) return ret; } +/** Add GameCube audio group data pointers to engine; must remain resident! */ +const AudioGroup* Engine::addAudioGroup(const AudioGroupData& data) +{ + removeAudioGroup(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, N64DataTag{}); + break; + case DataFormat::PC: + grp = std::make_unique(data, PCDataTag{}); + break; + } + if (!grp) + return nullptr; + + return _addAudioGroup(data, std::move(grp)); +} + /** Remove audio group from engine */ void Engine::removeAudioGroup(const AudioGroupData& data) { @@ -331,11 +349,14 @@ std::shared_ptr Engine::fxStart(int sfxId, float vol, float pan, Submix* std::shared_ptr ret = _allocateVoice(*grp, std::get<1>(search->second), 32000.0, true, false, smx); - if (!ret->loadSoundObject(SBig(entry->objId), 0, 1000.f, entry->defKey, entry->defVel, 0)) + + ObjectId oid = (grp->getDataFormat() == DataFormat::PC) ? entry->objId : SBig(entry->objId); + if (!ret->loadSoundObject(oid, 0, 1000.f, entry->defKey, entry->defVel, 0)) { _destroyVoice(ret.get()); return {}; } + ret->setVolume(vol); ret->setPan(pan); return ret; @@ -358,12 +379,15 @@ std::shared_ptr Engine::addEmitter(const Vector3f& pos, const Vector3f& 32000.0, true, true, smx); m_activeEmitters.emplace(m_activeEmitters.end(), new Emitter(*this, *grp, std::move(vox))); Emitter& ret = *m_activeEmitters.back(); - if (!ret.getVoice()->loadSoundObject(entry->objId, 0, 1000.f, entry->defKey, entry->defVel, 0)) + + ObjectId oid = (grp->getDataFormat() == DataFormat::PC) ? entry->objId : SBig(entry->objId); + if (!ret.getVoice()->loadSoundObject(oid, 0, 1000.f, entry->defKey, entry->defVel, 0)) { ret._destroy(); m_activeEmitters.pop_back(); return {}; } + vox->setPan(entry->panning); ret.setPos(pos); ret.setDir(dir); diff --git a/lib/N64MusyXCodec.c b/lib/N64MusyXCodec.c new file mode 100644 index 0000000..52491cd --- /dev/null +++ b/lib/N64MusyXCodec.c @@ -0,0 +1,224 @@ +#include "amuse/N64MusyXCodec.h" +#include +#include + +/* Acknowledgements: + * SubDrag for N64 Sound Tool (http://www.goldeneyevault.com/viewfile.php?id=212) + * Bobby Smiles for MusyX codec research + */ + +static int rdot(unsigned n, const int16_t* x, const int16_t* y) +{ + int accu = 0; + + y += n; + + while (n != 0) + { + accu += ((int)*(x++) * (int)*(--y)); + --n; + } + + return accu; +} + +static int16_t adpcm_get_predicted_sample(unsigned char byte, unsigned char mask, + unsigned lshift, unsigned rshift) +{ + int16_t sample = ((int16_t)byte & (int16_t)mask) << lshift; + sample >>= rshift; /* signed */ + return sample; +} + +static void adpcm_get_predicted_frame(int16_t* dst, const unsigned char* src, + const unsigned char* nibbles, + unsigned rshift) +{ + *(dst++) = (src[0] << 8) | src[1]; + *(dst++) = (src[2] << 8) | src[3]; + + for (unsigned i=1 ; i<16 ; ++i) + { + unsigned char byteData = nibbles[i]; + + *(dst++) = adpcm_get_predicted_sample(byteData, 0xf0, 8, rshift); + *(dst++) = adpcm_get_predicted_sample(byteData, 0x0f, 12, rshift); + } +} + +static unsigned adpcm_decode_upto_8_samples(int16_t* dst, const int16_t* src, + const int16_t* cb_entry, + const int16_t* last_samples, + unsigned size) +{ + if (size == 0) + return 0; + + const int16_t* book1 = cb_entry; + const int16_t* book2 = cb_entry + 8; + + int16_t l1 = last_samples[0]; + int16_t l2 = last_samples[1]; + + int accu; + + for (unsigned i=0 ; i> 11); + } + + return size; +} + +static unsigned adpcm_decode_upto_8_samples_ranged(int16_t* dst, const int16_t* src, + const int16_t* cb_entry, + const int16_t* last_samples, + unsigned firstSample, + unsigned size) +{ + if (firstSample >= size) + return 0; + + const int16_t* book1 = cb_entry; + const int16_t* book2 = cb_entry + 8; + + int16_t l1 = last_samples[0]; + int16_t l2 = last_samples[1]; + + int accu; + + unsigned ret = 0; + for (unsigned i=firstSample ; i> 11); + ++ret; + } + + return ret; +} + +unsigned N64MusyXDecompressFrame(int16_t* out, const uint8_t* in, + const int16_t coefs[8][2][8], + unsigned lastSample) +{ + int16_t frame[32]; + + unsigned samples = 0; + unsigned procSamples; + + unsigned char c2 = in[0x8]; + c2 = c2 % 0x80; + + const int16_t* bookPredictors = coefs[(c2 & 0xf0) >> 8][0]; + unsigned int rshift = (c2 & 0x0f); + + adpcm_get_predicted_frame(frame, &in[0x0], &in[0x8], rshift); + + procSamples = (lastSample < 2) ? lastSample : 2; + memcpy(out, frame, 2 * procSamples); + samples += procSamples; + if (samples == lastSample) + return samples; + +#define adpcm_decode_upto_end_samples(inOffset, bookPredictors, outOffset, size) \ + procSamples = (lastSample < size) ? lastSample : size; \ + samples += adpcm_decode_upto_8_samples(out + inOffset, frame + inOffset, bookPredictors, out + outOffset, procSamples); \ + if (samples == lastSample) \ + return samples; + + adpcm_decode_upto_end_samples(2, bookPredictors, 0, 6); + adpcm_decode_upto_end_samples(8, bookPredictors, 6, 8); + adpcm_decode_upto_end_samples(16, bookPredictors, 14, 8); + adpcm_decode_upto_end_samples(24, bookPredictors, 22, 8); + + out += 32; + + + c2 = in[0x18]; + c2 = c2 % 0x80; + + bookPredictors = coefs[(c2 & 0xf0) >> 8][0]; + rshift = (c2 & 0x0f); + + adpcm_get_predicted_frame(frame, &in[0x4], &in[0x18], rshift); + + procSamples = (lastSample < 2) ? lastSample : 2; + memcpy(out, frame, 2 * procSamples); + samples += procSamples; + if (samples == lastSample) + return samples; + + adpcm_decode_upto_end_samples(2, bookPredictors, 0, 6); + adpcm_decode_upto_end_samples(8, bookPredictors, 6, 8); + adpcm_decode_upto_end_samples(16, bookPredictors, 14, 8); + adpcm_decode_upto_end_samples(24, bookPredictors, 22, 8); + + return samples; +} + +unsigned N64MusyXDecompressFrameRanged(int16_t* out, const uint8_t* in, + const int16_t coefs[8][2][8], + unsigned firstSample, unsigned lastSample) +{ + int16_t frame[32]; + + unsigned samples = 0; + unsigned procSamples; + + unsigned char c2 = in[0x8]; + c2 = c2 % 0x80; + + const int16_t* bookPredictors = coefs[(c2 & 0xf0) >> 8][0]; + unsigned int rshift = (c2 & 0x0f); + + adpcm_get_predicted_frame(frame, &in[0x0], &in[0x8], rshift); + + procSamples = (lastSample < 2) ? lastSample : 2; + memcpy(out, frame, 2 * procSamples); + samples += procSamples; + firstSample = (2 > firstSample) ? 0 : (firstSample - 2); + if (samples == lastSample) + return samples; + +#define adpcm_decode_upto_end_samples_ranged(inOffset, bookPredictors, outOffset, size) \ + procSamples = (lastSample < size) ? lastSample : size; \ + samples += adpcm_decode_upto_8_samples_ranged(out + inOffset, frame + inOffset, bookPredictors, out + outOffset, firstSample, procSamples); \ + firstSample = (size > firstSample) ? 0 : (firstSample - size); \ + if (samples == lastSample) \ + return samples;\ + + adpcm_decode_upto_end_samples_ranged(2, bookPredictors, 0, 6); + adpcm_decode_upto_end_samples_ranged(8, bookPredictors, 6, 8); + adpcm_decode_upto_end_samples_ranged(16, bookPredictors, 14, 8); + adpcm_decode_upto_end_samples_ranged(24, bookPredictors, 22, 8); + + out += 32; + + + c2 = in[0x18]; + c2 = c2 % 0x80; + + bookPredictors = coefs[(c2 & 0xf0) >> 8][0]; + rshift = (c2 & 0x0f); + + adpcm_get_predicted_frame(frame, &in[0x4], &in[0x18], rshift); + + procSamples = (lastSample < 2) ? lastSample : 2; + memcpy(out, frame, 2 * procSamples); + samples += procSamples; + firstSample = (2 > firstSample) ? 0 : (firstSample - 2); + if (samples == lastSample) + return samples; + + adpcm_decode_upto_end_samples_ranged(2, bookPredictors, 0, 6); + adpcm_decode_upto_end_samples_ranged(8, bookPredictors, 6, 8); + adpcm_decode_upto_end_samples_ranged(16, bookPredictors, 14, 8); + adpcm_decode_upto_end_samples_ranged(24, bookPredictors, 22, 8); + + return samples; +} + diff --git a/lib/Sequencer.cpp b/lib/Sequencer.cpp index 70611e5..00bf198 100644 --- a/lib/Sequencer.cpp +++ b/lib/Sequencer.cpp @@ -160,7 +160,9 @@ std::shared_ptr Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo { m_chanVoxs[note] = ret; ret->installCtrlValues(m_ctrlVals); - if (!ret->loadSoundObject(SBig(m_page->objId), 0, 1000.f, note, velocity, m_ctrlVals[1])) + + ObjectId oid = (m_parent.m_audioGroup.getDataFormat() == DataFormat::PC) ? m_page->objId : SBig(m_page->objId); + if (!ret->loadSoundObject(oid, 0, 1000.f, note, velocity, m_ctrlVals[1])) { m_parent.m_engine._destroyVoice(ret.get()); return {}; diff --git a/lib/SoundMacroState.cpp b/lib/SoundMacroState.cpp index 42997cc..334bf19 100644 --- a/lib/SoundMacroState.cpp +++ b/lib/SoundMacroState.cpp @@ -111,13 +111,13 @@ float SoundMacroState::Evaluator::evaluate(const Voice& vox, const SoundMacroSta return value; } -void SoundMacroState::initialize(const unsigned char* ptr, int step) +void SoundMacroState::initialize(const unsigned char* ptr, int step, bool swapData) { - initialize(ptr, step, 1000.f, 0, 0, 0); + initialize(ptr, step, 1000.f, 0, 0, 0, swapData); } void SoundMacroState::initialize(const unsigned char* ptr, int step, double ticksPerSec, - uint8_t midiKey, uint8_t midiVel, uint8_t midiMod) + uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool swapData) { m_ticksPerSec = ticksPerSec; m_initKey = midiKey; @@ -137,7 +137,8 @@ void SoundMacroState::initialize(const unsigned char* ptr, int step, double tick m_useAdsrControllers = false; m_portamentoMode = 0; m_header = *reinterpret_cast(ptr); - m_header.swapBig(); + if (swapData) + m_header.swapBig(); } bool SoundMacroState::advance(Voice& vox, double dt) @@ -173,7 +174,8 @@ bool SoundMacroState::advance(Voice& vox, double dt) /* Load next command based on counter */ const Command* commands = reinterpret_cast(m_pc.back().first + sizeof(Header)); Command cmd = commands[m_pc.back().second++]; - cmd.swapBig(); + if (vox.getAudioGroup().getDataFormat() != DataFormat::PC) + cmd.swapBig(); /* Perform function of command */ switch (cmd.m_op) @@ -751,7 +753,8 @@ bool SoundMacroState::advance(Voice& vox, double dt) { m_pc.pop_back(); m_header = *reinterpret_cast(m_pc.back().first); - m_header.swapBig(); + if (vox.getAudioGroup().getDataFormat() != DataFormat::PC) + m_header.swapBig(); vox.m_objectId = m_header.m_macroId; } break; @@ -768,7 +771,8 @@ bool SoundMacroState::advance(Voice& vox, double dt) m_initKey, m_initVel, m_initMod, true); m_header = *reinterpret_cast(m_pc.back().first); - m_header.swapBig(); + if (vox.getAudioGroup().getDataFormat() != DataFormat::PC) + m_header.swapBig(); vox.m_objectId = m_header.m_macroId; break; diff --git a/lib/Voice.cpp b/lib/Voice.cpp index fc7bea2..13e2023 100644 --- a/lib/Voice.cpp +++ b/lib/Voice.cpp @@ -5,7 +5,8 @@ #include "amuse/AudioGroup.hpp" #include "amuse/Common.hpp" #include "amuse/Engine.hpp" -#include "amuse/dsp.h" +#include "amuse/DSPCodec.h" +#include "amuse/N64MusyXCodec.h" #include #include @@ -81,8 +82,9 @@ void Voice::_macroSampleEnd() m_state.sampleEndNotify(*this); } -bool Voice::_checkSamplePos() +bool Voice::_checkSamplePos(bool& looped) { + looped = false; if (!m_curSample) return true; @@ -92,8 +94,9 @@ bool Voice::_checkSamplePos() { /* Turn over looped sample */ m_curSamplePos = m_curSample->first.m_loopStartSample; - m_prev1 = m_curSample->second.m_hist2; - m_prev2 = m_curSample->second.m_hist1; + m_prev1 = m_curSample->second.m_hist1; + m_prev2 = m_curSample->second.m_hist2; + looped = true; } else { @@ -350,6 +353,19 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch) return refresh; } +uint32_t Voice::_GetBlockSampleCount(SampleFormat fmt) +{ + switch (fmt) + { + default: + return 1; + case Voice::SampleFormat::DSP: + return 14; + case Voice::SampleFormat::N64: + return 64; + } +} + size_t Voice::supplyAudio(size_t samples, int16_t* data) { uint32_t samplesRem = samples; @@ -371,105 +387,129 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data) if (m_curSample) { - uint32_t block = m_curSamplePos / 14; - uint32_t rem = m_curSamplePos % 14; - - bool refresh = false; + uint32_t blockSampleCount = _GetBlockSampleCount(m_curFormat); + uint32_t block; int32_t curPitch = m_curPitch; + bool refresh = false; - if (rem) + bool looped = true; + while (looped && samplesRem) { - uint32_t remCount = std::min(samplesRem, m_lastSamplePos - block * 14); - uint32_t decSamples; + block = m_curSamplePos / blockSampleCount; + uint32_t rem = m_curSamplePos % blockSampleCount; - switch (m_curFormat) + if (rem) { - case SampleFormat::DSP: - { - decSamples = DSPDecompressFrameRanged(data, m_curSampleData + 8 * block, - m_curSample->second.m_coefs, - &m_prev1, &m_prev2, rem, remCount); - break; - } - case SampleFormat::PCM: - { - const int16_t* pcm = reinterpret_cast(m_curSampleData); - remCount = std::min(samplesRem, m_lastSamplePos - m_curSamplePos); - for (uint32_t i=0 ; isecond.m_coefs, + &m_prev1, &m_prev2, rem, remCount); + break; + } + case SampleFormat::PCM: + { + const int16_t* pcm = reinterpret_cast(m_curSampleData); + remCount = std::min(samplesRem, m_lastSamplePos - m_curSamplePos); + for (uint32_t i=0 ; i(m_curSampleData), + rem, remCount); + break; + } + default: + memset(data, 0, sizeof(int16_t) * samples); + return samples; + } + + /* Per-sample processing */ + for (uint32_t i=0 ; isecond.m_coefs, - &m_prev1, &m_prev2, remCount); - break; - } - case SampleFormat::PCM: - { - const int16_t* pcm = reinterpret_cast(m_curSampleData); - remCount = std::min(samplesRem, m_lastSamplePos - m_curSamplePos); - for (uint32_t i=0 ; isecond.m_coefs, + &m_prev1, &m_prev2, remCount); + break; + } + case SampleFormat::PCM: + { + const int16_t* pcm = reinterpret_cast(m_curSampleData); + remCount = std::min(samplesRem, m_lastSamplePos - m_curSamplePos); + for (uint32_t i=0 ; i(m_curSampleData), + remCount); + break; + } + default: + memset(data, 0, sizeof(int16_t) * samples); + return samples; + } + + /* Per-sample processing */ + for (uint32_t i=0 ; i(macroData); - m_state.m_header.swapBig(); + if (m_audioGroup.getDataFormat() != DataFormat::PC) + m_state.m_header.swapBig(); } m_voxState = VoiceState::Playing; @@ -543,8 +585,9 @@ bool Voice::_loadKeymap(const Keymap* keymap, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc) { const Keymap& km = keymap[midiKey]; + ObjectId oid = (m_audioGroup.getDataFormat() == DataFormat::PC) ? km.objectId : SBig(km.objectId); midiKey += km.transpose; - bool ret = loadSoundObject(SBig(km.objectId), macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc); + bool ret = loadSoundObject(oid, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc); m_curVol = 1.f; _setPan((km.pan - 64) / 64.f); _setSurroundPan(-1.f); @@ -559,10 +602,11 @@ bool Voice::_loadLayer(const std::vector& layer, int macroS { if (midiKey >= mapping->keyLo && midiKey <= mapping->keyHi) { + ObjectId oid = (m_audioGroup.getDataFormat() == DataFormat::PC) ? mapping->objectId : SBig(mapping->objectId); uint8_t mappingKey = midiKey + mapping->transpose; if (m_voxState != VoiceState::Playing) { - ret |= loadSoundObject(SBig(mapping->objectId), macroStep, ticksPerSec, + ret |= loadSoundObject(oid, macroStep, ticksPerSec, mappingKey, midiVel, midiMod, pushPc); m_curVol = mapping->volume / 127.f; _setPan((mapping->pan - 64) / 64.f); @@ -571,7 +615,7 @@ bool Voice::_loadLayer(const std::vector& layer, int macroS else { std::shared_ptr vox = - _startChildMacro(SBig(mapping->objectId), macroStep, ticksPerSec, + _startChildMacro(oid, macroStep, ticksPerSec, mappingKey, midiVel, midiMod, pushPc); if (vox) { @@ -685,13 +729,16 @@ void Voice::startSample(int16_t sampId, int32_t offset) m_lastSamplePos = m_curSample->first.m_loopLengthSamples ? (m_curSample->first.m_loopStartSample + m_curSample->first.m_loopLengthSamples) : numSamples; - if (m_curFormat != SampleFormat::DSP && m_curFormat != SampleFormat::PCM) + if (m_curFormat != SampleFormat::DSP && + m_curFormat != SampleFormat::PCM && + m_curFormat != SampleFormat::N64) { m_curSample = nullptr; return; } - _checkSamplePos(); + bool looped; + _checkSamplePos(looped); /* Seek DSPADPCM state if needed */ if (m_curSample && m_curSamplePos && m_curFormat == SampleFormat::DSP)