mirror of
https://github.com/AxioDL/amuse.git
synced 2025-12-08 13:14:58 +00:00
Work on project file reading
This commit is contained in:
@@ -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; }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user