Add N64-VADPCM decoder; DSP-ADPCM seamless loop fix

This commit is contained in:
Jack Andersen 2016-05-27 16:28:59 -10:00
parent 5bee3361b7
commit 5a9fd66dff
25 changed files with 1032 additions and 147 deletions

View File

@ -26,7 +26,8 @@ set(SOURCES
lib/EffectDelay.cpp lib/EffectDelay.cpp
lib/SurroundProfiles.cpp lib/SurroundProfiles.cpp
lib/ContainerRegistry.cpp lib/ContainerRegistry.cpp
lib/dsp.c) lib/DSPCodec.c
lib/N64MusyXCodec.c)
set(HEADERS set(HEADERS
include/amuse/AudioGroup.hpp include/amuse/AudioGroup.hpp
@ -55,7 +56,8 @@ set(HEADERS
include/amuse/ContainerRegistry.hpp include/amuse/ContainerRegistry.hpp
include/amuse/Common.hpp include/amuse/Common.hpp
include/amuse/amuse.hpp include/amuse/amuse.hpp
include/amuse/dsp.h) include/amuse/DSPCodec.h
include/amuse/N64MusyXCodec.h)
unset(EXTRAS) unset(EXTRAS)
if(TARGET boo) if(TARGET boo)

View File

@ -586,7 +586,7 @@ struct AppCallback : boo::IApplicationCallback
for (auto& grp : data) for (auto& grp : data)
{ {
/* Load project to assemble group list */ /* 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(); amuse::AudioGroupProject& proj = m_projs.back();
totalGroups += proj.sfxGroups().size() + proj.songGroups().size(); totalGroups += proj.sfxGroups().size() + proj.songGroups().size();

View File

@ -19,15 +19,19 @@ class AudioGroup
AudioGroupPool m_pool; AudioGroupPool m_pool;
AudioGroupSampleDirectory m_sdir; AudioGroupSampleDirectory m_sdir;
const unsigned char* m_samp; const unsigned char* m_samp;
DataFormat m_fmt;
bool m_valid; bool m_valid;
public: public:
operator bool() const {return m_valid;} 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 Sample* getSample(int sfxId) const;
const unsigned char* getSampleData(uint32_t offset) const; const unsigned char* getSampleData(uint32_t offset) const;
const AudioGroupProject& getProj() const {return m_proj;} const AudioGroupProject& getProj() const {return m_proj;}
const AudioGroupPool& getPool() const {return m_pool;} const AudioGroupPool& getPool() const {return m_pool;}
DataFormat getDataFormat() const {return m_fmt;}
}; };
} }

View File

@ -1,21 +1,36 @@
#ifndef __AMUSE_AUDIOGROUPDATA_HPP__ #ifndef __AMUSE_AUDIOGROUPDATA_HPP__
#define __AMUSE_AUDIOGROUPDATA_HPP__ #define __AMUSE_AUDIOGROUPDATA_HPP__
#include "Common.hpp"
namespace amuse namespace amuse
{ {
/** Simple pointer-container of the four Audio Group chunks */ /** Simple pointer-container of the four Audio Group chunks */
class AudioGroupData class AudioGroupData
{ {
friend class Engine;
friend class AudioGroupProject;
protected: protected:
unsigned char* m_proj; unsigned char* m_proj;
unsigned char* m_pool; unsigned char* m_pool;
unsigned char* m_sdir; unsigned char* m_sdir;
unsigned char* m_samp; 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: public:
AudioGroupData(unsigned char* proj, unsigned char* pool, AudioGroupData(unsigned char* proj, unsigned char* pool,
unsigned char* sdir, unsigned char* samp) unsigned char* sdir, unsigned char* samp, GCNDataTag)
: m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp) {} : 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* getProj() const {return m_proj;}
const unsigned char* getPool() const {return m_pool;} const unsigned char* getPool() const {return m_pool;}

View File

@ -98,6 +98,7 @@ class AudioGroupPool
std::unordered_map<ObjectId, std::vector<const LayerMapping*>> m_layers; std::unordered_map<ObjectId, std::vector<const LayerMapping*>> m_layers;
public: public:
AudioGroupPool(const unsigned char* data); AudioGroupPool(const unsigned char* data);
AudioGroupPool(const unsigned char* data, PCDataTag);
const unsigned char* soundMacro(ObjectId id) const; const unsigned char* soundMacro(ObjectId id) const;
const Keymap* keymap(ObjectId id) const; const Keymap* keymap(ObjectId id) const;
const std::vector<const LayerMapping*>* layer(ObjectId id) const; const std::vector<const LayerMapping*>* layer(ObjectId id) const;

View File

@ -2,12 +2,14 @@
#define __AMUSE_AUDIOGROUPPROJECT_HPP__ #define __AMUSE_AUDIOGROUPPROJECT_HPP__
#include "Entity.hpp" #include "Entity.hpp"
#include "Common.hpp"
#include <vector> #include <vector>
#include <array> #include <array>
#include <unordered_map> #include <unordered_map>
namespace amuse namespace amuse
{ {
class AudioGroupData;
/** Common index members of SongGroups and SFXGroups */ /** Common index members of SongGroups and SFXGroups */
struct AudioGroupIndex struct AudioGroupIndex
@ -68,8 +70,19 @@ class AudioGroupProject
{ {
std::unordered_map<int, SongGroupIndex> m_songGroups; std::unordered_map<int, SongGroupIndex> m_songGroups;
std::unordered_map<int, SFXGroupIndex> m_sfxGroups; std::unordered_map<int, SFXGroupIndex> m_sfxGroups;
/* MusyX 1.0 structures converted to MusyX 2.0 structures for pointer-compatible access */
std::unique_ptr<SongGroupIndex::PageEntry[]> m_convNormalPages;
std::unique_ptr<SongGroupIndex::PageEntry[]> m_convDrumPages;
std::unique_ptr<std::array<SongGroupIndex::MIDISetup, 16>[]> m_convMidiSetups;
void _allocateConvBuffers(const unsigned char* data, N64DataTag);
void _allocateConvBuffers(const unsigned char* data, PCDataTag);
public: 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 SongGroupIndex* getSongGroupIndex(int groupId) const;
const SFXGroupIndex* getSFXGroupIndex(int groupId) const; const SFXGroupIndex* getSFXGroupIndex(int groupId) const;

View File

@ -3,6 +3,7 @@
#include <unordered_map> #include <unordered_map>
#include <stdint.h> #include <stdint.h>
#include "Common.hpp"
namespace amuse namespace amuse
{ {
@ -30,15 +31,17 @@ public:
uint16_t m_bytesPerFrame; uint16_t m_bytesPerFrame;
uint8_t m_ps; uint8_t m_ps;
uint8_t m_lps; uint8_t m_lps;
int16_t m_hist1;
int16_t m_hist2; int16_t m_hist2;
int16_t m_hist1;
int16_t m_coefs[8][2]; int16_t m_coefs[8][2];
void swapBig(); void swapBig();
}; };
private: private:
std::unordered_map<uint16_t, std::pair<Entry, ADPCMParms>> m_entries; std::unordered_map<uint16_t, std::pair<Entry, ADPCMParms>> m_entries;
public: public:
AudioGroupSampleDirectory(const unsigned char* data); AudioGroupSampleDirectory(const unsigned char* data, GCNDataTag);
AudioGroupSampleDirectory(const unsigned char* data, N64DataTag);
AudioGroupSampleDirectory(const unsigned char* data, PCDataTag);
}; };
} }

View File

@ -204,6 +204,23 @@ static inline double SBig(double val) {return val;}
#endif #endif
#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__ #endif // __AMUSE_COMMON_HPP__

View File

@ -1,5 +1,5 @@
#ifndef _DSP_h #ifndef _DSPCODEC_h
#define _DSP_h #define _DSPCODEC_h
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -36,4 +36,4 @@ unsigned DSPDecompressFrameStateOnly(const uint8_t* in,
} }
#endif #endif
#endif // _DSP_h #endif // _DSPCODEC_h

View File

@ -46,6 +46,7 @@ class Engine
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random; std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
int m_nextVid = 0; int m_nextVid = 0;
AudioGroup* _addAudioGroup(const AudioGroupData& data, std::unique_ptr<AudioGroup>&& grp);
std::pair<AudioGroup*, const SongGroupIndex*> _findSongGroup(int groupId) const; std::pair<AudioGroup*, const SongGroupIndex*> _findSongGroup(int groupId) const;
std::pair<AudioGroup*, const SFXGroupIndex*> _findSFXGroup(int groupId) const; std::pair<AudioGroup*, const SFXGroupIndex*> _findSFXGroup(int groupId) const;

View File

@ -0,0 +1,29 @@
#ifndef _N64MUSYXCODEC_h
#define _N64MUSYXCODEC_h
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
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

View File

@ -215,9 +215,9 @@ class SoundMacroState
public: public:
/** initialize state for SoundMacro data at `ptr` */ /** 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, 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 /** advances `dt` seconds worth of commands in the SoundMacro
* @return `true` if END reached * @return `true` if END reached

View File

@ -50,7 +50,8 @@ class Voice : public Entity
{ {
DSP, DSP,
IMA, IMA,
PCM PCM,
N64
}; };
const Sample* m_curSample = nullptr; /**< Current sample entry playing */ const Sample* m_curSample = nullptr; /**< Current sample entry playing */
const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */ const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */
@ -128,7 +129,7 @@ class Voice : public Entity
void _destroy(); void _destroy();
void _reset(); void _reset();
bool _checkSamplePos(); bool _checkSamplePos(bool& looped);
void _doKeyOff(); void _doKeyOff();
void _macroKeyOff(); void _macroKeyOff();
void _macroSampleEnd(); void _macroSampleEnd();
@ -136,6 +137,7 @@ class Voice : public Entity
void _setTotalPitch(int32_t cents, bool slew); void _setTotalPitch(int32_t cents, bool slew);
bool _isRecursivelyDead(); bool _isRecursivelyDead();
void _bringOutYourDead(); void _bringOutYourDead();
static uint32_t _GetBlockSampleCount(SampleFormat fmt);
std::shared_ptr<Voice> _findVoice(int vid, std::weak_ptr<Voice> thisPtr); std::shared_ptr<Voice> _findVoice(int vid, std::weak_ptr<Voice> thisPtr);
std::unique_ptr<int8_t[]>& _ensureCtrlVals(); std::unique_ptr<int8_t[]>& _ensureCtrlVals();

View File

@ -4,11 +4,28 @@
namespace amuse namespace amuse
{ {
AudioGroup::AudioGroup(const AudioGroupData& data) AudioGroup::AudioGroup(const AudioGroupData& data, GCNDataTag)
: m_proj(data.getProj()), : m_proj(data.getProj(), GCNDataTag{}),
m_pool(data.getPool()), m_pool(data.getPool()),
m_sdir(data.getSdir()), m_sdir(data.getSdir(), GCNDataTag{}),
m_samp(data.getSamp()) 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 const Sample* AudioGroup::getSample(int sfxId) const

View File

@ -15,7 +15,7 @@ IntrusiveAudioGroupData::~IntrusiveAudioGroupData()
} }
IntrusiveAudioGroupData::IntrusiveAudioGroupData(IntrusiveAudioGroupData&& other) 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; m_owns = other.m_owns;
other.m_owns = false; other.m_owns = false;

View File

@ -81,6 +81,66 @@ AudioGroupPool::AudioGroupPool(const unsigned char* data)
} }
} }
AudioGroupPool::AudioGroupPool(const unsigned char* data, PCDataTag)
{
const Header* head = reinterpret_cast<const Header*>(data);
if (head->soundMacrosOffset)
{
const unsigned char* cur = data + head->soundMacrosOffset;
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff)
{
uint32_t size = *reinterpret_cast<const uint32_t*>(cur);
ObjectId id = *reinterpret_cast<const ObjectId*>(cur + 4);
m_soundMacros[id] = cur;
cur += size;
}
}
if (head->tablesOffset)
{
const unsigned char* cur = data + head->tablesOffset;
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff)
{
uint32_t size = *reinterpret_cast<const uint32_t*>(cur);
ObjectId id = *reinterpret_cast<const ObjectId*>(cur + 4);
m_tables[id] = cur + 8;
cur += size;
}
}
if (head->keymapsOffset)
{
const unsigned char* cur = data + head->keymapsOffset;
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff)
{
uint32_t size = *reinterpret_cast<const uint32_t*>(cur);
ObjectId id = *reinterpret_cast<const ObjectId*>(cur + 4);
m_keymaps[id] = reinterpret_cast<const Keymap*>(cur + 8);
cur += size;
}
}
if (head->layersOffset)
{
const unsigned char* cur = data + head->layersOffset;
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff)
{
uint32_t size = *reinterpret_cast<const uint32_t*>(cur);
ObjectId id = *reinterpret_cast<const ObjectId*>(cur + 4);
std::vector<const LayerMapping*>& mappingsOut = m_layers[id];
uint32_t count = *reinterpret_cast<const uint32_t*>(cur+8);
mappingsOut.reserve(count);
const unsigned char* subcur = cur + 12;
for (int i=0 ; i<count ; ++i)
mappingsOut.push_back(reinterpret_cast<const LayerMapping*>(subcur + i * 12));
cur += size;
}
}
}
const unsigned char* AudioGroupPool::soundMacro(ObjectId id) const const unsigned char* AudioGroupPool::soundMacro(ObjectId id) const
{ {
auto search = m_soundMacros.find(id); auto search = m_soundMacros.find(id);

View File

@ -1,4 +1,5 @@
#include "amuse/AudioGroupProject.hpp" #include "amuse/AudioGroupProject.hpp"
#include "amuse/AudioGroupData.hpp"
#include "amuse/Common.hpp" #include "amuse/Common.hpp"
#include <stdint.h> #include <stdint.h>
@ -41,7 +42,7 @@ struct GroupHeader
} }
}; };
AudioGroupProject::AudioGroupProject(const unsigned char* data) AudioGroupProject::AudioGroupProject(const unsigned char* data, GCNDataTag)
{ {
const GroupHeader* group = reinterpret_cast<const GroupHeader*>(data); const GroupHeader* group = reinterpret_cast<const GroupHeader*>(data);
while (group->groupEndOff != 0xffffffff) 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<const GroupHeader*>(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<const MusyX1PageEntry*>(subData + header.pageTableOff);
while (normEntries->objId != 0xffff)
{
++normPageCount;
++normEntries;
}
/* Drum pages */
const MusyX1PageEntry* drumEntries =
reinterpret_cast<const MusyX1PageEntry*>(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<const GroupHeader*>(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<SongGroupIndex::MIDISetup, 16>[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<SongGroupIndex::MIDISetup, 16>* midiSetupsBuf = m_convMidiSetups.get();
const GroupHeader* group = reinterpret_cast<const GroupHeader*>(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<const MusyX1PageEntry*>(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<const MusyX1PageEntry*>(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<const uint16_t*>(setupData));
const std::array<MusyX1MIDISetup, 16>* midiSetups =
reinterpret_cast<const std::array<MusyX1MIDISetup, 16>*>(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<const uint16_t*>(subData + header.pageTableOff));
idx.m_sfxEntries.reserve(count);
for (int i=0 ; i<count ; ++i)
{
const SFXGroupIndex::SFXEntry* entries =
reinterpret_cast<const SFXGroupIndex::SFXEntry*>(subData + header.pageTableOff + 4 + i * 12);
idx.m_sfxEntries[SBig(entries->defineId)] = entries;
}
}
if (bIdx)
{
bIdx->m_soundMacroIndex = reinterpret_cast<const uint16_t*>(subData + header.soundMacroIdsOff);
bIdx->m_tablesIndex = reinterpret_cast<const uint16_t*>(subData + header.tableIdsOff);
bIdx->m_keymapsIndex = reinterpret_cast<const uint16_t*>(subData + header.keymapIdsOff);
bIdx->m_layersIndex = reinterpret_cast<const uint16_t*>(subData + header.layerIdsOff);
}
data += header.groupEndOff;
group = reinterpret_cast<const GroupHeader*>(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<const GroupHeader*>(data);
while (group->groupEndOff != 0xffffffff)
{
const unsigned char* subData = data + 8;
if (group->type == GroupType::Song)
{
/* Normal pages */
const MusyX1PageEntry* normEntries =
reinterpret_cast<const MusyX1PageEntry*>(subData + group->pageTableOff);
while (normEntries->objId != 0xffff)
{
++normPageCount;
++normEntries;
}
/* Drum pages */
const MusyX1PageEntry* drumEntries =
reinterpret_cast<const MusyX1PageEntry*>(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<const GroupHeader*>(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<SongGroupIndex::MIDISetup, 16>[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<SongGroupIndex::MIDISetup, 16>* midiSetupsBuf = m_convMidiSetups.get();
const GroupHeader* group = reinterpret_cast<const GroupHeader*>(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<const MusyX1PageEntry*>(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<const MusyX1PageEntry*>(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<const uint16_t*>(setupData));
const std::array<MusyX1MIDISetup, 16>* midiSetups =
reinterpret_cast<const std::array<MusyX1MIDISetup, 16>*>(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<const uint16_t*>(subData + group->pageTableOff);
idx.m_sfxEntries.reserve(count);
for (int i=0 ; i<count ; ++i)
{
const SFXGroupIndex::SFXEntry* entries =
reinterpret_cast<const SFXGroupIndex::SFXEntry*>(subData + group->pageTableOff + 4 + i * 12);
idx.m_sfxEntries[entries->defineId] = entries;
}
}
if (bIdx)
{
bIdx->m_soundMacroIndex = reinterpret_cast<const uint16_t*>(subData + group->soundMacroIdsOff);
bIdx->m_tablesIndex = reinterpret_cast<const uint16_t*>(subData + group->tableIdsOff);
bIdx->m_keymapsIndex = reinterpret_cast<const uint16_t*>(subData + group->keymapIdsOff);
bIdx->m_layersIndex = reinterpret_cast<const uint16_t*>(subData + group->layerIdsOff);
}
data += group->groupEndOff;
group = reinterpret_cast<const GroupHeader*>(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 const SongGroupIndex* AudioGroupProject::getSongGroupIndex(int groupId) const
{ {
auto search = m_songGroups.find(groupId); auto search = m_songGroups.find(groupId);

View File

@ -19,8 +19,8 @@ void AudioGroupSampleDirectory::Entry::swapBig()
void AudioGroupSampleDirectory::ADPCMParms::swapBig() void AudioGroupSampleDirectory::ADPCMParms::swapBig()
{ {
m_bytesPerFrame = SBig(m_bytesPerFrame); m_bytesPerFrame = SBig(m_bytesPerFrame);
m_hist1 = SBig(m_hist1);
m_hist2 = SBig(m_hist2); m_hist2 = SBig(m_hist2);
m_hist1 = SBig(m_hist1);
for (int i=0 ; i<8 ; ++i) for (int i=0 ; i<8 ; ++i)
{ {
m_coefs[i][0] = SBig(m_coefs[i][0]); 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; const unsigned char* cur = data;
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff) while (*reinterpret_cast<const uint32_t*>(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<const uint32_t*>(cur) != 0xffffffff)
{
MusyX1SdirEntry ent = *reinterpret_cast<const MusyX1SdirEntry*>(cur);
ent.swapBig();
std::pair<Entry, ADPCMParms>& 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<const uint32_t*>(cur) != 0xffffffff)
{
const MusyX1SdirEntry* ent = reinterpret_cast<const MusyX1SdirEntry*>(cur);
std::pair<Entry, ADPCMParms>& store = m_entries[ent->m_sfxId];
ent->setIntoMusyX2(store.first);
cur += 24;
}
}
} }

View File

@ -228,7 +228,7 @@ static std::vector<std::pair<std::string, IntrusiveAudioGroupData>> LoadMP1(FILE
fread(sdir.get(), 1, len, fp); fread(sdir.get(), 1, len, fp);
ret.emplace_back(std::move(name), IntrusiveAudioGroupData{proj.release(), pool.release(), 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); FSeek(fp, origPos, SEEK_SET);
@ -379,7 +379,8 @@ static std::vector<std::pair<std::string, IntrusiveAudioGroupData>> LoadMP2(FILE
fread(pool.get(), 1, sampSz, fp); fread(pool.get(), 1, sampSz, fp);
ret.emplace_back(std::move(name), IntrusiveAudioGroupData{proj.release(), pool.release(), 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); FSeek(fp, origPos, SEEK_SET);
} }
@ -503,7 +504,7 @@ static std::vector<std::pair<std::string, IntrusiveAudioGroupData>> LoadRS1PC(FI
} }
ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), 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<std::pair<std::string, IntrusiveAudioGroupData>> LoadRS1PC(FI
static bool ValidateRS1N64(FILE* fp) static bool ValidateRS1N64(FILE* fp)
{ {
size_t endPos = FileLength(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<uint8_t[]> data(new uint8_t[endPos]); std::unique_ptr<uint8_t[]> data(new uint8_t[endPos]);
fread(data.get(), 1, endPos, fp); fread(data.get(), 1, endPos, fp);
@ -646,6 +656,9 @@ static std::vector<std::pair<std::string, IntrusiveAudioGroupData>> LoadRS1N64(F
} }
} }
} }
ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(),
sdir.release(), samp.release(), N64DataTag{}});
} }
return ret; return ret;
@ -773,7 +786,7 @@ static std::vector<std::pair<std::string, IntrusiveAudioGroupData>> LoadRS2(FILE
char name[128]; char name[128];
snprintf(name, 128, "GroupFile%u", j); snprintf(name, 128, "GroupFile%u", j);
ret.emplace_back(name, IntrusiveAudioGroupData{proj.release(), pool.release(), ret.emplace_back(name, IntrusiveAudioGroupData{proj.release(), pool.release(),
sdir.release(), samp.release()}); sdir.release(), samp.release(), GCNDataTag{}});
} }
break; break;
@ -879,7 +892,7 @@ static std::vector<std::pair<std::string, IntrusiveAudioGroupData>> LoadRS3(FILE
char name[128]; char name[128];
snprintf(name, 128, "GroupFile%u", j); snprintf(name, 128, "GroupFile%u", j);
ret.emplace_back(name, IntrusiveAudioGroupData{proj.release(), pool.release(), ret.emplace_back(name, IntrusiveAudioGroupData{proj.release(), pool.release(),
sdir.release(), samp.release()}); sdir.release(), samp.release(), GCNDataTag{}});
} }
break; break;
@ -1086,8 +1099,18 @@ ContainerRegistry::LoadContainer(const char* path)
fread(samp.get(), 1, fLen, fp); fread(samp.get(), 1, fLen, fp);
fclose(fp); fclose(fp);
/* SDIR-based format detection */
if (*reinterpret_cast<uint32_t*>(sdir.get() + 8) == 0x0)
ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(),
sdir.release(), samp.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; return ret;
} }

View File

@ -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}; static const int32_t NibbleToInt[16] = {0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1};

View File

@ -176,14 +176,8 @@ void Engine::pumpEngine()
m_nextVid = maxVid + 1; m_nextVid = maxVid + 1;
} }
/** Add audio group data pointers to engine; must remain resident! */ AudioGroup* Engine::_addAudioGroup(const AudioGroupData& data, std::unique_ptr<AudioGroup>&& grp)
const AudioGroup* Engine::addAudioGroup(const AudioGroupData& data)
{ {
removeAudioGroup(data);
std::unique_ptr<AudioGroup> grp = std::make_unique<AudioGroup>(data);
if (!grp)
return nullptr;
AudioGroup* ret = grp.get(); AudioGroup* ret = grp.get();
m_audioGroups.emplace(std::make_pair(&data, std::move(grp))); m_audioGroups.emplace(std::make_pair(&data, std::move(grp)));
@ -199,6 +193,30 @@ const AudioGroup* Engine::addAudioGroup(const AudioGroupData& data)
return ret; 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<AudioGroup> grp;
switch (data.m_fmt)
{
case DataFormat::GCN:
grp = std::make_unique<AudioGroup>(data, GCNDataTag{});
break;
case DataFormat::N64:
grp = std::make_unique<AudioGroup>(data, N64DataTag{});
break;
case DataFormat::PC:
grp = std::make_unique<AudioGroup>(data, PCDataTag{});
break;
}
if (!grp)
return nullptr;
return _addAudioGroup(data, std::move(grp));
}
/** Remove audio group from engine */ /** Remove audio group from engine */
void Engine::removeAudioGroup(const AudioGroupData& data) void Engine::removeAudioGroup(const AudioGroupData& data)
{ {
@ -331,11 +349,14 @@ std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, Submix*
std::shared_ptr<Voice> ret = _allocateVoice(*grp, std::get<1>(search->second), std::shared_ptr<Voice> ret = _allocateVoice(*grp, std::get<1>(search->second),
32000.0, true, false, smx); 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()); _destroyVoice(ret.get());
return {}; return {};
} }
ret->setVolume(vol); ret->setVolume(vol);
ret->setPan(pan); ret->setPan(pan);
return ret; return ret;
@ -358,12 +379,15 @@ std::shared_ptr<Emitter> Engine::addEmitter(const Vector3f& pos, const Vector3f&
32000.0, true, true, smx); 32000.0, true, true, smx);
m_activeEmitters.emplace(m_activeEmitters.end(), new Emitter(*this, *grp, std::move(vox))); m_activeEmitters.emplace(m_activeEmitters.end(), new Emitter(*this, *grp, std::move(vox)));
Emitter& ret = *m_activeEmitters.back(); 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(); ret._destroy();
m_activeEmitters.pop_back(); m_activeEmitters.pop_back();
return {}; return {};
} }
vox->setPan(entry->panning); vox->setPan(entry->panning);
ret.setPos(pos); ret.setPos(pos);
ret.setDir(dir); ret.setDir(dir);

224
lib/N64MusyXCodec.c Normal file
View File

@ -0,0 +1,224 @@
#include "amuse/N64MusyXCodec.h"
#include <stdint.h>
#include <string.h>
/* 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<size ; ++i)
{
accu = (int)src[i] << 11;
accu += book1[i] * l1 + book2[i] * l2 + rdot(i, book2, src);
dst[i] = N64MusyXSampClamp(accu >> 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<size ; ++i)
{
accu = (int)src[i] << 11;
accu += book1[i] * l1 + book2[i] * l2 + rdot(i, book2, src);
*dst++ = N64MusyXSampClamp(accu >> 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;
}

View File

@ -160,7 +160,9 @@ std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo
{ {
m_chanVoxs[note] = ret; m_chanVoxs[note] = ret;
ret->installCtrlValues(m_ctrlVals); 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()); m_parent.m_engine._destroyVoice(ret.get());
return {}; return {};

View File

@ -111,13 +111,13 @@ float SoundMacroState::Evaluator::evaluate(const Voice& vox, const SoundMacroSta
return value; 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, 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_ticksPerSec = ticksPerSec;
m_initKey = midiKey; m_initKey = midiKey;
@ -137,6 +137,7 @@ void SoundMacroState::initialize(const unsigned char* ptr, int step, double tick
m_useAdsrControllers = false; m_useAdsrControllers = false;
m_portamentoMode = 0; m_portamentoMode = 0;
m_header = *reinterpret_cast<const Header*>(ptr); m_header = *reinterpret_cast<const Header*>(ptr);
if (swapData)
m_header.swapBig(); m_header.swapBig();
} }
@ -173,6 +174,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
/* Load next command based on counter */ /* Load next command based on counter */
const Command* commands = reinterpret_cast<const Command*>(m_pc.back().first + sizeof(Header)); const Command* commands = reinterpret_cast<const Command*>(m_pc.back().first + sizeof(Header));
Command cmd = commands[m_pc.back().second++]; Command cmd = commands[m_pc.back().second++];
if (vox.getAudioGroup().getDataFormat() != DataFormat::PC)
cmd.swapBig(); cmd.swapBig();
/* Perform function of command */ /* Perform function of command */
@ -751,6 +753,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
{ {
m_pc.pop_back(); m_pc.pop_back();
m_header = *reinterpret_cast<const Header*>(m_pc.back().first); m_header = *reinterpret_cast<const Header*>(m_pc.back().first);
if (vox.getAudioGroup().getDataFormat() != DataFormat::PC)
m_header.swapBig(); m_header.swapBig();
vox.m_objectId = m_header.m_macroId; vox.m_objectId = m_header.m_macroId;
} }
@ -768,6 +771,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
m_initKey, m_initVel, m_initMod, true); m_initKey, m_initVel, m_initMod, true);
m_header = *reinterpret_cast<const Header*>(m_pc.back().first); m_header = *reinterpret_cast<const Header*>(m_pc.back().first);
if (vox.getAudioGroup().getDataFormat() != DataFormat::PC)
m_header.swapBig(); m_header.swapBig();
vox.m_objectId = m_header.m_macroId; vox.m_objectId = m_header.m_macroId;

View File

@ -5,7 +5,8 @@
#include "amuse/AudioGroup.hpp" #include "amuse/AudioGroup.hpp"
#include "amuse/Common.hpp" #include "amuse/Common.hpp"
#include "amuse/Engine.hpp" #include "amuse/Engine.hpp"
#include "amuse/dsp.h" #include "amuse/DSPCodec.h"
#include "amuse/N64MusyXCodec.h"
#include <cmath> #include <cmath>
#include <string.h> #include <string.h>
@ -81,8 +82,9 @@ void Voice::_macroSampleEnd()
m_state.sampleEndNotify(*this); m_state.sampleEndNotify(*this);
} }
bool Voice::_checkSamplePos() bool Voice::_checkSamplePos(bool& looped)
{ {
looped = false;
if (!m_curSample) if (!m_curSample)
return true; return true;
@ -92,8 +94,9 @@ bool Voice::_checkSamplePos()
{ {
/* Turn over looped sample */ /* Turn over looped sample */
m_curSamplePos = m_curSample->first.m_loopStartSample; m_curSamplePos = m_curSample->first.m_loopStartSample;
m_prev1 = m_curSample->second.m_hist2; m_prev1 = m_curSample->second.m_hist1;
m_prev2 = m_curSample->second.m_hist1; m_prev2 = m_curSample->second.m_hist2;
looped = true;
} }
else else
{ {
@ -350,6 +353,19 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
return refresh; 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) size_t Voice::supplyAudio(size_t samples, int16_t* data)
{ {
uint32_t samplesRem = samples; uint32_t samplesRem = samples;
@ -371,15 +387,20 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
if (m_curSample) if (m_curSample)
{ {
uint32_t block = m_curSamplePos / 14; uint32_t blockSampleCount = _GetBlockSampleCount(m_curFormat);
uint32_t rem = m_curSamplePos % 14; uint32_t block;
bool refresh = false;
int32_t curPitch = m_curPitch; int32_t curPitch = m_curPitch;
bool refresh = false;
bool looped = true;
while (looped && samplesRem)
{
block = m_curSamplePos / blockSampleCount;
uint32_t rem = m_curSamplePos % blockSampleCount;
if (rem) if (rem)
{ {
uint32_t remCount = std::min(samplesRem, m_lastSamplePos - block * 14); uint32_t remCount = std::min(samplesRem, m_lastSamplePos - block * blockSampleCount);
uint32_t decSamples; uint32_t decSamples;
switch (m_curFormat) switch (m_curFormat)
@ -400,6 +421,13 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
decSamples = remCount; decSamples = remCount;
break; break;
} }
case SampleFormat::N64:
{
decSamples = N64MusyXDecompressFrameRanged(data, m_curSampleData + 256 + 40 * block,
reinterpret_cast<const int16_t(*)[2][8]>(m_curSampleData),
rem, remCount);
break;
}
default: default:
memset(data, 0, sizeof(int16_t) * samples); memset(data, 0, sizeof(int16_t) * samples);
return samples; return samples;
@ -417,17 +445,19 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
data += decSamples; data += decSamples;
} }
if (_checkSamplePos()) if (_checkSamplePos(looped))
{ {
if (samplesRem) if (samplesRem)
memset(data, 0, sizeof(int16_t) * samplesRem); memset(data, 0, sizeof(int16_t) * samplesRem);
return samples; return samples;
} }
if (looped)
continue;
while (samplesRem) while (samplesRem)
{ {
block = m_curSamplePos / 14; block = m_curSamplePos / blockSampleCount;
uint32_t remCount = std::min(samplesRem, m_lastSamplePos - block * 14); uint32_t remCount = std::min(samplesRem, m_lastSamplePos - block * blockSampleCount);
uint32_t decSamples; uint32_t decSamples;
switch (m_curFormat) switch (m_curFormat)
@ -448,6 +478,13 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
decSamples = remCount; decSamples = remCount;
break; break;
} }
case SampleFormat::N64:
{
decSamples = N64MusyXDecompressFrame(data, m_curSampleData + 256 + 40 * block,
reinterpret_cast<const int16_t(*)[2][8]>(m_curSampleData),
remCount);
break;
}
default: default:
memset(data, 0, sizeof(int16_t) * samples); memset(data, 0, sizeof(int16_t) * samples);
return samples; return samples;
@ -464,12 +501,15 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
samplesRem -= decSamples; samplesRem -= decSamples;
data += decSamples; data += decSamples;
if (_checkSamplePos()) if (_checkSamplePos(looped))
{ {
if (samplesRem) if (samplesRem)
memset(data, 0, sizeof(int16_t) * samplesRem); memset(data, 0, sizeof(int16_t) * samplesRem);
return samples; return samples;
} }
if (looped)
break;
}
} }
if (refresh) if (refresh)
@ -524,13 +564,15 @@ bool Voice::_loadSoundMacro(const unsigned char* macroData, int macroStep, doubl
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc) uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc)
{ {
if (m_state.m_pc.empty()) if (m_state.m_pc.empty())
m_state.initialize(macroData, macroStep, ticksPerSec, midiKey, midiVel, midiMod); m_state.initialize(macroData, macroStep, ticksPerSec, midiKey, midiVel, midiMod,
m_audioGroup.getDataFormat() != DataFormat::PC);
else else
{ {
if (!pushPc) if (!pushPc)
m_state.m_pc.clear(); m_state.m_pc.clear();
m_state.m_pc.push_back({macroData, macroStep}); m_state.m_pc.push_back({macroData, macroStep});
m_state.m_header = *reinterpret_cast<const SoundMacroState::Header*>(macroData); m_state.m_header = *reinterpret_cast<const SoundMacroState::Header*>(macroData);
if (m_audioGroup.getDataFormat() != DataFormat::PC)
m_state.m_header.swapBig(); m_state.m_header.swapBig();
} }
@ -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) uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc)
{ {
const Keymap& km = keymap[midiKey]; const Keymap& km = keymap[midiKey];
ObjectId oid = (m_audioGroup.getDataFormat() == DataFormat::PC) ? km.objectId : SBig(km.objectId);
midiKey += km.transpose; 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; m_curVol = 1.f;
_setPan((km.pan - 64) / 64.f); _setPan((km.pan - 64) / 64.f);
_setSurroundPan(-1.f); _setSurroundPan(-1.f);
@ -559,10 +602,11 @@ bool Voice::_loadLayer(const std::vector<const LayerMapping*>& layer, int macroS
{ {
if (midiKey >= mapping->keyLo && midiKey <= mapping->keyHi) 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; uint8_t mappingKey = midiKey + mapping->transpose;
if (m_voxState != VoiceState::Playing) if (m_voxState != VoiceState::Playing)
{ {
ret |= loadSoundObject(SBig(mapping->objectId), macroStep, ticksPerSec, ret |= loadSoundObject(oid, macroStep, ticksPerSec,
mappingKey, midiVel, midiMod, pushPc); mappingKey, midiVel, midiMod, pushPc);
m_curVol = mapping->volume / 127.f; m_curVol = mapping->volume / 127.f;
_setPan((mapping->pan - 64) / 64.f); _setPan((mapping->pan - 64) / 64.f);
@ -571,7 +615,7 @@ bool Voice::_loadLayer(const std::vector<const LayerMapping*>& layer, int macroS
else else
{ {
std::shared_ptr<Voice> vox = std::shared_ptr<Voice> vox =
_startChildMacro(SBig(mapping->objectId), macroStep, ticksPerSec, _startChildMacro(oid, macroStep, ticksPerSec,
mappingKey, midiVel, midiMod, pushPc); mappingKey, midiVel, midiMod, pushPc);
if (vox) if (vox)
{ {
@ -685,13 +729,16 @@ void Voice::startSample(int16_t sampId, int32_t offset)
m_lastSamplePos = m_curSample->first.m_loopLengthSamples ? m_lastSamplePos = m_curSample->first.m_loopLengthSamples ?
(m_curSample->first.m_loopStartSample + m_curSample->first.m_loopLengthSamples) : numSamples; (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; m_curSample = nullptr;
return; return;
} }
_checkSamplePos(); bool looped;
_checkSamplePos(looped);
/* Seek DSPADPCM state if needed */ /* Seek DSPADPCM state if needed */
if (m_curSample && m_curSamplePos && m_curFormat == SampleFormat::DSP) if (m_curSample && m_curSamplePos && m_curFormat == SampleFormat::DSP)