Work on project file reading

This commit is contained in:
Jack Andersen
2018-07-15 21:41:15 -10:00
parent 26cfa07f77
commit 7a38fd0676
16 changed files with 1108 additions and 351 deletions

View File

@@ -9,30 +9,26 @@ namespace amuse
{
class AudioGroupData;
using Sample = std::pair<AudioGroupSampleDirectory::Entry, AudioGroupSampleDirectory::ADPCMParms>;
/** Runtime audio group index container */
class AudioGroup
{
AudioGroupProject m_proj;
AudioGroupPool m_pool;
AudioGroupSampleDirectory m_sdir;
const unsigned char* m_samp;
DataFormat m_fmt;
const unsigned char* m_samp = nullptr;
SystemString m_groupPath;
bool m_valid;
public:
operator bool() const { return m_valid; }
AudioGroup(const AudioGroupData& data, GCNDataTag);
AudioGroup(const AudioGroupData& data, bool absOffs, N64DataTag);
AudioGroup(const AudioGroupData& data, bool absOffs, PCDataTag);
explicit AudioGroup(const AudioGroupData& data);
explicit AudioGroup(SystemStringView groupPath);
const Sample* getSample(int sfxId) const;
const unsigned char* getSampleData(uint32_t offset) const;
const AudioGroupSampleDirectory::Entry* getSample(SampleId sfxId) const;
const unsigned char* getSampleData(SampleId sfxId, const AudioGroupSampleDirectory::Entry* sample) const;
const AudioGroupProject& getProj() const { return m_proj; }
const AudioGroupPool& getPool() const { return m_pool; }
const AudioGroupSampleDirectory& getSdir() const { return m_sdir; }
DataFormat getDataFormat() const { return m_fmt; }
};
}

View File

@@ -7,6 +7,7 @@
#include <unordered_map>
#include "Entity.hpp"
#include "Common.hpp"
#include "athena/MemoryReader.hpp"
namespace amuse
{
@@ -122,9 +123,6 @@ struct SoundMacro
Invalid = 0xff
};
static std::string_view CmdOpToStr(CmdOp op);
static CmdOp CmdStrToOp(std::string_view op);
/** Base command interface. All versions of MusyX encode little-endian parameters */
struct ICmd : LittleDNAV
{
@@ -955,6 +953,11 @@ struct SoundMacro
CmdOp Isa() const { return CmdOp::IfLess; }
};
template <class R>
static std::unique_ptr<ICmd> MakeCmd(R& r);
static std::string_view CmdOpToStr(CmdOp op);
static CmdOp CmdStrToOp(std::string_view op);
std::vector<std::unique_ptr<ICmd>> m_cmds;
int assertPC(int pc) const;
@@ -977,6 +980,13 @@ struct ITable : LittleDNAV
{
AT_DECL_DNA_YAML
AT_DECL_DNAV
enum class Type
{
ADSR,
ADSRDLS,
Curve
};
virtual Type Isa() const = 0;
};
/** Defines phase-based volume curve for macro volume control */
@@ -993,6 +1003,8 @@ struct ADSR : ITable
double getDecay() const { return (decay == 0x8000) ? 0.0 : (decay / 1000.0); }
double getSustain() const { return sustain / double(0x1000); }
double getRelease() const { return release / 1000.0; }
Type Isa() const { return ITable::Type::ADSR; }
};
/** Defines phase-based volume curve for macro volume control (modified DLS standard) */
@@ -1023,6 +1035,8 @@ struct ADSRDLS : ITable
return getDecay();
return getDecay() + note * (keyToDecay / 65536.0 / 1000.0) / 128.0;
}
Type Isa() const { return ITable::Type::ADSRDLS; }
};
/** Defines arbitrary data for use as volume curve */
@@ -1031,6 +1045,8 @@ struct Curve : ITable
AT_DECL_EXPLICIT_DNA_YAML
AT_DECL_DNAV
std::vector<uint8_t> data;
Type Isa() const { return ITable::Type::Curve; }
};
/** Maps individual MIDI keys to sound-entity as indexed in table
@@ -1128,25 +1144,26 @@ struct LayerMapping : BigDNA
/** Database of functional objects within Audio Group */
class AudioGroupPool
{
std::unordered_map<ObjectId, SoundMacro> m_soundMacros;
std::unordered_map<ObjectId, std::unique_ptr<ITable>> m_tables;
std::unordered_map<ObjectId, Keymap> m_keymaps;
std::unordered_map<ObjectId, std::vector<LayerMapping>> m_layers;
std::unordered_map<SoundMacroId, SoundMacro> m_soundMacros;
std::unordered_map<TableId, std::unique_ptr<ITable>> m_tables;
std::unordered_map<KeymapId, Keymap> m_keymaps;
std::unordered_map<LayersId, std::vector<LayerMapping>> m_layers;
AudioGroupPool() = default;
template <athena::Endian DNAE>
static AudioGroupPool _AudioGroupPool(athena::io::IStreamReader& r);
public:
static AudioGroupPool CreateAudioGroupPool(const AudioGroupData& data);
static AudioGroupPool CreateAudioGroupPool(SystemStringView groupPath);
const SoundMacro* soundMacro(ObjectId id) const;
const Keymap* keymap(ObjectId id) const;
const std::vector<LayerMapping>* layer(ObjectId id) const;
const ADSR* tableAsAdsr(ObjectId id) const;
const ADSRDLS* tableAsAdsrDLS(ObjectId id) const { return reinterpret_cast<const ADSRDLS*>(tableAsAdsr(id)); }
const Curve* tableAsCurves(ObjectId id) const { return reinterpret_cast<const Curve*>(tableAsAdsr(id)); }
const ADSRDLS* tableAsAdsrDLS(ObjectId id) const;
const Curve* tableAsCurves(ObjectId id) const;
bool toYAML(athena::io::IStreamWriter& w) const;
bool toYAML(SystemStringView groupPath) const;
};
}

