mirror of https://github.com/AxioDL/amuse.git
Initial N64 SNG support; pitch-wheel fix
This commit is contained in:
parent
d2a8430746
commit
ee29fb4b1e
|
@ -782,9 +782,11 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
}
|
}
|
||||||
for (const auto& pair : allSongGroups)
|
for (const auto& pair : allSongGroups)
|
||||||
{
|
{
|
||||||
amuse::Printf(_S(" %d %s (SongGroup) %" PRISize " normal-pages, %" PRISize " drum-pages\n"),
|
amuse::Printf(_S(" %d %s (SongGroup) %" PRISize " normal-pages, %" PRISize " drum-pages, %" PRISize " MIDI-setups\n"),
|
||||||
pair.first, pair.second.first->first.c_str(),
|
pair.first, pair.second.first->first.c_str(),
|
||||||
pair.second.second->m_normPages.size(), pair.second.second->m_drumPages.size());
|
pair.second.second->m_normPages.size(),
|
||||||
|
pair.second.second->m_drumPages.size(),
|
||||||
|
pair.second.second->m_midiSetups.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
int userSel = 0;
|
int userSel = 0;
|
||||||
|
|
|
@ -26,29 +26,23 @@ class SongState
|
||||||
/** Song header */
|
/** Song header */
|
||||||
struct Header
|
struct Header
|
||||||
{
|
{
|
||||||
uint32_t m_version;
|
uint32_t m_trackIdxOff;
|
||||||
uint32_t m_chanIdxOff;
|
uint32_t m_regionIdxOff;
|
||||||
uint32_t m_chanMapOff;
|
uint32_t m_chanMapOff;
|
||||||
uint32_t m_tempoTableOff;
|
uint32_t m_tempoTableOff;
|
||||||
uint32_t m_initialTempo;
|
uint32_t m_initialTempo;
|
||||||
uint32_t m_unkOff;
|
uint32_t m_unkOff;
|
||||||
uint32_t m_chanOffs[64];
|
|
||||||
void swapBig();
|
void swapBig();
|
||||||
} m_header;
|
} m_header;
|
||||||
|
|
||||||
/** Channel header */
|
/** Track region ('clip' in an NLA representation) */
|
||||||
struct ChanHeader
|
struct TrackRegion
|
||||||
{
|
{
|
||||||
uint32_t m_startTick;
|
uint32_t m_startTick;
|
||||||
uint16_t m_unk1;
|
uint16_t m_unk1;
|
||||||
uint16_t m_unk2;
|
uint16_t m_unk2;
|
||||||
uint16_t m_dataIndex;
|
uint16_t m_regionIndex;
|
||||||
uint16_t m_unk3;
|
int16_t m_initialPitch;
|
||||||
uint32_t m_startTick2;
|
|
||||||
uint16_t m_unk4;
|
|
||||||
uint16_t m_unk5;
|
|
||||||
uint16_t m_unk6;
|
|
||||||
uint16_t m_unk7;
|
|
||||||
void swapBig();
|
void swapBig();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -60,6 +54,8 @@ class SongState
|
||||||
void swapBig();
|
void swapBig();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const unsigned char* m_songData = nullptr; /**< Base pointer to active song */
|
||||||
|
|
||||||
/** State of a single channel within arrangement */
|
/** State of a single channel within arrangement */
|
||||||
struct Channel
|
struct Channel
|
||||||
{
|
{
|
||||||
|
@ -73,10 +69,10 @@ class SongState
|
||||||
|
|
||||||
SongState& m_parent;
|
SongState& m_parent;
|
||||||
uint8_t m_midiChan; /**< MIDI channel number of song channel */
|
uint8_t m_midiChan; /**< MIDI channel number of song channel */
|
||||||
uint32_t m_startTick; /**< Tick to start execution of channel commands */
|
const TrackRegion* m_curRegion; /**< Pointer to currently-playing track region */
|
||||||
|
const TrackRegion* m_nextRegion; /**< Pointer to next-queued track region */
|
||||||
|
|
||||||
const unsigned char* m_dataBase; /**< Base pointer to command data */
|
const unsigned char* m_data = nullptr; /**< Pointer to upcoming command data */
|
||||||
const unsigned char* m_data; /**< Pointer to upcoming command data */
|
|
||||||
const unsigned char* m_pitchWheelData = nullptr; /**< Pointer to upcoming pitch data */
|
const unsigned char* m_pitchWheelData = nullptr; /**< Pointer to upcoming pitch data */
|
||||||
const unsigned char* m_modWheelData = nullptr; /**< Pointer to upcoming modulation data */
|
const unsigned char* m_modWheelData = nullptr; /**< Pointer to upcoming modulation data */
|
||||||
uint32_t m_lastPitchTick = 0; /**< Last position of pitch wheel change */
|
uint32_t m_lastPitchTick = 0; /**< Last position of pitch wheel change */
|
||||||
|
@ -85,13 +81,16 @@ class SongState
|
||||||
int32_t m_lastModVal = 0; /**< Last value of mod */
|
int32_t m_lastModVal = 0; /**< Last value of mod */
|
||||||
std::array<uint16_t, 128> m_remNoteLengths = {}; /**< Remaining ticks per note */
|
std::array<uint16_t, 128> m_remNoteLengths = {}; /**< Remaining ticks per note */
|
||||||
|
|
||||||
int32_t m_waitCountdown = 0; /**< Current wait in ticks */
|
int32_t m_eventWaitCountdown = 0; /**< Current wait in ticks */
|
||||||
|
int32_t m_lastN64EventTick = 0; /**< Last command time on this channel (for computing delta times from absolute times in N64 songs) */
|
||||||
|
|
||||||
Channel(SongState& parent, uint8_t midiChan, uint32_t startTick,
|
Channel(SongState& parent, uint8_t midiChan, const TrackRegion* regions);
|
||||||
const unsigned char* song, const unsigned char* chan);
|
void setRegion(Sequencer& seq, const TrackRegion* region);
|
||||||
|
void advanceRegion(Sequencer& seq);
|
||||||
bool advance(Sequencer& seq, int32_t ticks);
|
bool advance(Sequencer& seq, int32_t ticks);
|
||||||
};
|
};
|
||||||
std::array<std::experimental::optional<Channel>, 64> m_channels;
|
std::array<std::experimental::optional<Channel>, 64> m_channels;
|
||||||
|
const uint32_t* m_regionIdx; /**< Table of offsets to song-region data */
|
||||||
|
|
||||||
/** Current pointer to tempo control, iterated over playback */
|
/** Current pointer to tempo control, iterated over playback */
|
||||||
const TempoChange* m_tempoPtr = nullptr;
|
const TempoChange* m_tempoPtr = nullptr;
|
||||||
|
|
|
@ -159,6 +159,7 @@ class Voice : public Entity
|
||||||
|
|
||||||
void _setPan(float pan);
|
void _setPan(float pan);
|
||||||
void _setSurroundPan(float span);
|
void _setSurroundPan(float span);
|
||||||
|
void _setPitchWheel(float pitchWheel);
|
||||||
void _notifyCtrlChange(uint8_t ctrl, int8_t val);
|
void _notifyCtrlChange(uint8_t ctrl, int8_t val);
|
||||||
public:
|
public:
|
||||||
~Voice();
|
~Voice();
|
||||||
|
@ -321,7 +322,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get MIDI pitch wheel value on voice */
|
/** Get MIDI pitch wheel value on voice */
|
||||||
int8_t getPitchWheel() const {return m_curPitchWheel * 127;}
|
int8_t getPitchWheel() const {return m_curPitchWheel * 127 / 2 + 64;}
|
||||||
|
|
||||||
/** Get MIDI aftertouch value on voice */
|
/** Get MIDI aftertouch value on voice */
|
||||||
int8_t getAftertouch() const {return m_curAftertouch;}
|
int8_t getAftertouch() const {return m_curAftertouch;}
|
||||||
|
|
|
@ -890,6 +890,61 @@ static std::vector<std::pair<SystemString, IntrusiveAudioGroupData>> LoadRS1N64(
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<std::pair<SystemString, ContainerRegistry::SongData>> LoadRS1N64Songs(FILE* fp)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<SystemString, ContainerRegistry::SongData>> ret;
|
||||||
|
size_t endPos = FileLength(fp);
|
||||||
|
|
||||||
|
std::unique_ptr<uint8_t[]> data(new uint8_t[endPos]);
|
||||||
|
fread(data.get(), 1, endPos, fp);
|
||||||
|
|
||||||
|
if ((data[0] & 0x80) != 0x80 && (data[3] & 0x80) == 0x80)
|
||||||
|
SwapN64Rom32(data.get(), endPos);
|
||||||
|
else if ((data[0] & 0x80) != 0x80 && (data[1] & 0x80) == 0x80)
|
||||||
|
SwapN64Rom16(data.get(), endPos);
|
||||||
|
|
||||||
|
const uint8_t* dataSeg = reinterpret_cast<const uint8_t*>(memmem(data.get(), endPos,
|
||||||
|
"dbg_data\0\0\0\0\0\0\0\0", 16));
|
||||||
|
if (dataSeg)
|
||||||
|
{
|
||||||
|
dataSeg += 28;
|
||||||
|
size_t fstEnd = SBig(*reinterpret_cast<const uint32_t*>(dataSeg));
|
||||||
|
dataSeg += 4;
|
||||||
|
size_t fstOff = SBig(*reinterpret_cast<const uint32_t*>(dataSeg));
|
||||||
|
if (endPos <= size_t(dataSeg - data.get()) + fstOff || endPos <= size_t(dataSeg - data.get()) + fstEnd)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
const RS1FSTEntry* entry = reinterpret_cast<const RS1FSTEntry*>(dataSeg + fstOff);
|
||||||
|
const RS1FSTEntry* lastEnt = reinterpret_cast<const RS1FSTEntry*>(dataSeg + fstEnd);
|
||||||
|
|
||||||
|
for (; entry != lastEnt ; ++entry)
|
||||||
|
{
|
||||||
|
RS1FSTEntry ent = *entry;
|
||||||
|
ent.swapBig();
|
||||||
|
|
||||||
|
if (strstr(ent.name, "SNG"))
|
||||||
|
{
|
||||||
|
std::unique_ptr<uint8_t[]> song(new uint8_t[ent.decompSz]);
|
||||||
|
|
||||||
|
if (ent.compSz == 0xffffffff)
|
||||||
|
{
|
||||||
|
memmove(song.get(), dataSeg + ent.offset, ent.decompSz);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uLongf outSz = ent.decompSz;
|
||||||
|
uncompress(song.get(), &outSz, dataSeg + ent.offset, ent.compSz);
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemString name = StrToSys(ent.name);
|
||||||
|
ret.emplace_back(name, ContainerRegistry::SongData(std::move(song), ent.decompSz, -1, -1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static bool ValidateBFNPC(FILE* fp)
|
static bool ValidateBFNPC(FILE* fp)
|
||||||
{
|
{
|
||||||
size_t endPos = FileLength(fp);
|
size_t endPos = FileLength(fp);
|
||||||
|
@ -1847,17 +1902,17 @@ ContainerRegistry::LoadSongs(const SystemChar* path)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
if (ValidateRS1N64(fp))
|
||||||
if (ValidateRS1PCSongs(fp))
|
|
||||||
{
|
{
|
||||||
auto ret = LoadRS1PCSongs(fp);
|
auto ret = LoadRS1N64Songs(fp);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ValidateRS1N64Songs(fp))
|
#if 0
|
||||||
|
if (ValidateRS1PCSongs(fp))
|
||||||
{
|
{
|
||||||
auto ret = LoadRS1N64Songs(fp);
|
auto ret = LoadRS1PCSongs(fp);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,28 +68,21 @@ static uint32_t DecodeTimeRLE(const unsigned char*& data)
|
||||||
|
|
||||||
void SongState::Header::swapBig()
|
void SongState::Header::swapBig()
|
||||||
{
|
{
|
||||||
m_version = SBig(m_version);
|
m_trackIdxOff = SBig(m_trackIdxOff);
|
||||||
m_chanIdxOff = SBig(m_chanIdxOff);
|
m_regionIdxOff = SBig(m_regionIdxOff);
|
||||||
m_chanMapOff = SBig(m_chanMapOff);
|
m_chanMapOff = SBig(m_chanMapOff);
|
||||||
m_tempoTableOff = SBig(m_tempoTableOff);
|
m_tempoTableOff = SBig(m_tempoTableOff);
|
||||||
m_initialTempo = SBig(m_initialTempo);
|
m_initialTempo = SBig(m_initialTempo);
|
||||||
m_unkOff = SBig(m_unkOff);
|
m_unkOff = SBig(m_unkOff);
|
||||||
for (int i=0 ; i<64 ; ++i)
|
|
||||||
m_chanOffs[i] = SBig(m_chanOffs[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SongState::ChanHeader::swapBig()
|
void SongState::TrackRegion::swapBig()
|
||||||
{
|
{
|
||||||
m_startTick = SBig(m_startTick);
|
m_startTick = SBig(m_startTick);
|
||||||
m_unk1 = SBig(m_unk1);
|
m_unk1 = SBig(m_unk1);
|
||||||
m_unk2 = SBig(m_unk2);
|
m_unk2 = SBig(m_unk2);
|
||||||
m_dataIndex = SBig(m_dataIndex);
|
m_regionIndex = SBig(m_regionIndex);
|
||||||
m_unk3 = SBig(m_unk3);
|
m_initialPitch = SBig(m_initialPitch);
|
||||||
m_startTick2 = SBig(m_startTick2);
|
|
||||||
m_unk4 = SBig(m_unk4);
|
|
||||||
m_unk5 = SBig(m_unk5);
|
|
||||||
m_unk6 = SBig(m_unk6);
|
|
||||||
m_unk7 = SBig(m_unk7);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SongState::TempoChange::swapBig()
|
void SongState::TempoChange::swapBig()
|
||||||
|
@ -105,47 +98,69 @@ void SongState::Channel::Header::swapBig()
|
||||||
m_modOff = SBig(m_modOff);
|
m_modOff = SBig(m_modOff);
|
||||||
}
|
}
|
||||||
|
|
||||||
SongState::Channel::Channel(SongState& parent, uint8_t midiChan, uint32_t startTick,
|
SongState::Channel::Channel(SongState& parent, uint8_t midiChan, const TrackRegion* regions)
|
||||||
const unsigned char* song, const unsigned char* chan)
|
: m_parent(parent), m_midiChan(midiChan), m_curRegion(nullptr), m_nextRegion(regions)
|
||||||
: m_parent(parent), m_midiChan(midiChan), m_startTick(startTick), m_dataBase(chan + 12)
|
{}
|
||||||
{
|
|
||||||
m_data = m_dataBase;
|
|
||||||
|
|
||||||
Header header = *reinterpret_cast<const Header*>(chan);
|
void SongState::Channel::setRegion(Sequencer& seq, const TrackRegion* region)
|
||||||
|
{
|
||||||
|
assert(region->m_regionIndex != 0xffff);
|
||||||
|
m_curRegion = region;
|
||||||
|
uint32_t regionIdx = SBig(m_curRegion->m_regionIndex);
|
||||||
|
m_nextRegion = &m_curRegion[1];
|
||||||
|
|
||||||
|
m_data = m_parent.m_songData + SBig(m_parent.m_regionIdx[regionIdx]);
|
||||||
|
|
||||||
|
Header header = *reinterpret_cast<const Header*>(m_data);
|
||||||
header.swapBig();
|
header.swapBig();
|
||||||
if (header.m_type != 8)
|
assert(header.m_type == 8);
|
||||||
{
|
m_data += 12;
|
||||||
m_data = nullptr;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (header.m_pitchOff)
|
if (header.m_pitchOff)
|
||||||
m_pitchWheelData = song + header.m_pitchOff;
|
m_pitchWheelData = m_parent.m_songData + header.m_pitchOff;
|
||||||
if (header.m_modOff)
|
if (header.m_modOff)
|
||||||
m_modWheelData = song + header.m_modOff;
|
m_modWheelData = m_parent.m_songData + header.m_modOff;
|
||||||
|
|
||||||
m_waitCountdown = startTick;
|
m_eventWaitCountdown = 0;
|
||||||
m_lastPitchTick = startTick;
|
m_lastPitchTick = m_parent.m_curTick;
|
||||||
m_lastModTick = startTick;
|
//m_lastPitchVal = SBig(m_curRegion->m_initialPitch);
|
||||||
m_waitCountdown += int32_t(DecodeTimeRLE(m_data));
|
m_lastPitchVal = 0;
|
||||||
|
seq.setPitchWheel(m_midiChan, clamp(-1.f, m_lastPitchVal / 32768.f, 1.f));
|
||||||
|
m_lastModTick = m_parent.m_curTick;
|
||||||
|
m_lastModVal = 0;
|
||||||
|
seq.setCtrlValue(m_midiChan, 1, clamp(0, m_lastModVal * 128 / 16384, 127));
|
||||||
|
if (m_parent.m_header.m_trackIdxOff == 0x18 || m_parent.m_header.m_trackIdxOff == 0x58)
|
||||||
|
m_eventWaitCountdown = int32_t(DecodeTimeRLE(m_data));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int32_t absTick = SBig(*reinterpret_cast<const int32_t*>(m_data));
|
||||||
|
m_eventWaitCountdown = absTick;
|
||||||
|
m_lastN64EventTick = absTick;
|
||||||
|
m_data += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SongState::Channel::advanceRegion(Sequencer& seq)
|
||||||
|
{
|
||||||
|
setRegion(seq, m_nextRegion);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SongState::initialize(const unsigned char* ptr)
|
void SongState::initialize(const unsigned char* ptr)
|
||||||
{
|
{
|
||||||
|
m_songData = ptr;
|
||||||
m_header = *reinterpret_cast<const Header*>(ptr);
|
m_header = *reinterpret_cast<const Header*>(ptr);
|
||||||
m_header.swapBig();
|
m_header.swapBig();
|
||||||
|
const uint32_t* trackIdx = reinterpret_cast<const uint32_t*>(ptr + m_header.m_trackIdxOff);
|
||||||
|
m_regionIdx = reinterpret_cast<const uint32_t*>(ptr + m_header.m_regionIdxOff);
|
||||||
|
const uint8_t* chanMap = reinterpret_cast<const uint8_t*>(ptr + m_header.m_chanMapOff);
|
||||||
|
|
||||||
/* Initialize all channels */
|
/* Initialize all channels */
|
||||||
for (int i=0 ; i<64 ; ++i)
|
for (int i=0 ; i<64 ; ++i)
|
||||||
{
|
{
|
||||||
if (m_header.m_chanOffs[i])
|
if (trackIdx[i])
|
||||||
{
|
{
|
||||||
ChanHeader cHeader = *reinterpret_cast<const ChanHeader*>(ptr + m_header.m_chanOffs[i]);
|
const TrackRegion* region = reinterpret_cast<const TrackRegion*>(ptr + SBig(trackIdx[i]));
|
||||||
cHeader.swapBig();
|
m_channels[i].emplace(*this, chanMap[i], region);
|
||||||
const uint32_t* chanIdx = reinterpret_cast<const uint32_t*>(ptr + m_header.m_chanIdxOff);
|
|
||||||
const uint8_t* chanMap = reinterpret_cast<const uint8_t*>(ptr + m_header.m_chanMapOff);
|
|
||||||
m_channels[i].emplace(*this, chanMap[i], cHeader.m_startTick, ptr,
|
|
||||||
ptr + SBig(chanIdx[cHeader.m_dataIndex]));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_channels[i] = std::experimental::nullopt;
|
m_channels[i] = std::experimental::nullopt;
|
||||||
|
@ -164,11 +179,21 @@ void SongState::initialize(const unsigned char* ptr)
|
||||||
|
|
||||||
bool SongState::Channel::advance(Sequencer& seq, int32_t ticks)
|
bool SongState::Channel::advance(Sequencer& seq, int32_t ticks)
|
||||||
{
|
{
|
||||||
if (!m_data)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
int32_t endTick = m_parent.m_curTick + ticks;
|
int32_t endTick = m_parent.m_curTick + ticks;
|
||||||
|
|
||||||
|
/* Advance region if needed */
|
||||||
|
while (m_nextRegion->m_regionIndex != 0xffff)
|
||||||
|
{
|
||||||
|
uint32_t nextRegTick = SBig(m_nextRegion->m_startTick);
|
||||||
|
if (endTick > nextRegTick)
|
||||||
|
advanceRegion(seq);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_data)
|
||||||
|
return m_nextRegion->m_regionIndex == 0xffff;
|
||||||
|
|
||||||
/* Update continuous pitch data */
|
/* Update continuous pitch data */
|
||||||
if (m_pitchWheelData)
|
if (m_pitchWheelData)
|
||||||
{
|
{
|
||||||
|
@ -224,7 +249,7 @@ bool SongState::Channel::advance(Sequencer& seq, int32_t ticks)
|
||||||
m_lastModTick = nextTick;
|
m_lastModTick = nextTick;
|
||||||
remModTicks -= (nextTick - modTick);
|
remModTicks -= (nextTick - modTick);
|
||||||
modTick = nextTick;
|
modTick = nextTick;
|
||||||
seq.setCtrlValue(m_midiChan, 1, clamp(0, (m_lastModVal + 8192) * 128 / 16384, 127));
|
seq.setCtrlValue(m_midiChan, 1, clamp(0, m_lastModVal * 128 / 16384, 127));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
remModTicks -= (nextTick - modTick);
|
remModTicks -= (nextTick - modTick);
|
||||||
|
@ -251,14 +276,17 @@ bool SongState::Channel::advance(Sequencer& seq, int32_t ticks)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Loop through as many commands as we can for this time period */
|
/* Loop through as many commands as we can for this time period */
|
||||||
|
if (m_parent.m_header.m_trackIdxOff == 0x18 || m_parent.m_header.m_trackIdxOff == 0x58)
|
||||||
|
{
|
||||||
|
/* GameCube */
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
/* Advance wait timer if active, returning if waiting */
|
/* Advance wait timer if active, returning if waiting */
|
||||||
if (m_waitCountdown)
|
if (m_eventWaitCountdown)
|
||||||
{
|
{
|
||||||
m_waitCountdown -= ticks;
|
m_eventWaitCountdown -= ticks;
|
||||||
ticks = 0;
|
ticks = 0;
|
||||||
if (m_waitCountdown > 0)
|
if (m_eventWaitCountdown > 0)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +295,7 @@ bool SongState::Channel::advance(Sequencer& seq, int32_t ticks)
|
||||||
{
|
{
|
||||||
/* End of channel */
|
/* End of channel */
|
||||||
m_data = nullptr;
|
m_data = nullptr;
|
||||||
return true;
|
return m_nextRegion->m_regionIndex == 0xffff;
|
||||||
}
|
}
|
||||||
else if (m_data[0] & 0x80)
|
else if (m_data[0] & 0x80)
|
||||||
{
|
{
|
||||||
|
@ -289,7 +317,59 @@ bool SongState::Channel::advance(Sequencer& seq, int32_t ticks)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set next delta-time */
|
/* Set next delta-time */
|
||||||
m_waitCountdown += int32_t(DecodeTimeRLE(m_data));
|
m_eventWaitCountdown += int32_t(DecodeTimeRLE(m_data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* N64 */
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
/* Advance wait timer if active, returning if waiting */
|
||||||
|
if (m_eventWaitCountdown)
|
||||||
|
{
|
||||||
|
m_eventWaitCountdown -= ticks;
|
||||||
|
ticks = 0;
|
||||||
|
if (m_eventWaitCountdown > 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load next command */
|
||||||
|
if (*reinterpret_cast<const uint32_t*>(m_data) == 0xffff0000)
|
||||||
|
{
|
||||||
|
/* End of channel */
|
||||||
|
m_data = nullptr;
|
||||||
|
return m_nextRegion->m_regionIndex == 0xffff;
|
||||||
|
}
|
||||||
|
else if (m_data[0] & 0x80)
|
||||||
|
{
|
||||||
|
/* Control change */
|
||||||
|
uint8_t val = m_data[0] & 0x7f;
|
||||||
|
uint8_t ctrl = m_data[1] & 0x7f;
|
||||||
|
seq.setCtrlValue(m_midiChan, ctrl, val);
|
||||||
|
m_data += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((m_data[2] & 0x80) != 0x80)
|
||||||
|
{
|
||||||
|
/* Note */
|
||||||
|
uint16_t length = SBig(*reinterpret_cast<const uint16_t*>(m_data));
|
||||||
|
uint8_t note = m_data[2] & 0x7f;
|
||||||
|
uint8_t vel = m_data[3] & 0x7f;
|
||||||
|
seq.keyOn(m_midiChan, note, vel);
|
||||||
|
m_remNoteLengths[note] = length;
|
||||||
|
}
|
||||||
|
m_data += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set next delta-time */
|
||||||
|
int32_t absTick = SBig(*reinterpret_cast<const int32_t*>(m_data));
|
||||||
|
assert(absTick >= m_lastN64EventTick);
|
||||||
|
m_eventWaitCountdown += absTick - m_lastN64EventTick;
|
||||||
|
m_lastN64EventTick = absTick;
|
||||||
|
m_data += 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -57,12 +57,12 @@ float SoundMacroState::Evaluator::evaluate(const Voice& vox, const SoundMacroSta
|
||||||
case 130:
|
case 130:
|
||||||
/* LFO1 */
|
/* LFO1 */
|
||||||
if (vox.m_lfoPeriods[0])
|
if (vox.m_lfoPeriods[0])
|
||||||
thisValue = (std::sin(vox.m_voiceTime / vox.m_lfoPeriods[0] * 2.f * M_PIF) / 2.f + 1.f) * 127.f;
|
thisValue = (std::sin(vox.m_voiceTime / vox.m_lfoPeriods[0] * 2.f * M_PIF) / 2.f + 0.5f) * 127.f;
|
||||||
break;
|
break;
|
||||||
case 131:
|
case 131:
|
||||||
/* LFO2 */
|
/* LFO2 */
|
||||||
if (vox.m_lfoPeriods[1])
|
if (vox.m_lfoPeriods[1])
|
||||||
thisValue = (std::sin(vox.m_voiceTime / vox.m_lfoPeriods[1] * 2.f * M_PIF) / 2.f + 1.f) * 127.f;
|
thisValue = (std::sin(vox.m_voiceTime / vox.m_lfoPeriods[1] * 2.f * M_PIF) / 2.f + 0.5f) * 127.f;
|
||||||
break;
|
break;
|
||||||
case 132:
|
case 132:
|
||||||
/* Surround panning */
|
/* Surround panning */
|
||||||
|
|
|
@ -287,7 +287,7 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
|
||||||
_setPan(m_curPan);
|
_setPan(m_curPan);
|
||||||
|
|
||||||
if (m_state.m_pitchWheelSel)
|
if (m_state.m_pitchWheelSel)
|
||||||
setPitchWheel(m_state.m_pitchWheelSel.evaluate(*this, m_state) / 127.f);
|
_setPitchWheel((m_state.m_pitchWheelSel.evaluate(*this, m_state) - 64.f) / 64.f);
|
||||||
|
|
||||||
/* Process user volume slew */
|
/* Process user volume slew */
|
||||||
if (m_engine.m_ampMode == AmplitudeMode::PerSample)
|
if (m_engine.m_ampMode == AmplitudeMode::PerSample)
|
||||||
|
@ -793,7 +793,7 @@ void Voice::startSample(int16_t sampId, int32_t offset)
|
||||||
m_sampleRate = m_curSample->first.m_sampleRate;
|
m_sampleRate = m_curSample->first.m_sampleRate;
|
||||||
m_curPitch = m_curSample->first.m_pitch;
|
m_curPitch = m_curSample->first.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->first.m_sampleRate);
|
||||||
|
|
||||||
int32_t numSamples = m_curSample->first.m_numSamples & 0xffffff;
|
int32_t numSamples = m_curSample->first.m_numSamples & 0xffffff;
|
||||||
|
@ -1063,9 +1063,8 @@ void Voice::setPitchAdsr(ObjectId adsrId, int32_t cents)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setPitchWheel(float pitchWheel)
|
void Voice::_setPitchWheel(float pitchWheel)
|
||||||
{
|
{
|
||||||
m_curPitchWheel = amuse::clamp(-1.f, pitchWheel, 1.f);
|
|
||||||
if (pitchWheel > 0.f)
|
if (pitchWheel > 0.f)
|
||||||
m_pitchWheelVal = m_pitchWheelUp * m_curPitchWheel;
|
m_pitchWheelVal = m_pitchWheelUp * m_curPitchWheel;
|
||||||
else if (pitchWheel < 0.f)
|
else if (pitchWheel < 0.f)
|
||||||
|
@ -1073,6 +1072,12 @@ void Voice::setPitchWheel(float pitchWheel)
|
||||||
else
|
else
|
||||||
m_pitchWheelVal = 0;
|
m_pitchWheelVal = 0;
|
||||||
m_pitchDirty = true;
|
m_pitchDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::setPitchWheel(float pitchWheel)
|
||||||
|
{
|
||||||
|
m_curPitchWheel = amuse::clamp(-1.f, pitchWheel, 1.f);
|
||||||
|
_setPitchWheel(m_curPitchWheel);
|
||||||
|
|
||||||
for (std::shared_ptr<Voice>& vox : m_childVoices)
|
for (std::shared_ptr<Voice>& vox : m_childVoices)
|
||||||
vox->setPitchWheel(pitchWheel);
|
vox->setPitchWheel(pitchWheel);
|
||||||
|
@ -1082,7 +1087,7 @@ void Voice::setPitchWheelRange(int8_t up, int8_t down)
|
||||||
{
|
{
|
||||||
m_pitchWheelUp = up * 100;
|
m_pitchWheelUp = up * 100;
|
||||||
m_pitchWheelDown = down * 100;
|
m_pitchWheelDown = down * 100;
|
||||||
setPitchWheel(m_curPitchWheel);
|
_setPitchWheel(m_curPitchWheel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setAftertouch(uint8_t aftertouch)
|
void Voice::setAftertouch(uint8_t aftertouch)
|
||||||
|
|
Loading…
Reference in New Issue