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

@ -225,10 +225,10 @@ void MainWindow::importAction()
{ {
auto data = amuse::ContainerRegistry::LoadContainer(QStringToSysString(dir.filePath(fPath)).c_str()); auto data = amuse::ContainerRegistry::LoadContainer(QStringToSysString(dir.filePath(fPath)).c_str());
for (auto& p : data) for (auto& p : data)
if (!m_projectModel->importGroupData(SysStringToQString(p.first), std::move(p.second), if (!m_projectModel->importGroupData(SysStringToQString(p.first), std::move(p.second)))
ProjectModel::ImportMode(impMode)))
return; return;
} }
m_projectModel->extractSamples(ProjectModel::ImportMode(impMode), this);
m_projectModel->saveToFile(this); m_projectModel->saveToFile(this);
return; return;
} }
@ -254,10 +254,10 @@ void MainWindow::importAction()
/* Handle single container */ /* Handle single container */
auto data = amuse::ContainerRegistry::LoadContainer(QStringToSysString(path).c_str()); auto data = amuse::ContainerRegistry::LoadContainer(QStringToSysString(path).c_str());
for (auto& p : data) for (auto& p : data)
if (!m_projectModel->importGroupData(SysStringToQString(p.first), std::move(p.second), if (!m_projectModel->importGroupData(SysStringToQString(p.first), std::move(p.second)))
ProjectModel::ImportMode(impMode)))
return; return;
m_projectModel->extractSamples(ProjectModel::ImportMode(impMode), this);
m_projectModel->saveToFile(this); m_projectModel->saveToFile(this);
} }

View File