View File

@@ -193,6 +193,7 @@ class AudioGroupProject
static void BootstrapObjectIDs(athena::io::IStreamReader& r, bool absOffs);
public:
static AudioGroupProject CreateAudioGroupProject(const AudioGroupData& data);
static AudioGroupProject CreateAudioGroupProject(SystemStringView groupPath);
static void BootstrapObjectIDs(const AudioGroupData& data);
const SongGroupIndex* getSongGroupIndex(int groupId) const;
@@ -201,7 +202,7 @@ public:
const std::unordered_map<int, SongGroupIndex>& songGroups() const { return m_songGroups; }
const std::unordered_map<int, SFXGroupIndex>& sfxGroups() const { return m_sfxGroups; }
bool toYAML(athena::io::IStreamWriter& w) const;
bool toYAML(SystemStringView groupPath) const;
};
}

View File

@@ -9,18 +9,135 @@ namespace amuse
{
class AudioGroupData;
struct DSPADPCMHeader : BigDNA
{
AT_DECL_DNA
Value<atUint32> x0_num_samples;
Value<atUint32> x4_num_nibbles;
Value<atUint32> x8_sample_rate;
Value<atUint16> xc_loop_flag;
Value<atUint16> xe_format = 0; /* 0 for ADPCM */
Value<atUint32> x10_loop_start_nibble = 0;
Value<atUint32> x14_loop_end_nibble = 0;
Value<atUint32> x18_ca = 0;
Value<atInt16> x1c_coef[8][2];
Value<atInt16> x3c_gain = 0;
Value<atInt16> x3e_ps;
Value<atInt16> x40_hist1;
Value<atInt16> x42_hist2;
Value<atInt16> x44_loop_ps;
Value<atInt16> x46_loop_hist1 = 0;
Value<atInt16> x48_loop_hist2 = 0;
Value<atUint8> m_pitch = 0; // Stash this in the padding
Seek<21, athena::Current> pad;
};
struct WAVFormatChunk : LittleDNA
{
AT_DECL_DNA
Value<atUint16> sampleFmt = 1;
Value<atUint16> numChannels = 1;
Value<atUint32> sampleRate;
Value<atUint32> byteRate; // sampleRate * numChannels * bitsPerSample/8
Value<atUint16> blockAlign = 2; // numChannels * bitsPerSample/8
Value<atUint16> bitsPerSample = 16;
};
struct WAVSampleChunk : LittleDNA
{
AT_DECL_DNA
Value<atUint32> smplManufacturer = 0;
Value<atUint32> smplProduct = 0;
Value<atUint32> smplPeriod; // 1 / sampleRate in nanoseconds
Value<atUint32> midiNote; // native MIDI note of sample
Value<atUint32> midiPitchFrac = 0;
Value<atUint32> smpteFormat = 0;
Value<atUint32> smpteOffset = 0;
Value<atUint32> numSampleLoops = 0;
Value<atUint32> additionalDataSize = 0;
};
struct WAVSampleLoop : LittleDNA
{
AT_DECL_DNA
Value<atUint32> cuePointId = 0;
Value<atUint32> loopType = 0; // 0: forward loop
Value<atUint32> start; // in bytes
Value<atUint32> end; // in bytes
Value<atUint32> fraction = 0;
Value<atUint32> playCount = 0;
};
struct WAVHeader : LittleDNA
{
AT_DECL_DNA
Value<atUint32> riffMagic = SBIG('RIFF');
Value<atUint32> wavChuckSize; // everything - 8
Value<atUint32> wavMagic = SBIG('WAVE');
Value<atUint32> fmtMagic = SBIG('fmt ');
Value<atUint32> fmtChunkSize = 16;
WAVFormatChunk fmtChunk;
Value<atUint32> smplMagic = SBIG('smpl');
Value<atUint32> smplChunkSize = 36; // 36 + numSampleLoops*24
WAVSampleChunk smplChunk;
Value<atUint32> dataMagic = SBIG('data');
Value<atUint32> dataChunkSize; // numSamples * numChannels * bitsPerSample/8
};
struct WAVHeaderLoop : LittleDNA
{
AT_DECL_DNA
Value<atUint32> riffMagic = SBIG('RIFF');
Value<atUint32> wavChuckSize; // everything - 8
Value<atUint32> wavMagic = SBIG('WAVE');
Value<atUint32> fmtMagic = SBIG('fmt ');
Value<atUint32> fmtChunkSize = 16;
WAVFormatChunk fmtChunk;
Value<atUint32> smplMagic = SBIG('smpl');
Value<atUint32> smplChunkSize = 60; // 36 + numSampleLoops*24
WAVSampleChunk smplChunk;
WAVSampleLoop sampleLoop;
Value<atUint32> dataMagic = SBIG('data');
Value<atUint32> dataChunkSize; // numSamples * numChannels * bitsPerSample/8
};
enum class SampleFormat : uint8_t
{
DSP, /**< GCN DSP-ucode ADPCM (very common for GameCube games) */
DSP_DRUM, /**< GCN DSP-ucode ADPCM (seems to be set into drum samples for expanding their amplitude appropriately) */
PCM, /**< Big-endian PCM found in MusyX2 demo GM instruments */
N64, /**< 2-stage VADPCM coding with SAMP-embedded codebooks */
PCM_PC /**< Little-endian PCM found in PC Rogue Squadron (actually enum 0 which conflicts with DSP-ADPCM) */
};
/** Indexes individual samples in SAMP chunk */
class AudioGroupSampleDirectory
{
friend class AudioGroup;
public:
enum class SampleFormat : atUint8
{
DSP,
DSP2,
PCM,
N64
union ADPCMParms {
struct DSPParms
{
uint16_t m_bytesPerFrame;
uint8_t m_ps;
uint8_t m_lps;
int16_t m_hist2;
int16_t m_hist1;
int16_t m_coefs[8][2];
} dsp;
struct VADPCMParms
{
int16_t m_coefs[8][2][8];
} vadpcm;
void swapBigDSP();
void swapBigVADPCM();
};
template <athena::Endian DNAEn>
@@ -28,11 +145,12 @@ public:
EntryDNA : BigDNA
{
AT_DECL_DNA
SFXIdDNA<DNAEn> m_sfxId;
SampleIdDNA<DNAEn> m_sfxId;
Seek<2, athena::Current> pad;
Value<atUint32, DNAEn> m_sampleOff;
Value<atUint32, DNAEn> m_unk;
Value<atUint8, DNAEn> m_pitch;
Seek<1, athena::Current> pad;
Seek<1, athena::Current> pad2;
Value<atUint16, DNAEn> m_sampleRate;
Value<atUint32, DNAEn> m_numSamples; // Top 8 bits is SampleFormat
Value<atUint32, DNAEn> m_loopStartSample;
@@ -44,7 +162,8 @@ public:
MusyX1SdirEntry : BigDNA
{
AT_DECL_DNA
SFXIdDNA<DNAEn> m_sfxId;
SampleIdDNA<DNAEn> m_sfxId;
Seek<2, athena::Current> pad;
Value<atUint32, DNAEn> m_sampleOff;
Value<atUint32, DNAEn> m_pitchSampleRate;
Value<atUint32, DNAEn> m_numSamples;
@@ -56,7 +175,8 @@ public:
MusyX1AbsSdirEntry : BigDNA
{
AT_DECL_DNA
SFXIdDNA<DNAEn> m_sfxId;
SampleIdDNA<DNAEn> m_sfxId;
Seek<2, athena::Current> pad;
Value<uint32_t, DNAEn> m_sampleOff;
Value<uint32_t, DNAEn> m_unk;
Value<uint32_t, DNAEn> m_pitchSampleRate;
@@ -66,14 +186,23 @@ public:
};
struct Entry
{
atUint32 m_sampleOff;
atUint32 m_unk;
atUint8 m_pitch;
atUint16 m_sampleRate;
atUint32 m_numSamples; // Top 8 bits is SampleFormat
atUint32 m_loopStartSample;
atUint32 m_loopLengthSamples;
atUint32 m_adpcmParmOffset;
atUint32 m_sampleOff = 0;
atUint32 m_unk = 0;
atUint8 m_pitch = 0;
atUint16 m_sampleRate = 0;
atUint32 m_numSamples = 0; // Top 8 bits is SampleFormat
atUint32 m_loopStartSample = 0;
atUint32 m_loopLengthSamples = 0;
atUint32 m_adpcmParmOffset = 0;
/* Stored out-of-band in a platform-dependent way */
ADPCMParms m_ADPCMParms;
/* In-memory storage of an individual sample. Editors use this structure
* to override the loaded sample with a file-backed version without repacking
* the sample data into a SAMP block. */
time_t m_looseModTime = 0;
std::unique_ptr<uint8_t[]> m_looseData;
Entry() = default;
@@ -113,36 +242,32 @@ public:
ret.m_adpcmParmOffset = m_adpcmParmOffset;
return ret;
}
};
union ADPCMParms {
struct DSPParms
{
uint16_t m_bytesPerFrame;
uint8_t m_ps;
uint8_t m_lps;
int16_t m_hist2;
int16_t m_hist1;
int16_t m_coefs[8][2];
} dsp;
struct VADPCMParms
{
int16_t m_coefs[8][2][8];
} vadpcm;
void swapBigDSP();
void swapBigVADPCM();
void loadLooseData(SystemStringView basePath);
};
private:
std::unordered_map<SFXId, std::pair<Entry, ADPCMParms>> m_entries;
std::unordered_map<SampleId, Entry> m_entries;
static void _extractWAV(SampleId id, const Entry& ent, amuse::SystemStringView destDir,
const unsigned char* samp);
static void _extractCompressed(SampleId id, const Entry& ent, amuse::SystemStringView destDir,
const unsigned char* samp);
AudioGroupSampleDirectory() = default;
public:
AudioGroupSampleDirectory(athena::io::IStreamReader& r, GCNDataTag);
AudioGroupSampleDirectory(athena::io::IStreamReader& r, const unsigned char* sampData, bool absOffs, N64DataTag);
AudioGroupSampleDirectory(athena::io::IStreamReader& r, bool absOffs, PCDataTag);
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(const AudioGroupData& data);
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(SystemStringView groupPath);
const std::unordered_map<SFXId, std::pair<Entry, ADPCMParms>>& sampleEntries() const { return m_entries; }
const std::unordered_map<SampleId, Entry>& sampleEntries() const { return m_entries; }
void extractWAV(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const;
void extractAllWAV(amuse::SystemStringView destDir, const unsigned char* samp) const;
void extractCompressed(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const;
void extractAllCompressed(amuse::SystemStringView destDir, const unsigned char* samp) const;
};
}

View File

@@ -464,7 +464,7 @@ struct NameDB
std::unordered_map<std::string, ObjectId> m_stringToId;
std::unordered_map<ObjectId, std::string> m_idToString;
ObjectId generateId(Type tp);
ObjectId generateId(Type tp) const;
static std::string generateName(ObjectId id, Type tp);
std::string_view registerPair(std::string_view str, ObjectId id);
std::string_view resolveNameFromId(ObjectId id) const;

View File

@@ -58,16 +58,7 @@ class Voice : public Entity
std::list<std::shared_ptr<Voice>> m_childVoices; /**< Child voices for PLAYMACRO usage */
uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */
enum class SampleFormat : uint8_t
{
DSP, /**< GCN DSP-ucode ADPCM (very common for GameCube games) */
DSP_DRUM, /**< GCN DSP-ucode ADPCM (seems to be set into drum samples for expanding their amplitude
appropriately) */
PCM, /**< Big-endian PCM found in MusyX2 demo GM instruments */
N64, /**< 2-stage VADPCM coding with SAMP-embedded codebooks */
PCM_PC /**< Little-endian PCM found in PC Rogue Squadron (actually enum 0 which conflicts with DSP-ADPCM) */
};
const Sample* m_curSample = nullptr; /**< Current sample entry playing */
const AudioGroupSampleDirectory::Entry* m_curSample = nullptr; /**< Current sample entry playing */
const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */
SampleFormat m_curFormat; /**< Current sample format playing */
uint32_t m_curSamplePos = 0; /**< Current sample position */
@@ -236,7 +227,7 @@ public:
void message(int32_t val);
/** Start playing specified sample from within group, optionally by sample offset */
void startSample(int16_t sampId, int32_t offset);
void startSample(SampleId sampId, int32_t offset);
/** Stop playing current sample */
void stopSample();