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