@ -17,7 +17,7 @@ ProjectModel::ProjectGroup::ProjectGroup(amuse::IntrusiveAudioGroupData&& data)
m_sdir(amuse::AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(m_data)) m_sdir(amuse::AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(m_data))
{} {}
bool ProjectModel::importGroupData(const QString& groupName, amuse::IntrusiveAudioGroupData&& data, ImportMode mode) bool ProjectModel::importGroupData(const QString& groupName, amuse::IntrusiveAudioGroupData&& data)
{ {
setIdDatabases(); setIdDatabases();
@ -28,6 +28,42 @@ bool ProjectModel::importGroupData(const QString& groupName, amuse::IntrusiveAud
return true; return true;
} }
bool ProjectModel::extractSamples(ImportMode mode, QWidget* parent)
{
setIdDatabases();
if (!MkPath(m_dir.path(), parent))
return false;
for (auto& g : m_groups)
{
g.second.setIdDatabases();
QDir dir(QFileInfo(m_dir, g.first).filePath());
if (!MkPath(dir.path(), parent))
return false;
amuse::SystemString sysDir = QStringToSysString(dir.path());
switch (mode)
{
case ImportMode::Original:
g.second.m_sdir.extractAllCompressed(sysDir, g.second.m_data.getSamp());
break;
case ImportMode::WAVs:
g.second.m_sdir.extractAllWAV(sysDir, g.second.m_data.getSamp());
break;
case ImportMode::Both:
g.second.m_sdir.extractAllCompressed(sysDir, g.second.m_data.getSamp());
g.second.m_sdir.extractAllWAV(sysDir, g.second.m_data.getSamp());
break;
default:
break;
}
}
return true;
}
bool ProjectModel::saveToFile(QWidget* parent) bool ProjectModel::saveToFile(QWidget* parent)
{ {
setIdDatabases(); setIdDatabases();
@ -42,15 +78,9 @@ bool ProjectModel::saveToFile(QWidget* parent)
return false; return false;
g.second.setIdDatabases(); g.second.setIdDatabases();
{ amuse::SystemString groupPath = QStringToSysString(dir.path());
athena::io::FileWriter fo(QStringToSysString(dir.filePath("!project.yaml"))); g.second.m_proj.toYAML(groupPath);
g.second.m_proj.toYAML(fo); g.second.m_pool.toYAML(groupPath);
}
{
athena::io::FileWriter fo(QStringToSysString(dir.filePath("!pool.yaml")));
g.second.m_pool.toYAML(fo);
}
//g.second.m_sdir.sampleEntries()
} }
return true; return true;

View File

@ -58,7 +58,8 @@ private:
public: public:
explicit ProjectModel(const QString& path, QObject* parent = Q_NULLPTR); explicit ProjectModel(const QString& path, QObject* parent = Q_NULLPTR);
bool importGroupData(const QString& groupName, amuse::IntrusiveAudioGroupData&& data, ImportMode mode); bool importGroupData(const QString& groupName, amuse::IntrusiveAudioGroupData&& data);
bool extractSamples(ImportMode mode, QWidget* parent);
bool saveToFile(QWidget* parent); bool saveToFile(QWidget* parent);
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;

View File

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

View File

@ -7,6 +7,7 @@
#include <unordered_map> #include <unordered_map>
#include "Entity.hpp" #include "Entity.hpp"
#include "Common.hpp" #include "Common.hpp"
#include "athena/MemoryReader.hpp"
namespace amuse namespace amuse
{ {
@ -122,9 +123,6 @@ struct SoundMacro
Invalid = 0xff 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 */ /** Base command interface. All versions of MusyX encode little-endian parameters */
struct ICmd : LittleDNAV struct ICmd : LittleDNAV
{ {
@ -955,6 +953,11 @@ struct SoundMacro
CmdOp Isa() const { return CmdOp::IfLess; } 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; std::vector<std::unique_ptr<ICmd>> m_cmds;
int assertPC(int pc) const; int assertPC(int pc) const;
@ -977,6 +980,13 @@ struct ITable : LittleDNAV
{ {
AT_DECL_DNA_YAML AT_DECL_DNA_YAML
AT_DECL_DNAV AT_DECL_DNAV
enum class Type
{
ADSR,
ADSRDLS,
Curve
};
virtual Type Isa() const = 0;
}; };
/** Defines phase-based volume curve for macro volume control */ /** 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 getDecay() const { return (decay == 0x8000) ? 0.0 : (decay / 1000.0); }
double getSustain() const { return sustain / double(0x1000); } double getSustain() const { return sustain / double(0x1000); }
double getRelease() const { return release / 1000.0; } 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) */ /** Defines phase-based volume curve for macro volume control (modified DLS standard) */
@ -1023,6 +1035,8 @@ struct ADSRDLS : ITable
return getDecay(); return getDecay();
return getDecay() + note * (keyToDecay / 65536.0 / 1000.0) / 128.0; 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 */ /** Defines arbitrary data for use as volume curve */
@ -1031,6 +1045,8 @@ struct Curve : ITable
AT_DECL_EXPLICIT_DNA_YAML AT_DECL_EXPLICIT_DNA_YAML
AT_DECL_DNAV AT_DECL_DNAV
std::vector<uint8_t> data; std::vector<uint8_t> data;
Type Isa() const { return ITable::Type::Curve; }
}; };
/** Maps individual MIDI keys to sound-entity as indexed in table /** 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 */ /** Database of functional objects within Audio Group */
class AudioGroupPool class AudioGroupPool
{ {
std::unordered_map<ObjectId, SoundMacro> m_soundMacros; std::unordered_map<SoundMacroId, SoundMacro> m_soundMacros;
std::unordered_map<ObjectId, std::unique_ptr<ITable>> m_tables; std::unordered_map<TableId, std::unique_ptr<ITable>> m_tables;
std::unordered_map<ObjectId, Keymap> m_keymaps; std::unordered_map<KeymapId, Keymap> m_keymaps;
std::unordered_map<ObjectId, std::vector<LayerMapping>> m_layers; std::unordered_map<LayersId, std::vector<LayerMapping>> m_layers;
AudioGroupPool() = default; AudioGroupPool() = default;
template <athena::Endian DNAE> template <athena::Endian DNAE>
static AudioGroupPool _AudioGroupPool(athena::io::IStreamReader& r); static AudioGroupPool _AudioGroupPool(athena::io::IStreamReader& r);
public: public:
static AudioGroupPool CreateAudioGroupPool(const AudioGroupData& data); static AudioGroupPool CreateAudioGroupPool(const AudioGroupData& data);
static AudioGroupPool CreateAudioGroupPool(SystemStringView groupPath);
const SoundMacro* soundMacro(ObjectId id) const; const SoundMacro* soundMacro(ObjectId id) const;
const Keymap* keymap(ObjectId id) const; const Keymap* keymap(ObjectId id) const;
const std::vector<LayerMapping>* layer(ObjectId id) const; const std::vector<LayerMapping>* layer(ObjectId id) const;
const ADSR* tableAsAdsr(ObjectId id) const; const ADSR* tableAsAdsr(ObjectId id) const;
const ADSRDLS* tableAsAdsrDLS(ObjectId id) const { return reinterpret_cast<const ADSRDLS*>(tableAsAdsr(id)); } const ADSRDLS* tableAsAdsrDLS(ObjectId id) const;
const Curve* tableAsCurves(ObjectId id) const { return reinterpret_cast<const Curve*>(tableAsAdsr(id)); } 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); static void BootstrapObjectIDs(athena::io::IStreamReader& r, bool absOffs);
public: public:
static AudioGroupProject CreateAudioGroupProject(const AudioGroupData& data); static AudioGroupProject CreateAudioGroupProject(const AudioGroupData& data);
static AudioGroupProject CreateAudioGroupProject(SystemStringView groupPath);
static void BootstrapObjectIDs(const AudioGroupData& data); static void BootstrapObjectIDs(const AudioGroupData& data);
const SongGroupIndex* getSongGroupIndex(int groupId) const; 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, SongGroupIndex>& songGroups() const { return m_songGroups; }
const std::unordered_map<int, SFXGroupIndex>& sfxGroups() const { return m_sfxGroups; } 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; 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 */ /** Indexes individual samples in SAMP chunk */
class AudioGroupSampleDirectory class AudioGroupSampleDirectory
{ {
friend class AudioGroup; friend class AudioGroup;
public: public:
enum class SampleFormat : atUint8 union ADPCMParms {
struct DSPParms
{ {
DSP, uint16_t m_bytesPerFrame;
DSP2, uint8_t m_ps;
PCM, uint8_t m_lps;
N64 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> template <athena::Endian DNAEn>
@ -28,11 +145,12 @@ public:
EntryDNA : BigDNA EntryDNA : BigDNA
{ {
AT_DECL_DNA 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_sampleOff;
Value<atUint32, DNAEn> m_unk; Value<atUint32, DNAEn> m_unk;
Value<atUint8, DNAEn> m_pitch; Value<atUint8, DNAEn> m_pitch;
Seek<1, athena::Current> pad; Seek<1, athena::Current> pad2;
Value<atUint16, DNAEn> m_sampleRate; Value<atUint16, DNAEn> m_sampleRate;
Value<atUint32, DNAEn> m_numSamples; // Top 8 bits is SampleFormat Value<atUint32, DNAEn> m_numSamples; // Top 8 bits is SampleFormat
Value<atUint32, DNAEn> m_loopStartSample; Value<atUint32, DNAEn> m_loopStartSample;
@ -44,7 +162,8 @@ public:
MusyX1SdirEntry : BigDNA MusyX1SdirEntry : BigDNA
{ {
AT_DECL_DNA 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_sampleOff;
Value<atUint32, DNAEn> m_pitchSampleRate; Value<atUint32, DNAEn> m_pitchSampleRate;
Value<atUint32, DNAEn> m_numSamples; Value<atUint32, DNAEn> m_numSamples;
@ -56,7 +175,8 @@ public:
MusyX1AbsSdirEntry : BigDNA MusyX1AbsSdirEntry : BigDNA
{ {
AT_DECL_DNA 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_sampleOff;
Value<uint32_t, DNAEn> m_unk; Value<uint32_t, DNAEn> m_unk;
Value<uint32_t, DNAEn> m_pitchSampleRate; Value<uint32_t, DNAEn> m_pitchSampleRate;
@ -66,14 +186,23 @@ public:
}; };
struct Entry struct Entry
{ {
atUint32 m_sampleOff; atUint32 m_sampleOff = 0;
atUint32 m_unk; atUint32 m_unk = 0;
atUint8 m_pitch; atUint8 m_pitch = 0;
atUint16 m_sampleRate; atUint16 m_sampleRate = 0;
atUint32 m_numSamples; // Top 8 bits is SampleFormat atUint32 m_numSamples = 0; // Top 8 bits is SampleFormat
atUint32 m_loopStartSample; atUint32 m_loopStartSample = 0;
atUint32 m_loopLengthSamples; atUint32 m_loopLengthSamples = 0;
atUint32 m_adpcmParmOffset; 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; Entry() = default;
@ -113,36 +242,32 @@ public:
ret.m_adpcmParmOffset = m_adpcmParmOffset; ret.m_adpcmParmOffset = m_adpcmParmOffset;
return ret; return ret;
} }
};
union ADPCMParms { void loadLooseData(SystemStringView basePath);
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();
}; };
private: 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: public:
AudioGroupSampleDirectory(athena::io::IStreamReader& r, GCNDataTag); AudioGroupSampleDirectory(athena::io::IStreamReader& r, GCNDataTag);
AudioGroupSampleDirectory(athena::io::IStreamReader& r, const unsigned char* sampData, bool absOffs, N64DataTag); AudioGroupSampleDirectory(athena::io::IStreamReader& r, const unsigned char* sampData, bool absOffs, N64DataTag);
AudioGroupSampleDirectory(athena::io::IStreamReader& r, bool absOffs, PCDataTag); AudioGroupSampleDirectory(athena::io::IStreamReader& r, bool absOffs, PCDataTag);
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(const AudioGroupData& data); 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<std::string, ObjectId> m_stringToId;
std::unordered_map<ObjectId, std::string> m_idToString; 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); static std::string generateName(ObjectId id, Type tp);
std::string_view registerPair(std::string_view str, ObjectId id); std::string_view registerPair(std::string_view str, ObjectId id);
std::string_view resolveNameFromId(ObjectId id) const; 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 */ std::list<std::shared_ptr<Voice>> m_childVoices; /**< Child voices for PLAYMACRO usage */
uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */ uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */
enum class SampleFormat : uint8_t const AudioGroupSampleDirectory::Entry* m_curSample = nullptr; /**< Current sample entry playing */
{
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 unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */ const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */
SampleFormat m_curFormat; /**< Current sample format playing */ SampleFormat m_curFormat; /**< Current sample format playing */
uint32_t m_curSamplePos = 0; /**< Current sample position */ uint32_t m_curSamplePos = 0; /**< Current sample position */
@ -236,7 +227,7 @@ public:
void message(int32_t val); void message(int32_t val);
/** Start playing specified sample from within group, optionally by sample offset */ /** 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 */ /** Stop playing current sample */
void stopSample(); void stopSample();

View File

@ -4,34 +4,21 @@
namespace amuse namespace amuse
{ {
AudioGroup::AudioGroup(const AudioGroupData& data, GCNDataTag) AudioGroup::AudioGroup(const AudioGroupData& data)
: m_proj(AudioGroupProject::CreateAudioGroupProject(data)) : m_proj(AudioGroupProject::CreateAudioGroupProject(data))
, m_pool(AudioGroupPool::CreateAudioGroupPool(data)) , m_pool(AudioGroupPool::CreateAudioGroupPool(data))
, m_sdir(AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(data)) , m_sdir(AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(data))
, m_samp(data.getSamp()) , m_samp(data.getSamp())
, m_fmt(DataFormat::GCN) {}
{
}
AudioGroup::AudioGroup(const AudioGroupData& data, bool absOffs, N64DataTag) AudioGroup::AudioGroup(SystemStringView groupPath)
: m_proj(AudioGroupProject::CreateAudioGroupProject(data)) : m_proj(AudioGroupProject::CreateAudioGroupProject(groupPath))
, m_pool(AudioGroupPool::CreateAudioGroupPool(data)) , m_pool(AudioGroupPool::CreateAudioGroupPool(groupPath))
, m_sdir(AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(data)) , m_sdir(AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(groupPath))
, m_samp(data.getSamp()) , m_groupPath(groupPath)
, m_fmt(DataFormat::N64) {}
{
}
AudioGroup::AudioGroup(const AudioGroupData& data, bool absOffs, PCDataTag) const AudioGroupSampleDirectory::Entry* AudioGroup::getSample(SampleId sfxId) const
: m_proj(AudioGroupProject::CreateAudioGroupProject(data))
, m_pool(AudioGroupPool::CreateAudioGroupPool(data))
, m_sdir(AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(data))
, m_samp(data.getSamp())
, m_fmt(DataFormat::PC)
{
}
const Sample* AudioGroup::getSample(int sfxId) const
{ {
auto search = m_sdir.m_entries.find(sfxId); auto search = m_sdir.m_entries.find(sfxId);
if (search == m_sdir.m_entries.cend()) if (search == m_sdir.m_entries.cend())
@ -39,5 +26,20 @@ const Sample* AudioGroup::getSample(int sfxId) const
return &search->second; return &search->second;
} }
const unsigned char* AudioGroup::getSampleData(uint32_t offset) const { return m_samp + offset; } const unsigned char* AudioGroup::getSampleData(SampleId sfxId, const AudioGroupSampleDirectory::Entry* sample) const
{
if (sample->m_looseData)
{
#if _WIN32
SystemString basePath = m_groupPath + _S('/') +
athena::utility::utf8ToWide(SampleId::CurNameDB->resolveNameFromId(sfxId));
#else
SystemString basePath = m_groupPath + _S('/') +
SampleId::CurNameDB->resolveNameFromId(sfxId).data();
#endif
const_cast<AudioGroupSampleDirectory::Entry*>(sample)->loadLooseData(basePath);
return sample->m_looseData.get();
}
return m_samp + sample->m_sampleOff;
}
} }

View File

@ -2,8 +2,9 @@
#include "amuse/Common.hpp" #include "amuse/Common.hpp"
#include "amuse/Entity.hpp" #include "amuse/Entity.hpp"
#include "amuse/AudioGroupData.hpp" #include "amuse/AudioGroupData.hpp"
#include "athena/MemoryReader.hpp"
#include "logvisor/logvisor.hpp" #include "logvisor/logvisor.hpp"
#include "athena/FileWriter.hpp"
#include "athena/FileReader.hpp"
using namespace std::literals; using namespace std::literals;
@ -51,11 +52,11 @@ AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r)
auto& ptr = ret.m_tables[objHead.objectId.id]; auto& ptr = ret.m_tables[objHead.objectId.id];
switch (objHead.size) switch (objHead.size)
{ {
case 8: case 0x10:
ptr = std::make_unique<ADSR>(); ptr = std::make_unique<ADSR>();
static_cast<ADSR&>(*ptr).read(r); static_cast<ADSR&>(*ptr).read(r);
break; break;
case 0x14: case 0x1c:
ptr = std::make_unique<ADSRDLS>(); ptr = std::make_unique<ADSRDLS>();
static_cast<ADSRDLS&>(*ptr).read(r); static_cast<ADSRDLS&>(*ptr).read(r);
break; break;
@ -123,11 +124,147 @@ AudioGroupPool AudioGroupPool::CreateAudioGroupPool(const AudioGroupData& data)
} }
} }
template <class Tp> AudioGroupPool AudioGroupPool::CreateAudioGroupPool(SystemStringView groupPath)
static std::unique_ptr<SoundMacro::ICmd> MakeCmd(athena::io::MemoryReader& r)
{ {
std::unique_ptr<SoundMacro::ICmd> ret = std::make_unique<Tp>(); AudioGroupPool ret;
static_cast<Tp&>(*ret).read(r); SystemString poolPath(groupPath);
poolPath += _S("/!pool.yaml");
athena::io::FileReader fi(poolPath);
if (!fi.hasError())
{
athena::io::YAMLDocReader r;
if (r.parse(&fi) && r.ValidateClassType("amuse::Pool"))
{
if (auto __r = r.enterSubRecord("soundMacros"))
{
ret.m_soundMacros.reserve(r.getCurNode()->m_mapChildren.size());
for (const auto& sm : r.getCurNode()->m_mapChildren)
{
ObjectId macroId = SoundMacroId::CurNameDB->generateId(NameDB::Type::SoundMacro);
SoundMacroId::CurNameDB->registerPair(sm.first, macroId);
}
}
if (auto __r = r.enterSubRecord("tables"))
{
ret.m_tables.reserve(r.getCurNode()->m_mapChildren.size());
for (const auto& t : r.getCurNode()->m_mapChildren)
{
if (auto __v = r.enterSubRecord(t.first.c_str()))
{
ObjectId tableId = TableId::CurNameDB->generateId(NameDB::Type::Table);
TableId::CurNameDB->registerPair(t.first, tableId);
}
}
}
if (auto __r = r.enterSubRecord("keymaps"))
{
ret.m_keymaps.reserve(r.getCurNode()->m_mapChildren.size());
for (const auto& k : r.getCurNode()->m_mapChildren)
if (auto __v = r.enterSubRecord(k.first.c_str()))
{
ObjectId keymapId = KeymapId::CurNameDB->generateId(NameDB::Type::Keymap);
KeymapId::CurNameDB->registerPair(k.first, keymapId);
}
}
if (auto __r = r.enterSubRecord("layers"))
{
for (const auto& l : r.getCurNode()->m_mapChildren)
{
size_t mappingCount;
if (auto __v = r.enterSubVector(l.first.c_str(), mappingCount))
{
ObjectId layersId = LayersId::CurNameDB->generateId(NameDB::Type::Layer);
LayersId::CurNameDB->registerPair(l.first, layersId);
}
}
}
if (auto __r = r.enterSubRecord("soundMacros"))
{
ret.m_soundMacros.reserve(r.getCurNode()->m_mapChildren.size());
for (const auto& sm : r.getCurNode()->m_mapChildren)
{
SoundMacro& smOut = ret.m_soundMacros[SoundMacroId::CurNameDB->resolveIdFromName(sm.first)];
size_t cmdCount;
if (auto __v = r.enterSubVector(sm.first.c_str(), cmdCount))
{
smOut.m_cmds.reserve(cmdCount);
for (int c = 0; c < cmdCount; ++c)
if (auto __r2 = r.enterSubRecord(nullptr))
smOut.m_cmds.push_back(SoundMacro::MakeCmd(r));
}
}
}
if (auto __r = r.enterSubRecord("tables"))
{
ret.m_tables.reserve(r.getCurNode()->m_mapChildren.size());
for (const auto& t : r.getCurNode()->m_mapChildren)
{
if (auto __v = r.enterSubRecord(t.first.c_str()))
{
std::unique_ptr<ITable>& tableOut = ret.m_tables[TableId::CurNameDB->resolveIdFromName(t.first)];
if (auto __att = r.enterSubRecord("attack"))
{
__att.leave();
if (auto __vta = r.enterSubRecord("velToAttack"))
{
__vta.leave();
tableOut = std::make_unique<ADSRDLS>();
static_cast<ADSRDLS&>(*tableOut).read(r);
}
else
{
tableOut = std::make_unique<ADSR>();
static_cast<ADSR&>(*tableOut).read(r);
}
}
else if (auto __dat = r.enterSubRecord("data"))
{
__dat.leave();
tableOut = std::make_unique<Curve>();
static_cast<Curve&>(*tableOut).read(r);
}
}
}
}
if (auto __r = r.enterSubRecord("keymaps"))
{
ret.m_keymaps.reserve(r.getCurNode()->m_mapChildren.size());
for (const auto& k : r.getCurNode()->m_mapChildren)
if (auto __v = r.enterSubRecord(k.first.c_str()))
ret.m_keymaps[KeymapId::CurNameDB->resolveIdFromName(k.first)].read(r);
}
if (auto __r = r.enterSubRecord("layers"))
{
ret.m_layers.reserve(r.getCurNode()->m_mapChildren.size());
for (const auto& l : r.getCurNode()->m_mapChildren)
{
size_t mappingCount;
if (auto __v = r.enterSubVector(l.first.c_str(), mappingCount))
{
std::vector<LayerMapping>& layOut = ret.m_layers[LayersId::CurNameDB->resolveIdFromName(l.first)];
layOut.reserve(mappingCount);
for (int lm = 0; lm < mappingCount; ++lm)
{
if (auto __r2 = r.enterSubRecord(nullptr))
{
layOut.emplace_back();
layOut.back().read(r);
}
}
}
}
}
}
}
return ret; return ret;
} }
@ -153,167 +290,7 @@ void SoundMacro::readCmds(athena::io::IStreamReader& r, uint32_t size)
uint32_t data[2]; uint32_t data[2];
athena::io::Read<athena::io::PropType::None>::Do<decltype(data), DNAE>({}, data, r); athena::io::Read<athena::io::PropType::None>::Do<decltype(data), DNAE>({}, data, r);
athena::io::MemoryReader r(data, 8); athena::io::MemoryReader r(data, 8);
std::unique_ptr<ICmd> cmd; m_cmds.push_back(MakeCmd(r));
switch (CmdOp(r.readUByte()))
{
case CmdOp::End:
cmd = MakeCmd<CmdEnd>(r); break;
case CmdOp::Stop:
cmd = MakeCmd<CmdStop>(r); break;
case CmdOp::SplitKey:
cmd = MakeCmd<CmdSplitKey>(r); break;
case CmdOp::SplitVel:
cmd = MakeCmd<CmdSplitVel>(r); break;
case CmdOp::WaitTicks:
cmd = MakeCmd<CmdWaitTicks>(r); break;
case CmdOp::Loop:
cmd = MakeCmd<CmdLoop>(r); break;
case CmdOp::Goto:
cmd = MakeCmd<CmdGoto>(r); break;
case CmdOp::WaitMs:
cmd = MakeCmd<CmdWaitMs>(r); break;
case CmdOp::PlayMacro:
cmd = MakeCmd<CmdPlayMacro>(r); break;
case CmdOp::SendKeyOff:
cmd = MakeCmd<CmdSendKeyOff>(r); break;
case CmdOp::SplitMod:
cmd = MakeCmd<CmdSplitMod>(r); break;
case CmdOp::PianoPan:
cmd = MakeCmd<CmdPianoPan>(r); break;
case CmdOp::SetAdsr:
cmd = MakeCmd<CmdSetAdsr>(r); break;
case CmdOp::ScaleVolume:
cmd = MakeCmd<CmdScaleVolume>(r); break;
case CmdOp::Panning:
cmd = MakeCmd<CmdPanning>(r); break;
case CmdOp::Envelope:
cmd = MakeCmd<CmdEnvelope>(r); break;
case CmdOp::StartSample:
cmd = MakeCmd<CmdStartSample>(r); break;
case CmdOp::StopSample:
cmd = MakeCmd<CmdStopSample>(r); break;
case CmdOp::KeyOff:
cmd = MakeCmd<CmdKeyOff>(r); break;
case CmdOp::SplitRnd:
cmd = MakeCmd<CmdSplitRnd>(r); break;
case CmdOp::FadeIn:
cmd = MakeCmd<CmdFadeIn>(r); break;
case CmdOp::Spanning:
cmd = MakeCmd<CmdSpanning>(r); break;
case CmdOp::SetAdsrCtrl:
cmd = MakeCmd<CmdSetAdsrCtrl>(r); break;
case CmdOp::RndNote:
cmd = MakeCmd<CmdRndNote>(r); break;
case CmdOp::AddNote:
cmd = MakeCmd<CmdAddNote>(r); break;
case CmdOp::SetNote:
cmd = MakeCmd<CmdSetNote>(r); break;
case CmdOp::LastNote:
cmd = MakeCmd<CmdLastNote>(r); break;
case CmdOp::Portamento:
cmd = MakeCmd<CmdPortamento>(r); break;
case CmdOp::Vibrato:
cmd = MakeCmd<CmdVibrato>(r); break;
case CmdOp::PitchSweep1:
cmd = MakeCmd<CmdPitchSweep1>(r); break;
case CmdOp::PitchSweep2:
cmd = MakeCmd<CmdPitchSweep2>(r); break;
case CmdOp::SetPitch:
cmd = MakeCmd<CmdSetPitch>(r); break;
case CmdOp::SetPitchAdsr:
cmd = MakeCmd<CmdSetPitchAdsr>(r); break;
case CmdOp::ScaleVolumeDLS:
cmd = MakeCmd<CmdScaleVolumeDLS>(r); break;
case CmdOp::Mod2Vibrange:
cmd = MakeCmd<CmdMod2Vibrange>(r); break;
case CmdOp::SetupTremolo:
cmd = MakeCmd<CmdSetupTremolo>(r); break;
case CmdOp::Return:
cmd = MakeCmd<CmdReturn>(r); break;
case CmdOp::GoSub:
cmd = MakeCmd<CmdGoSub>(r); break;
case CmdOp::TrapEvent:
cmd = MakeCmd<CmdTrapEvent>(r); break;
case CmdOp::UntrapEvent:
cmd = MakeCmd<CmdUntrapEvent>(r); break;
case CmdOp::SendMessage:
cmd = MakeCmd<CmdSendMessage>(r); break;
case CmdOp::GetMessage:
cmd = MakeCmd<CmdGetMessage>(r); break;
case CmdOp::GetVid:
cmd = MakeCmd<CmdGetVid>(r); break;
case CmdOp::AddAgeCount:
cmd = MakeCmd<CmdAddAgeCount>(r); break;
case CmdOp::SetAgeCount:
cmd = MakeCmd<CmdSetAgeCount>(r); break;
case CmdOp::SendFlag:
cmd = MakeCmd<CmdSendFlag>(r); break;
case CmdOp::PitchWheelR:
cmd = MakeCmd<CmdPitchWheelR>(r); break;
case CmdOp::SetPriority:
cmd = MakeCmd<CmdSetPriority>(r); break;
case CmdOp::AddPriority:
cmd = MakeCmd<CmdAddPriority>(r); break;
case CmdOp::AgeCntSpeed:
cmd = MakeCmd<CmdAgeCntSpeed>(r); break;
case CmdOp::AgeCntVel:
cmd = MakeCmd<CmdAgeCntVel>(r); break;
case CmdOp::VolSelect:
cmd = MakeCmd<CmdVolSelect>(r); break;
case CmdOp::PanSelect:
cmd = MakeCmd<CmdPanSelect>(r); break;
case CmdOp::PitchWheelSelect:
cmd = MakeCmd<CmdPitchWheelSelect>(r); break;
case CmdOp::ModWheelSelect:
cmd = MakeCmd<CmdModWheelSelect>(r); break;
case CmdOp::PedalSelect:
cmd = MakeCmd<CmdPedalSelect>(r); break;
case CmdOp::PortamentoSelect:
cmd = MakeCmd<CmdPortamentoSelect>(r); break;
case CmdOp::ReverbSelect:
cmd = MakeCmd<CmdReverbSelect>(r); break;
case CmdOp::SpanSelect:
cmd = MakeCmd<CmdSpanSelect>(r); break;
case CmdOp::DopplerSelect:
cmd = MakeCmd<CmdDopplerSelect>(r); break;
case CmdOp::TremoloSelect:
cmd = MakeCmd<CmdTremoloSelect>(r); break;
case CmdOp::PreASelect:
cmd = MakeCmd<CmdPreASelect>(r); break;
case CmdOp::PreBSelect:
cmd = MakeCmd<CmdPreBSelect>(r); break;
case CmdOp::PostBSelect:
cmd = MakeCmd<CmdPostBSelect>(r); break;
case CmdOp::AuxAFXSelect:
cmd = MakeCmd<CmdAuxAFXSelect>(r); break;
case CmdOp::AuxBFXSelect:
cmd = MakeCmd<CmdAuxBFXSelect>(r); break;
case CmdOp::SetupLFO:
cmd = MakeCmd<CmdSetupLFO>(r); break;
case CmdOp::ModeSelect:
cmd = MakeCmd<CmdModeSelect>(r); break;
case CmdOp::SetKeygroup:
cmd = MakeCmd<CmdSetKeygroup>(r); break;
case CmdOp::SRCmodeSelect:
cmd = MakeCmd<CmdSRCmodeSelect>(r); break;
case CmdOp::AddVars:
cmd = MakeCmd<CmdAddVars>(r); break;
case CmdOp::SubVars:
cmd = MakeCmd<CmdSubVars>(r); break;
case CmdOp::MulVars:
cmd = MakeCmd<CmdMulVars>(r); break;
case CmdOp::DivVars:
cmd = MakeCmd<CmdDivVars>(r); break;
case CmdOp::AddIVars:
cmd = MakeCmd<CmdAddIVars>(r); break;
case CmdOp::IfEqual:
cmd = MakeCmd<CmdIfEqual>(r); break;
case CmdOp::IfLess:
cmd = MakeCmd<CmdIfLess>(r); break;
default:
break;
}
m_cmds.push_back(std::move(cmd));
} }
} }
template void SoundMacro::readCmds<athena::Big>(athena::io::IStreamReader& r, uint32_t size); template void SoundMacro::readCmds<athena::Big>(athena::io::IStreamReader& r, uint32_t size);
@ -346,11 +323,213 @@ const std::vector<LayerMapping>* AudioGroupPool::layer(ObjectId id) const
const ADSR* AudioGroupPool::tableAsAdsr(ObjectId id) const const ADSR* AudioGroupPool::tableAsAdsr(ObjectId id) const
{ {
auto search = m_tables.find(id); auto search = m_tables.find(id);
if (search == m_tables.cend()) if (search == m_tables.cend() || search->second->Isa() != ITable::Type::ADSR)
return nullptr; return nullptr;
return static_cast<const ADSR*>(search->second.get()); return static_cast<const ADSR*>(search->second.get());
} }
const ADSRDLS* AudioGroupPool::tableAsAdsrDLS(ObjectId id) const
{
auto search = m_tables.find(id);
if (search == m_tables.cend() || search->second->Isa() != ITable::Type::ADSRDLS)
return nullptr;
return static_cast<const ADSRDLS*>(search->second.get());
}
const Curve* AudioGroupPool::tableAsCurves(ObjectId id) const
{
auto search = m_tables.find(id);
if (search == m_tables.cend() || search->second->Isa() != ITable::Type::Curve)
return nullptr;
return static_cast<const Curve*>(search->second.get());
}
template <class Tp, class R>
static std::unique_ptr<SoundMacro::ICmd> _MakeCmd(R& r)
{
std::unique_ptr<SoundMacro::ICmd> ret = std::make_unique<Tp>();
static_cast<Tp&>(*ret).read(r);
return ret;
}
static SoundMacro::CmdOp _ReadCmdOp(athena::io::MemoryReader& r)
{
return SoundMacro::CmdOp(r.readUByte());
}
static SoundMacro::CmdOp _ReadCmdOp(athena::io::YAMLDocReader& r)
{
return SoundMacro::CmdStrToOp(r.readString("cmdOp"));
}
template <class R>
std::unique_ptr<SoundMacro::ICmd> SoundMacro::MakeCmd(R& r)
{
std::unique_ptr<ICmd> cmd;
switch (_ReadCmdOp(r))
{
case CmdOp::End:
cmd = _MakeCmd<CmdEnd>(r); break;
case CmdOp::Stop:
cmd = _MakeCmd<CmdStop>(r); break;
case CmdOp::SplitKey:
cmd = _MakeCmd<CmdSplitKey>(r); break;
case CmdOp::SplitVel:
cmd = _MakeCmd<CmdSplitVel>(r); break;
case CmdOp::WaitTicks:
cmd = _MakeCmd<CmdWaitTicks>(r); break;
case CmdOp::Loop:
cmd = _MakeCmd<CmdLoop>(r); break;
case CmdOp::Goto:
cmd = _MakeCmd<CmdGoto>(r); break;
case CmdOp::WaitMs:
cmd = _MakeCmd<CmdWaitMs>(r); break;
case CmdOp::PlayMacro:
cmd = _MakeCmd<CmdPlayMacro>(r); break;
case CmdOp::SendKeyOff:
cmd = _MakeCmd<CmdSendKeyOff>(r); break;
case CmdOp::SplitMod:
cmd = _MakeCmd<CmdSplitMod>(r); break;
case CmdOp::PianoPan:
cmd = _MakeCmd<CmdPianoPan>(r); break;
case CmdOp::SetAdsr:
cmd = _MakeCmd<CmdSetAdsr>(r); break;
case CmdOp::ScaleVolume:
cmd = _MakeCmd<CmdScaleVolume>(r); break;
case CmdOp::Panning:
cmd = _MakeCmd<CmdPanning>(r); break;
case CmdOp::Envelope:
cmd = _MakeCmd<CmdEnvelope>(r); break;
case CmdOp::StartSample:
cmd = _MakeCmd<CmdStartSample>(r); break;
case CmdOp::StopSample:
cmd = _MakeCmd<CmdStopSample>(r); break;
case CmdOp::KeyOff:
cmd = _MakeCmd<CmdKeyOff>(r); break;
case CmdOp::SplitRnd:
cmd = _MakeCmd<CmdSplitRnd>(r); break;
case CmdOp::FadeIn:
cmd = _MakeCmd<CmdFadeIn>(r); break;
case CmdOp::Spanning:
cmd = _MakeCmd<CmdSpanning>(r); break;
case CmdOp::SetAdsrCtrl:
cmd = _MakeCmd<CmdSetAdsrCtrl>(r); break;
case CmdOp::RndNote:
cmd = _MakeCmd<CmdRndNote>(r); break;
case CmdOp::AddNote:
cmd = _MakeCmd<CmdAddNote>(r); break;
case CmdOp::SetNote:
cmd = _MakeCmd<CmdSetNote>(r); break;
case CmdOp::LastNote:
cmd = _MakeCmd<CmdLastNote>(r); break;
case CmdOp::Portamento:
cmd = _MakeCmd<CmdPortamento>(r); break;
case CmdOp::Vibrato:
cmd = _MakeCmd<CmdVibrato>(r); break;
case CmdOp::PitchSweep1:
cmd = _MakeCmd<CmdPitchSweep1>(r); break;
case CmdOp::PitchSweep2:
cmd = _MakeCmd<CmdPitchSweep2>(r); break;
case CmdOp::SetPitch:
cmd = _MakeCmd<CmdSetPitch>(r); break;
case CmdOp::SetPitchAdsr:
cmd = _MakeCmd<CmdSetPitchAdsr>(r); break;
case CmdOp::ScaleVolumeDLS:
cmd = _MakeCmd<CmdScaleVolumeDLS>(r); break;
case CmdOp::Mod2Vibrange:
cmd = _MakeCmd<CmdMod2Vibrange>(r); break;
case CmdOp::SetupTremolo:
cmd = _MakeCmd<CmdSetupTremolo>(r); break;
case CmdOp::Return:
cmd = _MakeCmd<CmdReturn>(r); break;
case CmdOp::GoSub:
cmd = _MakeCmd<CmdGoSub>(r); break;
case CmdOp::TrapEvent:
cmd = _MakeCmd<CmdTrapEvent>(r); break;
case CmdOp::UntrapEvent:
cmd = _MakeCmd<CmdUntrapEvent>(r); break;
case CmdOp::SendMessage:
cmd = _MakeCmd<CmdSendMessage>(r); break;
case CmdOp::GetMessage:
cmd = _MakeCmd<CmdGetMessage>(r); break;
case CmdOp::GetVid:
cmd = _MakeCmd<CmdGetVid>(r); break;
case CmdOp::AddAgeCount:
cmd = _MakeCmd<CmdAddAgeCount>(r); break;
case CmdOp::SetAgeCount:
cmd = _MakeCmd<CmdSetAgeCount>(r); break;
case CmdOp::SendFlag:
cmd = _MakeCmd<CmdSendFlag>(r); break;
case CmdOp::PitchWheelR:
cmd = _MakeCmd<CmdPitchWheelR>(r); break;
case CmdOp::SetPriority:
cmd = _MakeCmd<CmdSetPriority>(r); break;
case CmdOp::AddPriority:
cmd = _MakeCmd<CmdAddPriority>(r); break;
case CmdOp::AgeCntSpeed:
cmd = _MakeCmd<CmdAgeCntSpeed>(r); break;
case CmdOp::AgeCntVel:
cmd = _MakeCmd<CmdAgeCntVel>(r); break;
case CmdOp::VolSelect:
cmd = _MakeCmd<CmdVolSelect>(r); break;
case CmdOp::PanSelect:
cmd = _MakeCmd<CmdPanSelect>(r); break;
case CmdOp::PitchWheelSelect:
cmd = _MakeCmd<CmdPitchWheelSelect>(r); break;
case CmdOp::ModWheelSelect:
cmd = _MakeCmd<CmdModWheelSelect>(r); break;
case CmdOp::PedalSelect:
cmd = _MakeCmd<CmdPedalSelect>(r); break;
case CmdOp::PortamentoSelect:
cmd = _MakeCmd<CmdPortamentoSelect>(r); break;
case CmdOp::ReverbSelect:
cmd = _MakeCmd<CmdReverbSelect>(r); break;
case CmdOp::SpanSelect:
cmd = _MakeCmd<CmdSpanSelect>(r); break;
case CmdOp::DopplerSelect:
cmd = _MakeCmd<CmdDopplerSelect>(r); break;
case CmdOp::TremoloSelect:
cmd = _MakeCmd<CmdTremoloSelect>(r); break;
case CmdOp::PreASelect:
cmd = _MakeCmd<CmdPreASelect>(r); break;
case CmdOp::PreBSelect:
cmd = _MakeCmd<CmdPreBSelect>(r); break;
case CmdOp::PostBSelect:
cmd = _MakeCmd<CmdPostBSelect>(r); break;
case CmdOp::AuxAFXSelect:
cmd = _MakeCmd<CmdAuxAFXSelect>(r); break;
case CmdOp::AuxBFXSelect:
cmd = _MakeCmd<CmdAuxBFXSelect>(r); break;
case CmdOp::SetupLFO:
cmd = _MakeCmd<CmdSetupLFO>(r); break;
case CmdOp::ModeSelect:
cmd = _MakeCmd<CmdModeSelect>(r); break;
case CmdOp::SetKeygroup:
cmd = _MakeCmd<CmdSetKeygroup>(r); break;
case CmdOp::SRCmodeSelect:
cmd = _MakeCmd<CmdSRCmodeSelect>(r); break;
case CmdOp::AddVars:
cmd = _MakeCmd<CmdAddVars>(r); break;
case CmdOp::SubVars:
cmd = _MakeCmd<CmdSubVars>(r); break;
case CmdOp::MulVars:
cmd = _MakeCmd<CmdMulVars>(r); break;
case CmdOp::DivVars:
cmd = _MakeCmd<CmdDivVars>(r); break;
case CmdOp::AddIVars:
cmd = _MakeCmd<CmdAddIVars>(r); break;
case CmdOp::IfEqual:
cmd = _MakeCmd<CmdIfEqual>(r); break;
case CmdOp::IfLess:
cmd = _MakeCmd<CmdIfLess>(r); break;
default:
break;
}
return cmd;
}
template std::unique_ptr<SoundMacro::ICmd> SoundMacro::MakeCmd(athena::io::MemoryReader& r);
template std::unique_ptr<SoundMacro::ICmd> SoundMacro::MakeCmd(athena::io::YAMLDocReader& r);
std::string_view SoundMacro::CmdOpToStr(CmdOp op) std::string_view SoundMacro::CmdOpToStr(CmdOp op)
{ {
switch (op) switch (op)
@ -673,7 +852,7 @@ SoundMacro::CmdOp SoundMacro::CmdStrToOp(std::string_view op)
return CmdOp::Invalid; return CmdOp::Invalid;
} }
bool AudioGroupPool::toYAML(athena::io::IStreamWriter& writer) const bool AudioGroupPool::toYAML(SystemStringView groupPath) const
{ {
athena::io::YAMLDocWriter w("amuse::Pool"); athena::io::YAMLDocWriter w("amuse::Pool");
@ -750,7 +929,10 @@ bool AudioGroupPool::toYAML(athena::io::IStreamWriter& writer) const
} }
} }
return w.finish(&writer); SystemString poolPath(groupPath);
poolPath += _S("/!pool.yaml");
athena::io::FileWriter fo(poolPath);
return w.finish(&fo);
} }
template <> template <>
@ -785,7 +967,7 @@ void amuse::Curve::Enumerate<LittleDNA::WriteYaml>(athena::io::YAMLDocWriter& w)
const char* amuse::Curve::DNAType() const char* amuse::Curve::DNAType()
{ {
return "amuse::ADSR"; return "amuse::Curve";
} }
} }

View File

@ -1,6 +1,8 @@
#include "amuse/AudioGroupProject.hpp" #include "amuse/AudioGroupProject.hpp"
#include "amuse/AudioGroupData.hpp" #include "amuse/AudioGroupData.hpp"
#include "athena/MemoryReader.hpp" #include "athena/MemoryReader.hpp"
#include "athena/FileWriter.hpp"
#include "athena/FileReader.hpp"
namespace amuse namespace amuse
{ {
@ -208,6 +210,87 @@ AudioGroupProject AudioGroupProject::CreateAudioGroupProject(const AudioGroupDat
} }
} }
AudioGroupProject AudioGroupProject::CreateAudioGroupProject(SystemStringView groupPath)
{
AudioGroupProject ret;
SystemString projPath(groupPath);
projPath += _S("/!project.yaml");
athena::io::FileReader fi(projPath);
if (!fi.hasError())
{
athena::io::YAMLDocReader r;
if (r.parse(&fi) && r.ValidateClassType("amuse::Project"))
{
size_t songGroupCount;
if (auto __v = r.enterSubVector("songGroups", songGroupCount))
{
ret.m_songGroups.reserve(songGroupCount);
for (int g = 0; g < songGroupCount; ++g)
{
if (auto __r = r.enterSubRecord(nullptr))
{
SongGroupIndex& idx = ret.m_songGroups[g];
if (auto __v2 = r.enterSubRecord("normPages"))
{
idx.m_normPages.reserve(r.getCurNode()->m_mapChildren.size());
for (const auto& pg : r.getCurNode()->m_mapChildren)
if (auto __r2 = r.enterSubRecord(pg.first.c_str()))
idx.m_normPages[strtoul(pg.first.c_str(), nullptr, 0)].read(r);
}
if (auto __v2 = r.enterSubRecord("drumPages"))
{
idx.m_drumPages.reserve(r.getCurNode()->m_mapChildren.size());
for (const auto& pg : r.getCurNode()->m_mapChildren)
if (auto __r2 = r.enterSubRecord(pg.first.c_str()))
idx.m_drumPages[strtoul(pg.first.c_str(), nullptr, 0)].read(r);
}
if (auto __v2 = r.enterSubRecord("songs"))
{
idx.m_midiSetups.reserve(r.getCurNode()->m_mapChildren.size());
for (const auto& song : r.getCurNode()->m_mapChildren)
{
size_t chanCount;
if (auto __v3 = r.enterSubVector(song.first.c_str(), chanCount))
{
ObjectId songId = SongId::CurNameDB->generateId(NameDB::Type::Song);
SongId::CurNameDB->registerPair(song.first, songId);
std::array<SongGroupIndex::MIDISetup, 16>& setup = idx.m_midiSetups[songId];
for (int i = 0; i < 16 && i < chanCount; ++i)
if (auto __r2 = r.enterSubRecord(nullptr))
setup[i].read(r);
}
}
}
}
}
}
size_t sfxGroupCount;
if (auto __v = r.enterSubVector("sfxGroups", sfxGroupCount))
{
ret.m_sfxGroups.reserve(sfxGroupCount);
for (int g = 0; g < sfxGroupCount; ++g)
{
if (auto __r = r.enterSubRecord(nullptr))
{
SFXGroupIndex& idx = ret.m_sfxGroups[g];
for (const auto& sfx : r.getCurNode()->m_mapChildren)
if (auto __r2 = r.enterSubRecord(sfx.first.c_str()))
{
ObjectId sfxId = SFXId::CurNameDB->generateId(NameDB::Type::SFX);
SFXId::CurNameDB->registerPair(sfx.first, sfxId);
idx.m_sfxEntries[sfxId].read(r);
}
}
}
}
}
}
return ret;
}
template <athena::Endian DNAE> template <athena::Endian DNAE>
static void ReadRangedObjectIds(NameDB* db, athena::io::IStreamReader& r, NameDB::Type tp) static void ReadRangedObjectIds(NameDB* db, athena::io::IStreamReader& r, NameDB::Type tp)
{ {
@ -412,7 +495,7 @@ const SFXGroupIndex* AudioGroupProject::getSFXGroupIndex(int groupId) const
return nullptr; return nullptr;
} }
bool AudioGroupProject::toYAML(athena::io::IStreamWriter& writer) const bool AudioGroupProject::toYAML(SystemStringView groupPath) const
{ {
athena::io::YAMLDocWriter w("amuse::Project"); athena::io::YAMLDocWriter w("amuse::Project");
@ -498,7 +581,10 @@ bool AudioGroupProject::toYAML(athena::io::IStreamWriter& writer) const
} }
} }
return w.finish(&writer); SystemString projPath(groupPath);
projPath += _S("/!project.yaml");
athena::io::FileWriter fo(projPath);
return w.finish(&fo);
} }
} }

View File

@ -1,8 +1,13 @@
#include "amuse/AudioGroupSampleDirectory.hpp" #include "amuse/AudioGroupSampleDirectory.hpp"
#include "amuse/Common.hpp" #include "amuse/Common.hpp"
#include "amuse/AudioGroupData.hpp" #include "amuse/AudioGroupData.hpp"
#include "amuse/DSPCodec.hpp"
#include "amuse/N64MusyXCodec.hpp"
#include "amuse/DirectoryEnumerator.hpp"
#include "athena/MemoryReader.hpp"
#include "athena/FileWriter.hpp"
#include "athena/FileReader.hpp"
#include <cstring> #include <cstring>
#include <athena/MemoryReader.hpp>
namespace amuse namespace amuse
{ {
@ -39,17 +44,16 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
{ {
EntryDNA<athena::Big> ent; EntryDNA<athena::Big> ent;
ent.read(r); ent.read(r);
std::pair<Entry, ADPCMParms>& store = m_entries[ent.m_sfxId]; m_entries[ent.m_sfxId] = ent;
store.first = ent;
} }
for (auto& p : m_entries) for (auto& p : m_entries)
{ {
if (p.second.first.m_adpcmParmOffset) if (p.second.m_adpcmParmOffset)
{ {
r.seek(p.second.first.m_adpcmParmOffset, athena::Begin); r.seek(p.second.m_adpcmParmOffset, athena::Begin);
r.readUBytesToBuf(&p.second.second, sizeof(ADPCMParms::DSPParms)); r.readUBytesToBuf(&p.second.m_ADPCMParms, sizeof(ADPCMParms::DSPParms));
p.second.second.swapBigDSP(); p.second.m_ADPCMParms.swapBigDSP();
} }
} }
} }
@ -63,8 +67,7 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
{ {
MusyX1AbsSdirEntry<athena::Big> ent; MusyX1AbsSdirEntry<athena::Big> ent;
ent.read(r); ent.read(r);
std::pair<Entry, ADPCMParms>& store = m_entries[ent.m_sfxId]; m_entries[ent.m_sfxId] = ent;
store.first = ent;
} }
} }
else else
@ -73,15 +76,14 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
{ {
MusyX1SdirEntry<athena::Big> ent; MusyX1SdirEntry<athena::Big> ent;
ent.read(r); ent.read(r);
std::pair<Entry, ADPCMParms>& store = m_entries[ent.m_sfxId]; m_entries[ent.m_sfxId] = ent;
store.first = ent;
} }
} }
for (auto& p : m_entries) for (auto& p : m_entries)
{ {
memcpy(&p.second.second, sampData + p.second.first.m_sampleOff, sizeof(ADPCMParms::VADPCMParms)); memcpy(&p.second.m_ADPCMParms, sampData + p.second.m_sampleOff, sizeof(ADPCMParms::VADPCMParms));
p.second.second.swapBigVADPCM(); p.second.m_ADPCMParms.swapBigVADPCM();
} }
} }
@ -93,8 +95,9 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
{ {
MusyX1AbsSdirEntry<athena::Little> ent; MusyX1AbsSdirEntry<athena::Little> ent;
ent.read(r); ent.read(r);
std::pair<Entry, ADPCMParms>& store = m_entries[ent.m_sfxId]; Entry& store = m_entries[ent.m_sfxId];
store.first = ent; store = ent;
store.m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24;
} }
} }
else else
@ -103,8 +106,9 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
{ {
MusyX1SdirEntry<athena::Little> ent; MusyX1SdirEntry<athena::Little> ent;
ent.read(r); ent.read(r);
std::pair<Entry, ADPCMParms>& store = m_entries[ent.m_sfxId]; Entry& store = m_entries[ent.m_sfxId];
store.first = ent; store = ent;
store.m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24;
} }
} }
} }
@ -123,4 +127,344 @@ AudioGroupSampleDirectory AudioGroupSampleDirectory::CreateAudioGroupSampleDirec
return AudioGroupSampleDirectory(r, data.getAbsoluteProjOffsets(), PCDataTag{}); return AudioGroupSampleDirectory(r, data.getAbsoluteProjOffsets(), PCDataTag{});
} }
} }
static uint32_t DSPSampleToNibble(uint32_t sample)
{
uint32_t ret = sample / 14 * 16;
if (sample % 14)
ret += (sample % 14) + 2;
return ret;
}
static uint32_t DSPNibbleToSample(uint32_t nibble)
{
uint32_t ret = nibble / 16 * 14;
if (nibble % 16)
ret += (nibble % 16) - 2;
return ret;
}
void AudioGroupSampleDirectory::Entry::loadLooseData(SystemStringView basePath)
{
Sstat theStat;
SystemString filePath = SystemString(basePath) + _S(".dsp");
if (!Stat(filePath.c_str(), &theStat) && (!m_looseData || theStat.st_mtime > m_looseModTime))
{
athena::io::FileReader r(filePath);
if (!r.hasError())
{
DSPADPCMHeader header;
header.read(r);
m_pitch = header.m_pitch;
m_sampleRate = atUint16(header.x8_sample_rate);
m_numSamples = header.x0_num_samples;
if (header.xc_loop_flag)
{
m_loopStartSample = DSPNibbleToSample(header.x10_loop_start_nibble);
m_loopLengthSamples = DSPNibbleToSample(header.x14_loop_end_nibble) - m_loopStartSample;
}
m_ADPCMParms.dsp.m_ps = uint8_t(header.x3e_ps);
m_ADPCMParms.dsp.m_lps = uint8_t(header.x44_loop_ps);
m_ADPCMParms.dsp.m_hist1 = header.x40_hist1;
m_ADPCMParms.dsp.m_hist2 = header.x42_hist2;
for (int i = 0; i < 8; ++i)
for (int j = 0; j < 2; ++j)
m_ADPCMParms.dsp.m_coefs[i][j] = header.x1c_coef[i][j];
uint32_t dataLen = (header.x4_num_nibbles + 1) / 2;
m_looseData.reset(new uint8_t[dataLen]);
r.readUBytesToBuf(m_looseData.get(), dataLen);
m_looseModTime = theStat.st_mtime;
return;
}
}
if (!Stat(filePath.c_str(), &theStat) && (!m_looseData || theStat.st_mtime > m_looseModTime))
{
athena::io::FileReader r(filePath);
if (!r.hasError())
{
atUint32 riffMagic = r.readUint32Little();
if (riffMagic != SBIG('RIFF'))
return;
atUint32 wavChuckSize = r.readUint32Little();
atUint32 wavMagic = r.readUint32Little();
if (wavMagic != SBIG('WAVE'))
return;
while (r.position() < wavChuckSize + 8)
{
atUint32 chunkMagic = r.readUint32Little();
atUint32 chunkSize = r.readUint32Little();
atUint64 startPos = r.position();
if (chunkMagic == SBIG('fmt '))
{
WAVFormatChunk fmt;
fmt.read(r);
m_sampleRate = atUint16(fmt.sampleRate);
}
else if (chunkMagic == SBIG('smpl'))
{
WAVSampleChunk smpl;
smpl.read(r);
m_pitch = atUint8(smpl.midiNote);
if (smpl.numSampleLoops)
{
WAVSampleLoop loop;
loop.read(r);
m_loopStartSample = loop.start;
m_loopLengthSamples = loop.end - loop.start + 1;
}
}
else if (chunkMagic == SBIG('data'))
{
m_numSamples = ((chunkSize / 2) & 0xffffff) | (atUint32(SampleFormat::PCM_PC) << 24);
m_looseData.reset(new uint8_t[chunkSize]);
r.readUBytesToBuf(m_looseData.get(), chunkSize);
}
r.seek(startPos + chunkSize, athena::Begin);
}
m_looseModTime = theStat.st_mtime;
return;
}
}
}
AudioGroupSampleDirectory AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(SystemStringView groupPath)
{
AudioGroupSampleDirectory ret;
DirectoryEnumerator de(groupPath, DirectoryEnumerator::Mode::FilesSorted);
for (const DirectoryEnumerator::Entry& ent : de)
{
if (ent.m_name.size() < 4)
continue;
SystemString baseName;
if (!CompareCaseInsensitive(ent.m_name.data() + ent.m_name.size() - 4, _S(".dsp")) ||
!CompareCaseInsensitive(ent.m_name.data() + ent.m_name.size() - 4, _S(".wav")))
baseName = SystemString(ent.m_name.begin(), ent.m_name.begin() + ent.m_name.size() - 4);
else
continue;
ObjectId sampleId = SampleId::CurNameDB->generateId(NameDB::Type::Sample);
#ifdef _WIN32
SampleId::CurNameDB->registerPair(athena::utility::wideToUtf8(baseName), sampleId);
#else
SampleId::CurNameDB->registerPair(baseName, sampleId);
#endif
Entry& entry = ret.m_entries[sampleId];
SystemString basePath = SystemString(ent.m_path.begin(), ent.m_path.begin() + ent.m_path.size() - 4);
entry.loadLooseData(basePath);
}
return ret;
}
void AudioGroupSampleDirectory::_extractWAV(SampleId id, const Entry& ent,
amuse::SystemStringView destDir, const unsigned char* samp)
{
amuse::SystemString path(destDir);
path += _S("/");
#ifdef _WIN32
path += athena::utility::utf8ToWide(SampleId::CurNameDB->resolveNameFromId(id));
#else
path += SampleId::CurNameDB->resolveNameFromId(id);
#endif
path += _S(".wav");
athena::io::FileWriter w(path);
SampleFormat fmt = SampleFormat(ent.m_numSamples >> 24);
uint32_t numSamples = ent.m_numSamples & 0xffffff;
if (ent.m_loopLengthSamples)
{
WAVHeaderLoop header;
header.fmtChunk.sampleRate = ent.m_sampleRate;
header.fmtChunk.byteRate = ent.m_sampleRate * 2u;
header.smplChunk.smplPeriod = 1000000000u / ent.m_sampleRate;
header.smplChunk.midiNote = ent.m_pitch;
header.smplChunk.numSampleLoops = 1;
header.sampleLoop.start = ent.m_loopStartSample;
header.sampleLoop.end = ent.m_loopStartSample + ent.m_loopLengthSamples - 1;
header.dataChunkSize = numSamples * 2u;
header.wavChuckSize = 36 + 68 + header.dataChunkSize;
header.write(w);
}
else
{
WAVHeader header;
header.fmtChunk.sampleRate = ent.m_sampleRate;
header.fmtChunk.byteRate = ent.m_sampleRate * 2u;
header.smplChunk.smplPeriod = 1000000000u / ent.m_sampleRate;
header.smplChunk.midiNote = ent.m_pitch;
header.dataChunkSize = numSamples * 2u;
header.wavChuckSize = 36 + 44 + header.dataChunkSize;
header.write(w);
}
atUint64 dataLen;
if (fmt == SampleFormat::DSP || fmt == SampleFormat::DSP_DRUM)
{
uint32_t remSamples = numSamples;
uint32_t numFrames = (remSamples + 13) / 14;
const unsigned char* cur = samp + ent.m_sampleOff;
int16_t prev1 = ent.m_ADPCMParms.dsp.m_hist1;
int16_t prev2 = ent.m_ADPCMParms.dsp.m_hist2;
for (uint32_t i = 0; i < numFrames; ++i)
{
int16_t decomp[14] = {};
unsigned thisSamples = std::min(remSamples, 14u);
DSPDecompressFrame(decomp, cur, ent.m_ADPCMParms.dsp.m_coefs, &prev1, &prev2, thisSamples);
remSamples -= thisSamples;
cur += 8;
w.writeBytes(decomp, thisSamples * 2);
}
dataLen = (DSPSampleToNibble(numSamples) + 1) / 2;
}
else if (fmt == SampleFormat::N64)
{
uint32_t remSamples = numSamples;
uint32_t numFrames = (remSamples + 31) / 32;
const unsigned char* cur = samp + ent.m_sampleOff + sizeof(ADPCMParms::VADPCMParms);
for (uint32_t i = 0; i < numFrames; ++i)
{
int16_t decomp[32] = {};
unsigned thisSamples = std::min(remSamples, 32u);
N64MusyXDecompressFrame(decomp, cur, ent.m_ADPCMParms.vadpcm.m_coefs, thisSamples);
remSamples -= thisSamples;
cur += 16;
w.writeBytes(decomp, thisSamples * 2);
}
dataLen = sizeof(ADPCMParms::VADPCMParms) + (numSamples + 31) / 32 * 16;
}
else if (fmt == SampleFormat::PCM)
{
dataLen = numSamples * 2;
const int16_t* cur = reinterpret_cast<const int16_t*>(samp + ent.m_sampleOff);
for (int i = 0; i < numSamples; ++i)
w.writeInt16Big(cur[i]);
}
else // PCM_PC
{
dataLen = numSamples * 2;
w.writeBytes(samp + ent.m_sampleOff, dataLen);
}
std::unique_ptr<uint8_t[]>& ld = const_cast<std::unique_ptr<uint8_t[]>&>(ent.m_looseData);
if (!ld)
{
Sstat theStat;
Stat(path.c_str(), &theStat);
const_cast<time_t&>(ent.m_looseModTime) = theStat.st_mtime;
ld.reset(new uint8_t[dataLen]);
memcpy(ld.get(), samp + ent.m_sampleOff, dataLen);
}
}
void AudioGroupSampleDirectory::extractWAV(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const
{
auto search = m_entries.find(id);
if (search == m_entries.cend())
return;
_extractWAV(id, search->second, destDir, samp);
}
void AudioGroupSampleDirectory::extractAllWAV(amuse::SystemStringView destDir, const unsigned char* samp) const
{
for (const auto& ent : m_entries)
_extractWAV(ent.first, ent.second, destDir, samp);
}
void AudioGroupSampleDirectory::_extractCompressed(SampleId id, const Entry& ent,
amuse::SystemStringView destDir, const unsigned char* samp)
{
SampleFormat fmt = SampleFormat(ent.m_numSamples >> 24);
if (fmt == SampleFormat::PCM || fmt == SampleFormat::PCM_PC)
{
_extractWAV(id, ent, destDir, samp);
return;
}
amuse::SystemString path(destDir);
path += _S("/");
#ifdef _WIN32
path += athena::utility::utf8ToWide(SampleId::CurNameDB->resolveNameFromId(id));
#else
path += SampleId::CurNameDB->resolveNameFromId(id);
#endif
uint32_t numSamples = ent.m_numSamples & 0xffffff;
atUint64 dataLen;
if (fmt == SampleFormat::DSP || fmt == SampleFormat::DSP_DRUM)
{
DSPADPCMHeader header;
header.x0_num_samples = numSamples;
header.x4_num_nibbles = DSPSampleToNibble(numSamples);
header.x8_sample_rate = ent.m_sampleRate;
header.xc_loop_flag = atUint16(ent.m_loopLengthSamples != 0);
if (header.xc_loop_flag)
{
header.x10_loop_start_nibble = DSPSampleToNibble(ent.m_loopStartSample);
header.x14_loop_end_nibble = DSPSampleToNibble(ent.m_loopStartSample + ent.m_loopLengthSamples);
}
for (int i = 0; i < 8; ++i)
for (int j = 0; j < 2; ++j)
header.x1c_coef[i][j] = ent.m_ADPCMParms.dsp.m_coefs[i][j];
header.x3e_ps = ent.m_ADPCMParms.dsp.m_ps;
header.x40_hist1 = ent.m_ADPCMParms.dsp.m_hist1;
header.x42_hist2 = ent.m_ADPCMParms.dsp.m_hist2;
header.x44_loop_ps = ent.m_ADPCMParms.dsp.m_lps;
header.m_pitch = ent.m_pitch;
path += _S(".dsp");
athena::io::FileWriter w(path);
header.write(w);
dataLen = (header.x4_num_nibbles + 1) / 2;
w.writeUBytes(samp + ent.m_sampleOff, dataLen);
}
else if (fmt == SampleFormat::N64)
{
path += _S(".vadpcm");
athena::io::FileWriter w(path);
dataLen = sizeof(ADPCMParms::VADPCMParms) + (numSamples + 31) / 32 * 16;
w.writeUBytes(samp + ent.m_sampleOff, dataLen);
}
else
{
return;
}
std::unique_ptr<uint8_t[]>& ld = const_cast<std::unique_ptr<uint8_t[]>&>(ent.m_looseData);
if (!ld)
{
Sstat theStat;
Stat(path.c_str(), &theStat);
const_cast<time_t&>(ent.m_looseModTime) = theStat.st_mtime;
ld.reset(new uint8_t[dataLen]);
memcpy(ld.get(), samp + ent.m_sampleOff, dataLen);
}
}
void AudioGroupSampleDirectory::extractCompressed(SampleId id, amuse::SystemStringView destDir,
const unsigned char* samp) const
{
auto search = m_entries.find(id);
if (search == m_entries.cend())
return;
_extractCompressed(id, search->second, destDir, samp);
}
void AudioGroupSampleDirectory::extractAllCompressed(amuse::SystemStringView destDir,
const unsigned char* samp) const
{
for (const auto& ent : m_entries)
_extractCompressed(ent.first, ent.second, destDir, samp);
}
} }

View File

@ -193,7 +193,7 @@ const char* PageObjectIdDNA<DNAE>::DNAType()
template struct PageObjectIdDNA<athena::Big>; template struct PageObjectIdDNA<athena::Big>;
template struct PageObjectIdDNA<athena::Little>; template struct PageObjectIdDNA<athena::Little>;
ObjectId NameDB::generateId(Type tp) ObjectId NameDB::generateId(Type tp) const
{ {
uint16_t maxMatch = uint16_t(tp == Type::Layer ? 0x8000 : 0); uint16_t maxMatch = uint16_t(tp == Type::Layer ? 0x8000 : 0);
for (const auto& p : m_idToString) for (const auto& p : m_idToString)

View File

@ -213,24 +213,7 @@ AudioGroup* Engine::_addAudioGroup(const AudioGroupData& data, std::unique_ptr<A
const AudioGroup* Engine::addAudioGroup(const AudioGroupData& data) const AudioGroup* Engine::addAudioGroup(const AudioGroupData& data)
{ {
removeAudioGroup(data); removeAudioGroup(data);
return _addAudioGroup(data, std::make_unique<AudioGroup>(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, data.m_absOffs, N64DataTag{});
break;
case DataFormat::PC:
grp = std::make_unique<AudioGroup>(data, data.m_absOffs, PCDataTag{});
break;
}
if (!grp)
return nullptr;
return _addAudioGroup(data, std::move(grp));
} }
/** Remove audio group from engine */ /** Remove audio group from engine */

View File

@ -64,14 +64,14 @@ bool Voice::_checkSamplePos(bool& looped)
if (m_curSamplePos >= m_lastSamplePos) if (m_curSamplePos >= m_lastSamplePos)
{ {
if (m_curSample->first.m_loopLengthSamples) if (m_curSample->m_loopLengthSamples)
{ {
/* Turn over looped sample */ /* Turn over looped sample */
m_curSamplePos = m_curSample->first.m_loopStartSample; m_curSamplePos = m_curSample->m_loopStartSample;
if (m_curFormat == SampleFormat::DSP) if (m_curFormat == SampleFormat::DSP)
{ {
m_prev1 = m_curSample->second.dsp.m_hist1; m_prev1 = m_curSample->m_ADPCMParms.dsp.m_hist1;
m_prev2 = m_curSample->second.dsp.m_hist2; m_prev2 = m_curSample->m_ADPCMParms.dsp.m_hist2;
} }
looped = true; looped = true;
} }
@ -115,9 +115,9 @@ void Voice::_doKeyOff()
void Voice::_setTotalPitch(int32_t cents, bool slew) void Voice::_setTotalPitch(int32_t cents, bool slew)
{ {
// fprintf(stderr, "PITCH %d %d \n", cents, slew); // fprintf(stderr, "PITCH %d %d \n", cents, slew);
int32_t interval = cents - m_curSample->first.m_pitch * 100; int32_t interval = cents - m_curSample->m_pitch * 100;
double ratio = std::exp2(interval / 1200.0) * m_dopplerRatio; double ratio = std::exp2(interval / 1200.0) * m_dopplerRatio;
m_sampleRate = m_curSample->first.m_sampleRate * ratio; m_sampleRate = m_curSample->m_sampleRate * ratio;
m_backendVoice->setPitchRatio(ratio, slew); m_backendVoice->setPitchRatio(ratio, slew);
} }
@ -346,9 +346,10 @@ uint32_t Voice::_GetBlockSampleCount(SampleFormat fmt)
{ {
default: default:
return 1; return 1;
case Voice::SampleFormat::DSP: case SampleFormat::DSP:
case SampleFormat::DSP_DRUM:
return 14; return 14;
case Voice::SampleFormat::N64: case SampleFormat::N64:
return 64; return 64;
} }
} }
@ -514,14 +515,15 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
case SampleFormat::DSP: case SampleFormat::DSP:
{ {
decSamples = decSamples =
DSPDecompressFrameRanged(data, m_curSampleData + 8 * block, m_curSample->second.dsp.m_coefs, DSPDecompressFrameRanged(data, m_curSampleData + 8 * block,
m_curSample->m_ADPCMParms.dsp.m_coefs,
&m_prev1, &m_prev2, rem, remCount); &m_prev1, &m_prev2, rem, remCount);
break; break;
} }
case SampleFormat::N64: case SampleFormat::N64:
{ {
decSamples = N64MusyXDecompressFrameRanged(data, m_curSampleData + 256 + 40 * block, decSamples = N64MusyXDecompressFrameRanged(data, m_curSampleData + 256 + 40 * block,
m_curSample->second.vadpcm.m_coefs, rem, remCount); m_curSample->m_ADPCMParms.vadpcm.m_coefs, rem, remCount);
break; break;
} }
case SampleFormat::PCM: case SampleFormat::PCM:
@ -576,14 +578,14 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
{ {
case SampleFormat::DSP: case SampleFormat::DSP:
{ {
decSamples = DSPDecompressFrame(data, m_curSampleData + 8 * block, m_curSample->second.dsp.m_coefs, decSamples = DSPDecompressFrame(data, m_curSampleData + 8 * block,
&m_prev1, &m_prev2, remCount); m_curSample->m_ADPCMParms.dsp.m_coefs, &m_prev1, &m_prev2, remCount);
break; break;
} }
case SampleFormat::N64: case SampleFormat::N64:
{ {
decSamples = N64MusyXDecompressFrame(data, m_curSampleData + 256 + 40 * block, decSamples = N64MusyXDecompressFrame(data, m_curSampleData + 256 + 40 * block,
m_curSample->second.vadpcm.m_coefs, remCount); m_curSample->m_ADPCMParms.vadpcm.m_coefs, remCount);
break; break;
} }
case SampleFormat::PCM: case SampleFormat::PCM:
@ -877,7 +879,7 @@ void Voice::keyOff()
loadMacroObject(m_keyoffTrap.macroId, m_keyoffTrap.macroStep, m_state.m_ticksPerSec, m_state.m_initKey, loadMacroObject(m_keyoffTrap.macroId, m_keyoffTrap.macroStep, m_state.m_ticksPerSec, m_state.m_initKey,
m_state.m_initVel, m_state.m_initMod); m_state.m_initVel, m_state.m_initMod);
} }
else if (!m_curSample || m_curSample->first.m_loopLengthSamples) else if (!m_curSample || m_curSample->m_loopLengthSamples)
_macroKeyOff(); _macroKeyOff();
for (const std::shared_ptr<Voice>& vox : m_childVoices) for (const std::shared_ptr<Voice>& vox : m_childVoices)
@ -901,7 +903,7 @@ void Voice::message(int32_t val)
} }
} }
void Voice::startSample(int16_t sampId, int32_t offset) void Voice::startSample(SampleId sampId, int32_t offset)
{ {
if (m_destroyed) if (m_destroyed)
return; return;
@ -909,41 +911,38 @@ void Voice::startSample(int16_t sampId, int32_t offset)
m_curSample = m_audioGroup.getSample(sampId); m_curSample = m_audioGroup.getSample(sampId);
if (m_curSample) if (m_curSample)
{ {
m_sampleRate = m_curSample->first.m_sampleRate; m_curSampleData = m_audioGroup.getSampleData(sampId, m_curSample);
m_curPitch = m_curSample->first.m_pitch;
m_sampleRate = m_curSample->m_sampleRate;
m_curPitch = m_curSample->m_pitch;
m_pitchDirty = true; m_pitchDirty = true;
_setPitchWheel(m_curPitchWheel); _setPitchWheel(m_curPitchWheel);
m_backendVoice->resetSampleRate(m_curSample->first.m_sampleRate); m_backendVoice->resetSampleRate(m_curSample->m_sampleRate);
m_needsSlew = false; m_needsSlew = false;
int32_t numSamples = m_curSample->first.m_numSamples & 0xffffff; int32_t numSamples = m_curSample->m_numSamples & 0xffffff;
if (offset) if (offset)
{ {
if (m_curSample->first.m_loopLengthSamples) if (m_curSample->m_loopLengthSamples)
{ {
if (offset > int32_t(m_curSample->first.m_loopStartSample)) if (offset > int32_t(m_curSample->m_loopStartSample))
offset = offset =
((offset - m_curSample->first.m_loopStartSample) % m_curSample->first.m_loopLengthSamples) + ((offset - m_curSample->m_loopStartSample) % m_curSample->m_loopLengthSamples) +
m_curSample->first.m_loopStartSample; m_curSample->m_loopStartSample;
} }
else else
offset = clamp(0, offset, numSamples); offset = clamp(0, offset, numSamples);
} }
m_curSamplePos = offset; m_curSamplePos = offset;
m_curSampleData = m_audioGroup.getSampleData(m_curSample->first.m_sampleOff);
m_prev1 = 0; m_prev1 = 0;
m_prev2 = 0; m_prev2 = 0;
if (m_audioGroup.getDataFormat() == DataFormat::PC) m_curFormat = SampleFormat(m_curSample->m_numSamples >> 24);
m_curFormat = SampleFormat::PCM_PC;
else
m_curFormat = SampleFormat(m_curSample->first.m_numSamples >> 24);
if (m_curFormat == SampleFormat::DSP_DRUM) if (m_curFormat == SampleFormat::DSP_DRUM)
m_curFormat = SampleFormat::DSP; m_curFormat = SampleFormat::DSP;
m_lastSamplePos = m_curSample->first.m_loopLengthSamples m_lastSamplePos = m_curSample->m_loopLengthSamples
? (m_curSample->first.m_loopStartSample + m_curSample->first.m_loopLengthSamples) ? (m_curSample->m_loopStartSample + m_curSample->m_loopLengthSamples)
: numSamples; : numSamples;
bool looped; bool looped;
@ -955,11 +954,11 @@ void Voice::startSample(int16_t sampId, int32_t offset)
uint32_t block = m_curSamplePos / 14; uint32_t block = m_curSamplePos / 14;
uint32_t rem = m_curSamplePos % 14; uint32_t rem = m_curSamplePos % 14;
for (uint32_t b = 0; b < block; ++b) for (uint32_t b = 0; b < block; ++b)
DSPDecompressFrameStateOnly(m_curSampleData + 8 * b, m_curSample->second.dsp.m_coefs, &m_prev1, DSPDecompressFrameStateOnly(m_curSampleData + 8 * b, m_curSample->m_ADPCMParms.dsp.m_coefs, &m_prev1,
&m_prev2, 14); &m_prev2, 14);
if (rem) if (rem)
DSPDecompressFrameStateOnly(m_curSampleData + 8 * block, m_curSample->second.dsp.m_coefs, &m_prev1, DSPDecompressFrameStateOnly(m_curSampleData + 8 * block, m_curSample->m_ADPCMParms.dsp.m_coefs, &m_prev1,
&m_prev2, rem); &m_prev2, rem);
} }
} }