mirror of
https://github.com/AxioDL/amuse.git
synced 2025-12-08 13:14:58 +00:00
Clipboard support and various bug fixes
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
#include "amuse/AudioGroup.hpp"
|
||||
#include "amuse/AudioGroupData.hpp"
|
||||
#include <regex>
|
||||
#include <athena/FileReader.hpp>
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
@@ -20,6 +24,15 @@ void AudioGroup::assign(SystemStringView groupPath)
|
||||
m_proj = AudioGroupProject::CreateAudioGroupProject(groupPath);
|
||||
m_samp = nullptr;
|
||||
}
|
||||
void AudioGroup::assign(const AudioGroup& data, SystemStringView groupPath)
|
||||
{
|
||||
/* Reverse order when loading intermediates */
|
||||
m_groupPath = groupPath;
|
||||
m_sdir = AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(groupPath);
|
||||
m_pool = AudioGroupPool::CreateAudioGroupPool(groupPath);
|
||||
m_proj = AudioGroupProject::CreateAudioGroupProject(data.getProj());
|
||||
m_samp = nullptr;
|
||||
}
|
||||
|
||||
const SampleEntry* AudioGroup::getSample(SampleId sfxId) const
|
||||
{
|
||||
@@ -93,4 +106,249 @@ void AudioGroup::makeCompressedVersion(SampleId sfxId, const SampleEntry* sample
|
||||
m_sdir._extractCompressed(sfxId, *sample->m_data, m_groupPath, sample->m_data->m_looseData.get(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioGroupDatabase::renameSample(SampleId id, std::string_view str)
|
||||
{
|
||||
SystemString oldBasePath = getSampleBasePath(id);
|
||||
SampleId::CurNameDB->rename(id, str);
|
||||
SystemString newBasePath = getSampleBasePath(id);
|
||||
Rename((oldBasePath + _S(".wav")).c_str(), (newBasePath + _S(".wav")).c_str());
|
||||
Rename((oldBasePath + _S(".dsp")).c_str(), (newBasePath + _S(".dsp")).c_str());
|
||||
Rename((oldBasePath + _S(".vadpcm")).c_str(), (newBasePath + _S(".vadpcm")).c_str());
|
||||
}
|
||||
|
||||
void AudioGroupDatabase::deleteSample(SampleId id)
|
||||
{
|
||||
SystemString basePath = getSampleBasePath(id);
|
||||
Unlink((basePath + _S(".wav")).c_str());
|
||||
Unlink((basePath + _S(".dsp")).c_str());
|
||||
Unlink((basePath + _S(".vadpcm")).c_str());
|
||||
}
|
||||
|
||||
void AudioGroupDatabase::copySampleInto(const SystemString& basePath, const SystemString& newBasePath)
|
||||
{
|
||||
Copy((basePath + _S(".wav")).c_str(), (newBasePath + _S(".wav")).c_str());
|
||||
Copy((basePath + _S(".dsp")).c_str(), (newBasePath + _S(".dsp")).c_str());
|
||||
Copy((basePath + _S(".vadpcm")).c_str(), (newBasePath + _S(".vadpcm")).c_str());
|
||||
}
|
||||
|
||||
void AudioGroupDatabase::_recursiveRenameMacro(SoundMacroId id, std::string_view str, int& macroIdx,
|
||||
std::unordered_set<SoundMacroId>& renamedIds)
|
||||
{
|
||||
if (renamedIds.find(id) != renamedIds.cend())
|
||||
return;
|
||||
if (const SoundMacro* macro = getPool().soundMacro(id))
|
||||
{
|
||||
if (!strncmp(SoundMacroId::CurNameDB->resolveNameFromId(id).data(), "macro", 5))
|
||||
{
|
||||
std::string macroName("macro"sv);
|
||||
if (macroIdx)
|
||||
{
|
||||
char num[16];
|
||||
snprintf(num, 16, "%d", macroIdx);
|
||||
macroName += num;
|
||||
}
|
||||
macroName += '_';
|
||||
macroName += str;
|
||||
++macroIdx;
|
||||
SoundMacroId::CurNameDB->rename(id, macroName);
|
||||
renamedIds.insert(id);
|
||||
int sampleIdx = 0;
|
||||
for (const auto& cmd : macro->m_cmds)
|
||||
{
|
||||
switch (cmd->Isa())
|
||||
{
|
||||
case SoundMacro::CmdOp::StartSample:
|
||||
{
|
||||
SoundMacro::CmdStartSample* ss = static_cast<SoundMacro::CmdStartSample*>(cmd.get());
|
||||
if (!strncmp(SampleId::CurNameDB->resolveNameFromId(ss->sample.id).data(), "sample", 6))
|
||||
{
|
||||
std::string sampleName("sample"sv);
|
||||
if (sampleIdx)
|
||||
{
|
||||
char num[16];
|
||||
snprintf(num, 16, "%d", sampleIdx);
|
||||
sampleName += num;
|
||||
}
|
||||
sampleName += '_';
|
||||
sampleName += macroName;
|
||||
++sampleIdx;
|
||||
renameSample(ss->sample.id, sampleName);
|
||||
}
|
||||
}
|
||||
case SoundMacro::CmdOp::SplitKey:
|
||||
_recursiveRenameMacro(static_cast<SoundMacro::CmdSplitKey*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
||||
break;
|
||||
case SoundMacro::CmdOp::SplitVel:
|
||||
_recursiveRenameMacro(static_cast<SoundMacro::CmdSplitVel*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
||||
break;
|
||||
case SoundMacro::CmdOp::Goto:
|
||||
_recursiveRenameMacro(static_cast<SoundMacro::CmdGoto*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
||||
break;
|
||||
case SoundMacro::CmdOp::PlayMacro:
|
||||
_recursiveRenameMacro(static_cast<SoundMacro::CmdPlayMacro*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
||||
break;
|
||||
case SoundMacro::CmdOp::SplitMod:
|
||||
_recursiveRenameMacro(static_cast<SoundMacro::CmdSplitMod*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
||||
break;
|
||||
case SoundMacro::CmdOp::SplitRnd:
|
||||
_recursiveRenameMacro(static_cast<SoundMacro::CmdSplitRnd*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
||||
break;
|
||||
case SoundMacro::CmdOp::GoSub:
|
||||
_recursiveRenameMacro(static_cast<SoundMacro::CmdGoSub*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
||||
break;
|
||||
case SoundMacro::CmdOp::TrapEvent:
|
||||
_recursiveRenameMacro(static_cast<SoundMacro::CmdTrapEvent*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
||||
break;
|
||||
case SoundMacro::CmdOp::SendMessage:
|
||||
_recursiveRenameMacro(static_cast<SoundMacro::CmdSendMessage*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const std::regex DefineGRPEntry(R"(#define\s+GRP(\S+)\s+(\S+))",
|
||||
std::regex::ECMAScript|std::regex::optimize);
|
||||
static const std::regex DefineSNGEntry(R"(#define\s+SNG(\S+)\s+(\S+))",
|
||||
std::regex::ECMAScript|std::regex::optimize);
|
||||
static const std::regex DefineSFXEntry(R"(#define\s+SFX(\S+)\s+(\S+))",
|
||||
std::regex::ECMAScript|std::regex::optimize);
|
||||
|
||||
void AudioGroupDatabase::importCHeader(std::string_view header)
|
||||
{
|
||||
setIdDatabases();
|
||||
std::match_results<std::string_view::const_iterator> dirMatch;
|
||||
auto begin = header.cbegin();
|
||||
auto end = header.cend();
|
||||
while (std::regex_search(begin, end, dirMatch, DefineGRPEntry))
|
||||
{
|
||||
std::string key = dirMatch[1].str();
|
||||
std::string value = dirMatch[2].str();
|
||||
char* endPtr;
|
||||
amuse::ObjectId id;
|
||||
id.id = strtoul(value.c_str(), &endPtr, 0);
|
||||
if (endPtr == value.c_str())
|
||||
continue;
|
||||
GroupId::CurNameDB->rename(id, key);
|
||||
begin = dirMatch.suffix().first;
|
||||
}
|
||||
begin = header.cbegin();
|
||||
end = header.cend();
|
||||
while (std::regex_search(begin, end, dirMatch, DefineSNGEntry))
|
||||
{
|
||||
std::string key = dirMatch[1].str();
|
||||
std::string value = dirMatch[2].str();
|
||||
char* endPtr;
|
||||
amuse::ObjectId id;
|
||||
id.id = strtoul(value.c_str(), &endPtr, 0);
|
||||
if (endPtr == value.c_str())
|
||||
continue;
|
||||
SongId::CurNameDB->rename(id, key);
|
||||
begin = dirMatch.suffix().first;
|
||||
}
|
||||
begin = header.cbegin();
|
||||
end = header.cend();
|
||||
std::unordered_set<SoundMacroId> renamedMacroIDs;
|
||||
while (std::regex_search(begin, end, dirMatch, DefineSFXEntry))
|
||||
{
|
||||
std::string key = dirMatch[1].str();
|
||||
std::string value = dirMatch[2].str();
|
||||
char* endPtr;
|
||||
amuse::ObjectId id;
|
||||
id.id = strtoul(value.c_str(), &endPtr, 0);
|
||||
if (endPtr == value.c_str())
|
||||
continue;
|
||||
SFXId::CurNameDB->rename(id, key);
|
||||
int macroIdx = 0;
|
||||
for (auto& sfxGrp : getProj().sfxGroups())
|
||||
{
|
||||
for (auto& sfx : sfxGrp.second->m_sfxEntries)
|
||||
{
|
||||
if (sfx.first == id)
|
||||
{
|
||||
ObjectId sfxObjId = sfx.second.objId;
|
||||
if (sfxObjId == ObjectId() || sfxObjId.id & 0xc000)
|
||||
continue;
|
||||
_recursiveRenameMacro(sfxObjId, key, macroIdx, renamedMacroIDs);
|
||||
}
|
||||
}
|
||||
}
|
||||
begin = dirMatch.suffix().first;
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteDefineLine(std::string& ret, std::string_view typeStr, std::string_view name, ObjectId id)
|
||||
{
|
||||
ret += "#define "sv;
|
||||
ret += typeStr;
|
||||
ret += name;
|
||||
ret += ' ';
|
||||
char idStr[16];
|
||||
snprintf(idStr, 16, "%d", id.id);
|
||||
ret += idStr;
|
||||
ret += '\n';
|
||||
}
|
||||
|
||||
std::string AudioGroupDatabase::exportCHeader(std::string_view projectName, std::string_view groupName) const
|
||||
{
|
||||
setIdDatabases();
|
||||
std::string ret;
|
||||
ret += "/* Auto-generated Amuse Defines\n"
|
||||
" *\n"
|
||||
" * Project: "sv;
|
||||
ret += projectName;
|
||||
ret += "\n"
|
||||
" * Subproject: "sv;
|
||||
ret += groupName;
|
||||
ret += "\n"
|
||||
" * Date: "sv;
|
||||
time_t curTime = time(nullptr);
|
||||
struct tm curTm;
|
||||
localtime_r(&curTime, &curTm);
|
||||
char curTmStr[26];
|
||||
asctime_r(&curTm, curTmStr);
|
||||
ret += curTmStr;
|
||||
ret += "\n"
|
||||
" */\n\n\n"sv;
|
||||
|
||||
for (const auto& sg : SortUnorderedMap(getProj().songGroups()))
|
||||
{
|
||||
auto name = amuse::GroupId::CurNameDB->resolveNameFromId(sg.first);
|
||||
WriteDefineLine(ret, "GRP"sv, name, sg.first);
|
||||
}
|
||||
for (const auto& sg : SortUnorderedMap(getProj().sfxGroups()))
|
||||
{
|
||||
auto name = amuse::GroupId::CurNameDB->resolveNameFromId(sg.first);
|
||||
WriteDefineLine(ret, "GRP"sv, name, sg.first);
|
||||
}
|
||||
|
||||
ret += "\n\n"sv;
|
||||
|
||||
std::unordered_set<amuse::SongId> songIds;
|
||||
for (const auto& sg : getProj().songGroups())
|
||||
for (const auto& song : sg.second->m_midiSetups)
|
||||
songIds.insert(song.first);
|
||||
for (amuse::SongId id : SortUnorderedSet(songIds))
|
||||
{
|
||||
auto name = amuse::SongId::CurNameDB->resolveNameFromId(id);
|
||||
WriteDefineLine(ret, "SNG"sv, name, id);
|
||||
}
|
||||
|
||||
ret += "\n\n"sv;
|
||||
|
||||
for (const auto& sg : SortUnorderedMap(getProj().sfxGroups()))
|
||||
{
|
||||
for (const auto& sfx : SortUnorderedMap(sg.second.get()->m_sfxEntries))
|
||||
{
|
||||
auto name = amuse::SFXId::CurNameDB->resolveNameFromId(sfx.first);
|
||||
WriteDefineLine(ret, "SFX"sv, name, sfx.first.id);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,12 +279,7 @@ AudioGroupPool AudioGroupPool::CreateAudioGroupPool(SystemStringView groupPath)
|
||||
smOut = MakeObj<SoundMacro>();
|
||||
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::CmdDo<MakeCmdOp, std::unique_ptr<SoundMacro::ICmd>>(r));
|
||||
}
|
||||
smOut->fromYAML(r, cmdCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,6 +411,27 @@ void SoundMacro::buildFromPrototype(const SoundMacro& other)
|
||||
m_cmds.push_back(CmdDo<MakeCopyCmdOp, std::unique_ptr<SoundMacro::ICmd>>(*cmd));
|
||||
}
|
||||
|
||||
void SoundMacro::toYAML(athena::io::YAMLDocWriter& w) const
|
||||
{
|
||||
for (const auto& c : m_cmds)
|
||||
{
|
||||
if (auto __r2 = w.enterSubRecord(nullptr))
|
||||
{
|
||||
w.setStyle(athena::io::YAMLNodeStyle::Flow);
|
||||
w.writeString("cmdOp", SoundMacro::CmdOpToStr(c->Isa()));
|
||||
c->write(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundMacro::fromYAML(athena::io::YAMLDocReader& r, size_t cmdCount)
|
||||
{
|
||||
m_cmds.reserve(cmdCount);
|
||||
for (int c = 0; c < cmdCount; ++c)
|
||||
if (auto __r2 = r.enterSubRecord(nullptr))
|
||||
m_cmds.push_back(SoundMacro::CmdDo<MakeCmdOp, std::unique_ptr<SoundMacro::ICmd>>(r));
|
||||
}
|
||||
|
||||
const SoundMacro* AudioGroupPool::soundMacro(ObjectId id) const
|
||||
{
|
||||
auto search = m_soundMacros.find(id);
|
||||
@@ -1002,15 +1018,7 @@ bool AudioGroupPool::toYAML(SystemStringView groupPath) const
|
||||
{
|
||||
if (auto __v = w.enterSubVector(SoundMacroId::CurNameDB->resolveNameFromId(p.first).data()))
|
||||
{
|
||||
for (const auto& c : p.second.get()->m_cmds)
|
||||
{
|
||||
if (auto __r2 = w.enterSubRecord(nullptr))
|
||||
{
|
||||
w.setStyle(athena::io::YAMLNodeStyle::Flow);
|
||||
w.writeString("cmdOp", SoundMacro::CmdOpToStr(c->Isa()));
|
||||
c->write(w);
|
||||
}
|
||||
}
|
||||
p.second.get()->toYAML(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,6 +353,59 @@ std::string ParseStringSlashId(const std::string& str, uint16_t& idOut)
|
||||
return {str.begin(), str.begin() + slashPos};
|
||||
}
|
||||
|
||||
void SongGroupIndex::fromYAML(athena::io::YAMLDocReader& r)
|
||||
{
|
||||
if (auto __v2 = r.enterSubRecord("normPages"))
|
||||
{
|
||||
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()))
|
||||
m_normPages[strtoul(pg.first.c_str(), nullptr, 0)].read(r);
|
||||
}
|
||||
if (auto __v2 = r.enterSubRecord("drumPages"))
|
||||
{
|
||||
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()))
|
||||
m_drumPages[strtoul(pg.first.c_str(), nullptr, 0)].read(r);
|
||||
}
|
||||
if (auto __v2 = r.enterSubRecord("songs"))
|
||||
{
|
||||
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))
|
||||
{
|
||||
uint16_t songId;
|
||||
std::string songName = ParseStringSlashId(song.first, songId);
|
||||
if (songName.empty() || songId == 0xffff)
|
||||
continue;
|
||||
SongId::CurNameDB->registerPair(songName, songId);
|
||||
|
||||
std::array<SongGroupIndex::MIDISetup, 16>& setup = m_midiSetups[songId];
|
||||
for (int i = 0; i < 16 && i < chanCount; ++i)
|
||||
if (auto __r2 = r.enterSubRecord(nullptr))
|
||||
setup[i].read(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SFXGroupIndex::fromYAML(athena::io::YAMLDocReader& r)
|
||||
{
|
||||
for (const auto& sfx : r.getCurNode()->m_mapChildren)
|
||||
if (auto __r2 = r.enterSubRecord(sfx.first.c_str()))
|
||||
{
|
||||
uint16_t sfxId;
|
||||
std::string sfxName = ParseStringSlashId(sfx.first, sfxId);
|
||||
if (sfxName.empty() || sfxId == 0xffff)
|
||||
continue;
|
||||
SFXId::CurNameDB->registerPair(sfxName, sfxId);
|
||||
m_sfxEntries[sfxId].read(r);
|
||||
}
|
||||
}
|
||||
|
||||
AudioGroupProject AudioGroupProject::CreateAudioGroupProject(SystemStringView groupPath)
|
||||
{
|
||||
AudioGroupProject ret;
|
||||
@@ -380,41 +433,7 @@ AudioGroupProject AudioGroupProject::CreateAudioGroupProject(SystemStringView gr
|
||||
|
||||
auto& idx = ret.m_songGroups[groupId];
|
||||
idx = MakeObj<SongGroupIndex>();
|
||||
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))
|
||||
{
|
||||
uint16_t songId;
|
||||
std::string songName = ParseStringSlashId(song.first, songId);
|
||||
if (songName.empty() || songId == 0xffff)
|
||||
continue;
|
||||
SongId::CurNameDB->registerPair(songName, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
idx->fromYAML(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -434,16 +453,7 @@ AudioGroupProject AudioGroupProject::CreateAudioGroupProject(SystemStringView gr
|
||||
|
||||
auto& idx = ret.m_sfxGroups[groupId];
|
||||
idx = MakeObj<SFXGroupIndex>();
|
||||
for (const auto& sfx : r.getCurNode()->m_mapChildren)
|
||||
if (auto __r2 = r.enterSubRecord(sfx.first.c_str()))
|
||||
{
|
||||
uint16_t sfxId;
|
||||
std::string sfxName = ParseStringSlashId(sfx.first, sfxId);
|
||||
if (sfxName.empty() || sfxId == 0xffff)
|
||||
continue;
|
||||
SFXId::CurNameDB->registerPair(sfxName, sfxId);
|
||||
idx->m_sfxEntries[sfxId].read(r);
|
||||
}
|
||||
idx->fromYAML(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -453,6 +463,42 @@ AudioGroupProject AudioGroupProject::CreateAudioGroupProject(SystemStringView gr
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ObjectId RegisterDedupedName(ObjectId origId, amuse::NameDB* db, NameDB::Type tp)
|
||||
{
|
||||
std::string dupeName = std::string(db->resolveNameFromId(origId)) + "-copy";
|
||||
std::string useName = dupeName;
|
||||
int dupeIdx = 1;
|
||||
while (db->m_stringToId.find(useName) != db->m_stringToId.cend())
|
||||
{
|
||||
char num[16];
|
||||
snprintf(num, 16, "%d", dupeIdx++);
|
||||
useName = dupeName + num;
|
||||
}
|
||||
ObjectId ret = db->generateId(tp);
|
||||
db->registerPair(useName, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SFXGroupIndex::SFXGroupIndex(const SFXGroupIndex& other)
|
||||
{
|
||||
for (const auto& sfx : other.m_sfxEntries)
|
||||
m_sfxEntries[RegisterDedupedName(sfx.first, SFXId::CurNameDB, NameDB::Type::SFX)] = sfx.second;
|
||||
}
|
||||
|
||||
AudioGroupProject AudioGroupProject::CreateAudioGroupProject(const AudioGroupProject& oldProj)
|
||||
{
|
||||
AudioGroupProject ret;
|
||||
|
||||
for (const auto& grp : oldProj.songGroups())
|
||||
ret.m_songGroups[RegisterDedupedName(grp.first, GroupId::CurNameDB, NameDB::Type::Group)] =
|
||||
MakeObj<SongGroupIndex>(*grp.second);
|
||||
for (const auto& grp : oldProj.sfxGroups())
|
||||
ret.m_sfxGroups[RegisterDedupedName(grp.first, GroupId::CurNameDB, NameDB::Type::Group)] =
|
||||
MakeObj<SFXGroupIndex>(*grp.second);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AudioGroupProject::BootstrapObjectIDs(athena::io::IStreamReader& r, GCNDataTag)
|
||||
{
|
||||
while (!AtEnd32(r))
|
||||
@@ -638,6 +684,76 @@ const SFXGroupIndex* AudioGroupProject::getSFXGroupIndex(int groupId) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SongGroupIndex::toYAML(athena::io::YAMLDocWriter& w) const
|
||||
{
|
||||
if (!m_normPages.empty())
|
||||
{
|
||||
if (auto __v2 = w.enterSubRecord("normPages"))
|
||||
{
|
||||
for (const auto& pg : SortUnorderedMap(m_normPages))
|
||||
{
|
||||
char name[16];
|
||||
snprintf(name, 16, "%d", pg.first);
|
||||
if (auto __r2 = w.enterSubRecord(name))
|
||||
{
|
||||
w.setStyle(athena::io::YAMLNodeStyle::Flow);
|
||||
pg.second.get().write(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!m_drumPages.empty())
|
||||
{
|
||||
if (auto __v2 = w.enterSubRecord("drumPages"))
|
||||
{
|
||||
for (const auto& pg : SortUnorderedMap(m_drumPages))
|
||||
{
|
||||
char name[16];
|
||||
snprintf(name, 16, "%d", pg.first);
|
||||
if (auto __r2 = w.enterSubRecord(name))
|
||||
{
|
||||
w.setStyle(athena::io::YAMLNodeStyle::Flow);
|
||||
pg.second.get().write(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!m_midiSetups.empty())
|
||||
{
|
||||
if (auto __v2 = w.enterSubRecord("songs"))
|
||||
{
|
||||
for (const auto& song : SortUnorderedMap(m_midiSetups))
|
||||
{
|
||||
char songString[64];
|
||||
snprintf(songString, 64, "%s/0x%04X",
|
||||
SongId::CurNameDB->resolveNameFromId(song.first).data(), int(song.first.id));
|
||||
if (auto __v3 = w.enterSubVector(songString))
|
||||
for (int i = 0; i < 16; ++i)
|
||||
if (auto __r2 = w.enterSubRecord(nullptr))
|
||||
{
|
||||
w.setStyle(athena::io::YAMLNodeStyle::Flow);
|
||||
song.second.get()[i].write(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SFXGroupIndex::toYAML(athena::io::YAMLDocWriter& w) const
|
||||
{
|
||||
for (const auto& sfx : SortUnorderedMap(m_sfxEntries))
|
||||
{
|
||||
char sfxString[64];
|
||||
snprintf(sfxString, 64, "%s/0x%04X",
|
||||
SFXId::CurNameDB->resolveNameFromId(sfx.first).data(), int(sfx.first.id));
|
||||
if (auto __r2 = w.enterSubRecord(sfxString))
|
||||
{
|
||||
w.setStyle(athena::io::YAMLNodeStyle::Flow);
|
||||
sfx.second.get().write(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioGroupProject::toYAML(SystemStringView groupPath) const
|
||||
{
|
||||
athena::io::YAMLDocWriter w("amuse::Project");
|
||||
@@ -652,56 +768,7 @@ bool AudioGroupProject::toYAML(SystemStringView groupPath) const
|
||||
snprintf(groupString, 64, "%s/0x%04X", GroupId::CurNameDB->resolveNameFromId(p.first).data(), int(p.first.id));
|
||||
if (auto __r = w.enterSubRecord(groupString))
|
||||
{
|
||||
if (!p.second.get()->m_normPages.empty())
|
||||
{
|
||||
if (auto __v2 = w.enterSubRecord("normPages"))
|
||||
{
|
||||
for (const auto& pg : SortUnorderedMap(p.second.get()->m_normPages))
|
||||
{
|
||||
char name[16];
|
||||
snprintf(name, 16, "%d", pg.first);
|
||||
if (auto __r2 = w.enterSubRecord(name))
|
||||
{
|
||||
w.setStyle(athena::io::YAMLNodeStyle::Flow);
|
||||
pg.second.get().write(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!p.second.get()->m_drumPages.empty())
|
||||
{
|
||||
if (auto __v2 = w.enterSubRecord("drumPages"))
|
||||
{
|
||||
for (const auto& pg : SortUnorderedMap(p.second.get()->m_drumPages))
|
||||
{
|
||||
char name[16];
|
||||
snprintf(name, 16, "%d", pg.first);
|
||||
if (auto __r2 = w.enterSubRecord(name))
|
||||
{
|
||||
w.setStyle(athena::io::YAMLNodeStyle::Flow);
|
||||
pg.second.get().write(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!p.second.get()->m_midiSetups.empty())
|
||||
{
|
||||
if (auto __v2 = w.enterSubRecord("songs"))
|
||||
{
|
||||
for (const auto& song : SortUnorderedMap(p.second.get()->m_midiSetups))
|
||||
{
|
||||
char songString[64];
|
||||
snprintf(songString, 64, "%s/0x%04X", SongId::CurNameDB->resolveNameFromId(song.first).data(), int(song.first.id));
|
||||
if (auto __v3 = w.enterSubVector(songString))
|
||||
for (int i = 0; i < 16; ++i)
|
||||
if (auto __r2 = w.enterSubRecord(nullptr))
|
||||
{
|
||||
w.setStyle(athena::io::YAMLNodeStyle::Flow);
|
||||
song.second.get()[i].write(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
p.second.get()->toYAML(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -717,16 +784,7 @@ bool AudioGroupProject::toYAML(SystemStringView groupPath) const
|
||||
snprintf(groupString, 64, "%s/0x%04X", GroupId::CurNameDB->resolveNameFromId(p.first).data(), int(p.first.id));
|
||||
if (auto __r = w.enterSubRecord(groupString))
|
||||
{
|
||||
for (const auto& sfx : SortUnorderedMap(p.second.get()->m_sfxEntries))
|
||||
{
|
||||
char sfxString[64];
|
||||
snprintf(sfxString, 64, "%s/0x%04X", SFXId::CurNameDB->resolveNameFromId(sfx.first).data(), int(sfx.first.id));
|
||||
if (auto __r2 = w.enterSubRecord(sfxString))
|
||||
{
|
||||
w.setStyle(athena::io::YAMLNodeStyle::Flow);
|
||||
sfx.second.get().write(w);
|
||||
}
|
||||
}
|
||||
p.second.get()->toYAML(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,35 @@ namespace amuse
|
||||
{
|
||||
static logvisor::Module Log("amuse");
|
||||
|
||||
bool Copy(const SystemChar* from, const SystemChar* to)
|
||||
{
|
||||
#if _WIN32
|
||||
return CopyFileW(from, to, FALSE) != 0;
|
||||
#else
|
||||
FILE* fi = fopen(from, "rb");
|
||||
if (!fi)
|
||||
return false;
|
||||
FILE* fo = fopen(to, "wb");
|
||||
if (!fo)
|
||||
{
|
||||
fclose(fi);
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<uint8_t[]> buf(new uint8_t[65536]);
|
||||
size_t readSz = 0;
|
||||
while ((readSz = fread(buf.get(), 1, 65536, fi)))
|
||||
fwrite(buf.get(), 1, readSz, fo);
|
||||
fclose(fi);
|
||||
fclose(fo);
|
||||
struct stat theStat;
|
||||
if (::stat(from, &theStat))
|
||||
return true;
|
||||
struct timespec times[] = { theStat.st_atim, theStat.st_mtim };
|
||||
utimensat(AT_FDCWD, to, times, 0);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#define DEFINE_ID_TYPE(type, typeName) \
|
||||
thread_local NameDB* type::CurNameDB = nullptr; \
|
||||
template<> template<> \
|
||||
|
||||
@@ -32,6 +32,8 @@ void Sequencer::ChannelState::_bringOutYourDead()
|
||||
}
|
||||
++it;
|
||||
}
|
||||
std::unordered_set<ObjToken<Voice>> newSet = m_keyoffVoxs;
|
||||
m_keyoffVoxs = newSet;
|
||||
}
|
||||
|
||||
void Sequencer::_bringOutYourDead()
|
||||
@@ -57,8 +59,8 @@ Sequencer::~Sequencer()
|
||||
m_studio.reset();
|
||||
}
|
||||
|
||||
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SongGroupIndex* songGroup, int setupId,
|
||||
ObjToken<Studio> studio)
|
||||
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, GroupId groupId, const SongGroupIndex* songGroup,
|
||||
SongId setupId, ObjToken<Studio> studio)
|
||||
: Entity(engine, group, groupId), m_songGroup(songGroup), m_studio(studio)
|
||||
{
|
||||
auto it = m_songGroup->m_midiSetups.find(setupId);
|
||||
@@ -66,7 +68,7 @@ Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, const
|
||||
m_midiSetup = it->second.data();
|
||||
}
|
||||
|
||||
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SFXGroupIndex* sfxGroup,
|
||||
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, GroupId groupId, const SFXGroupIndex* sfxGroup,
|
||||
ObjToken<Studio> studio)
|
||||
: Entity(engine, group, groupId), m_sfxGroup(sfxGroup), m_studio(studio)
|
||||
{
|
||||
@@ -238,7 +240,7 @@ ObjToken<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velocity)
|
||||
m_lastVoice.reset();
|
||||
keySearch->second->keyOff();
|
||||
keySearch->second->setPedal(false);
|
||||
m_keyoffVoxs.emplace(std::move(keySearch->second));
|
||||
m_keyoffVoxs.emplace(keySearch->second);
|
||||
m_chanVoxs.erase(keySearch);
|
||||
}
|
||||
|
||||
@@ -310,7 +312,7 @@ void Sequencer::ChannelState::keyOff(uint8_t note, uint8_t velocity)
|
||||
if ((m_lastVoice && m_lastVoice->isDestroyed()) || keySearch->second == m_lastVoice)
|
||||
m_lastVoice.reset();
|
||||
keySearch->second->keyOff();
|
||||
m_keyoffVoxs.emplace(std::move(keySearch->second));
|
||||
m_keyoffVoxs.emplace(keySearch->second);
|
||||
m_chanVoxs.erase(keySearch);
|
||||
}
|
||||
|
||||
@@ -455,7 +457,7 @@ void Sequencer::ChannelState::allOff()
|
||||
if (it->second == m_lastVoice)
|
||||
m_lastVoice.reset();
|
||||
it->second->keyOff();
|
||||
m_keyoffVoxs.emplace(std::move(it->second));
|
||||
m_keyoffVoxs.emplace(it->second);
|
||||
it = m_chanVoxs.erase(it);
|
||||
}
|
||||
}
|
||||
@@ -517,7 +519,7 @@ void Sequencer::ChannelState::killKeygroup(uint8_t kg, bool now)
|
||||
continue;
|
||||
}
|
||||
vox->keyOff();
|
||||
m_keyoffVoxs.emplace(std::move(it->second));
|
||||
m_keyoffVoxs.emplace(it->second);
|
||||
it = m_chanVoxs.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -62,15 +62,15 @@ struct Event
|
||||
uint8_t noteOrCtrl;
|
||||
uint8_t velOrVal;
|
||||
uint8_t program;
|
||||
uint16_t length;
|
||||
int length;
|
||||
int pitchBend;
|
||||
|
||||
Event(NoteEvent, uint8_t chan, uint8_t note, uint8_t vel, uint16_t len)
|
||||
Event(NoteEvent, uint8_t chan, uint8_t note, uint8_t vel, int len)
|
||||
: m_type(Type::Note), channel(chan), noteOrCtrl(note), velOrVal(vel), length(len)
|
||||
{
|
||||
}
|
||||
|
||||
Event(CtrlEvent, uint8_t chan, uint8_t note, uint8_t vel, uint16_t len)
|
||||
Event(CtrlEvent, uint8_t chan, uint8_t note, uint8_t vel, int len)
|
||||
: m_type(Type::Control), channel(chan), noteOrCtrl(note), velOrVal(vel), length(len)
|
||||
{
|
||||
}
|
||||
@@ -83,16 +83,17 @@ struct Event
|
||||
class MIDIDecoder
|
||||
{
|
||||
int m_tick = 0;
|
||||
std::vector<std::pair<int, std::multimap<int, Event>>> m_results;
|
||||
std::vector<std::pair<int, std::multimap<int, Event>>> m_results[16];
|
||||
std::multimap<int, int> m_tempos;
|
||||
std::array<std::multimap<int, Event>::iterator, 128> m_notes;
|
||||
std::array<std::multimap<int, Event>::iterator, 128> m_notes[16];
|
||||
|
||||
void _addProgramChange(int prog)
|
||||
void _addProgramChange(int chan, int prog)
|
||||
{
|
||||
m_results.emplace_back();
|
||||
m_results.back().first = prog;
|
||||
auto& results = m_results[chan];
|
||||
results.emplace_back();
|
||||
results.back().first = prog;
|
||||
for (size_t i = 0; i < 128; ++i)
|
||||
m_notes[i] = m_results.back().second.end();
|
||||
m_notes[chan][i] = results.back().second.end();
|
||||
}
|
||||
|
||||
uint8_t m_status = 0;
|
||||
@@ -128,154 +129,93 @@ public:
|
||||
std::vector<uint8_t>::const_iterator end)
|
||||
{
|
||||
std::vector<uint8_t>::const_iterator it = begin;
|
||||
if (it == end)
|
||||
return begin;
|
||||
|
||||
uint32_t deltaTime;
|
||||
_readContinuedValue(it, end, deltaTime);
|
||||
m_tick += deltaTime;
|
||||
|
||||
uint8_t a = *it++;
|
||||
uint8_t b;
|
||||
if (a & 0x80)
|
||||
m_status = a;
|
||||
else
|
||||
it--;
|
||||
|
||||
/* Not actually used as such for now */
|
||||
if (m_results.empty())
|
||||
_addProgramChange(0);
|
||||
std::multimap<int, Event>& res = m_results.back().second;
|
||||
|
||||
if (m_status == 0xff)
|
||||
while (it != end)
|
||||
{
|
||||
/* Meta events */
|
||||
if (it == end)
|
||||
return begin;
|
||||
a = *it++;
|
||||
uint32_t deltaTime;
|
||||
_readContinuedValue(it, end, deltaTime);
|
||||
m_tick += deltaTime;
|
||||
|
||||
uint32_t length;
|
||||
_readContinuedValue(it, end, length);
|
||||
uint8_t a = *it++;
|
||||
uint8_t b;
|
||||
if (a & 0x80)
|
||||
m_status = a;
|
||||
else
|
||||
it--;
|
||||
|
||||
switch (a)
|
||||
{
|
||||
case 0x51:
|
||||
{
|
||||
uint32_t tempo = 0;
|
||||
memcpy(&reinterpret_cast<uint8_t*>(&tempo)[1], &*it, 3);
|
||||
m_tempos.emplace(m_tick, 60000000 / SBig(tempo));
|
||||
}
|
||||
default:
|
||||
it += length;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t chan = m_status & 0xf;
|
||||
switch (Status(m_status & 0xf0))
|
||||
{
|
||||
case Status::NoteOff:
|
||||
if (m_status == 0xff)
|
||||
{
|
||||
/* Meta events */
|
||||
if (it == end)
|
||||
return begin;
|
||||
a = *it++;
|
||||
if (it == end)
|
||||
return begin;
|
||||
b = *it++;
|
||||
|
||||
uint8_t notenum = clamp7(a);
|
||||
std::multimap<int, Event>::iterator note = m_notes[notenum];
|
||||
if (note != res.end())
|
||||
uint32_t length;
|
||||
_readContinuedValue(it, end, length);
|
||||
|
||||
switch (a)
|
||||
{
|
||||
note->second.length = uint16_t(m_tick - note->first);
|
||||
m_notes[notenum] = res.end();
|
||||
case 0x51:
|
||||
{
|
||||
uint32_t tempo = 0;
|
||||
memcpy(&reinterpret_cast<uint8_t*>(&tempo)[1], &*it, 3);
|
||||
m_tempos.emplace(m_tick, 60000000 / SBig(tempo));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Status::NoteOn:
|
||||
{
|
||||
if (it == end)
|
||||
return begin;
|
||||
a = *it++;
|
||||
if (it == end)
|
||||
return begin;
|
||||
b = *it++;
|
||||
|
||||
uint8_t notenum = clamp7(a);
|
||||
uint8_t vel = clamp7(b);
|
||||
std::multimap<int, Event>::iterator note = m_notes[notenum];
|
||||
if (note != res.end())
|
||||
note->second.length = uint16_t(m_tick - note->first);
|
||||
|
||||
m_notes[notenum] = res.emplace(m_tick, Event{NoteEvent{}, chan, notenum, vel, 0});
|
||||
break;
|
||||
}
|
||||
case Status::NotePressure:
|
||||
{
|
||||
if (it == end)
|
||||
return begin;
|
||||
a = *it++;
|
||||
if (it == end)
|
||||
return begin;
|
||||
b = *it++;
|
||||
break;
|
||||
}
|
||||
case Status::ControlChange:
|
||||
{
|
||||
if (it == end)
|
||||
return begin;
|
||||
a = *it++;
|
||||
if (it == end)
|
||||
return begin;
|
||||
b = *it++;
|
||||
res.emplace(m_tick, Event{CtrlEvent{}, chan, clamp7(a), clamp7(b), 0});
|
||||
break;
|
||||
}
|
||||
case Status::ProgramChange:
|
||||
{
|
||||
if (it == end)
|
||||
return begin;
|
||||
a = *it++;
|
||||
res.emplace(m_tick, Event{ProgEvent{}, chan, a});
|
||||
break;
|
||||
}
|
||||
case Status::ChannelPressure:
|
||||
{
|
||||
if (it == end)
|
||||
return begin;
|
||||
a = *it++;
|
||||
break;
|
||||
}
|
||||
case Status::PitchBend:
|
||||
{
|
||||
if (it == end)
|
||||
return begin;
|
||||
a = *it++;
|
||||
if (it == end)
|
||||
return begin;
|
||||
b = *it++;
|
||||
res.emplace(m_tick, Event{PitchEvent{}, chan, clamp7(b) * 128 + clamp7(a)});
|
||||
break;
|
||||
}
|
||||
case Status::SysEx:
|
||||
{
|
||||
switch (Status(m_status & 0xff))
|
||||
{
|
||||
case Status::SysEx:
|
||||
{
|
||||
uint32_t len;
|
||||
if (!_readContinuedValue(it, end, len) || end - it < len)
|
||||
return begin;
|
||||
break;
|
||||
default:
|
||||
it += length;
|
||||
}
|
||||
case Status::TimecodeQuarterFrame:
|
||||
} else
|
||||
{
|
||||
uint8_t chan = m_status & 0xf;
|
||||
auto& results = m_results[chan];
|
||||
|
||||
/* Not actually used as such for now */
|
||||
if (results.empty())
|
||||
_addProgramChange(chan, 0);
|
||||
std::multimap<int, Event>& res = results.back().second;
|
||||
|
||||
switch (Status(m_status & 0xf0))
|
||||
{
|
||||
case Status::NoteOff:
|
||||
{
|
||||
if (it == end)
|
||||
return begin;
|
||||
a = *it++;
|
||||
if (it == end)
|
||||
return begin;
|
||||
b = *it++;
|
||||
|
||||
uint8_t notenum = clamp7(a);
|
||||
std::multimap<int, Event>::iterator note = m_notes[chan][notenum];
|
||||
if (note != res.end())
|
||||
{
|
||||
note->second.length = m_tick - note->first;
|
||||
m_notes[chan][notenum] = res.end();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Status::SongPositionPointer:
|
||||
case Status::NoteOn:
|
||||
{
|
||||
if (it == end)
|
||||
return begin;
|
||||
a = *it++;
|
||||
if (it == end)
|
||||
return begin;
|
||||
b = *it++;
|
||||
|
||||
uint8_t notenum = clamp7(a);
|
||||
uint8_t vel = clamp7(b);
|
||||
std::multimap<int, Event>::iterator note = m_notes[chan][notenum];
|
||||
if (note != res.end())
|
||||
note->second.length = m_tick - note->first;
|
||||
|
||||
if (vel != 0)
|
||||
m_notes[chan][notenum] = res.emplace(m_tick, Event{NoteEvent{}, chan, notenum, vel, 0});
|
||||
else
|
||||
m_notes[chan][notenum] = res.end();
|
||||
|
||||
break;
|
||||
}
|
||||
case Status::NotePressure:
|
||||
{
|
||||
if (it == end)
|
||||
return begin;
|
||||
@@ -285,35 +225,100 @@ public:
|
||||
b = *it++;
|
||||
break;
|
||||
}
|
||||
case Status::SongSelect:
|
||||
case Status::ControlChange:
|
||||
{
|
||||
if (it == end)
|
||||
return begin;
|
||||
a = *it++;
|
||||
if (it == end)
|
||||
return begin;
|
||||
b = *it++;
|
||||
res.emplace(m_tick, Event{CtrlEvent{}, chan, clamp7(a), clamp7(b), 0});
|
||||
break;
|
||||
}
|
||||
case Status::ProgramChange:
|
||||
{
|
||||
if (it == end)
|
||||
return begin;
|
||||
a = *it++;
|
||||
res.emplace(m_tick, Event{ProgEvent{}, chan, a});
|
||||
break;
|
||||
}
|
||||
case Status::ChannelPressure:
|
||||
{
|
||||
if (it == end)
|
||||
return begin;
|
||||
a = *it++;
|
||||
break;
|
||||
}
|
||||
case Status::TuneRequest:
|
||||
case Status::Start:
|
||||
case Status::Continue:
|
||||
case Status::Stop:
|
||||
case Status::Reset:
|
||||
case Status::SysExTerm:
|
||||
case Status::TimingClock:
|
||||
case Status::ActiveSensing:
|
||||
case Status::PitchBend:
|
||||
{
|
||||
if (it == end)
|
||||
return begin;
|
||||
a = *it++;
|
||||
if (it == end)
|
||||
return begin;
|
||||
b = *it++;
|
||||
res.emplace(m_tick, Event{PitchEvent{}, chan, clamp7(b) * 128 + clamp7(a)});
|
||||
break;
|
||||
}
|
||||
case Status::SysEx:
|
||||
{
|
||||
switch (Status(m_status & 0xff))
|
||||
{
|
||||
case Status::SysEx:
|
||||
{
|
||||
uint32_t len;
|
||||
if (!_readContinuedValue(it, end, len) || end - it < len)
|
||||
return begin;
|
||||
break;
|
||||
}
|
||||
case Status::TimecodeQuarterFrame:
|
||||
{
|
||||
if (it == end)
|
||||
return begin;
|
||||
a = *it++;
|
||||
break;
|
||||
}
|
||||
case Status::SongPositionPointer:
|
||||
{
|
||||
if (it == end)
|
||||
return begin;
|
||||
a = *it++;
|
||||
if (it == end)
|
||||
return begin;
|
||||
b = *it++;
|
||||
break;
|
||||
}
|
||||
case Status::SongSelect:
|
||||
{
|
||||
if (it == end)
|
||||
return begin;
|
||||
a = *it++;
|
||||
break;
|
||||
}
|
||||
case Status::TuneRequest:
|
||||
case Status::Start:
|
||||
case Status::Continue:
|
||||
case Status::Stop:
|
||||
case Status::Reset:
|
||||
case Status::SysExTerm:
|
||||
case Status::TimingClock:
|
||||
case Status::ActiveSensing:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, std::multimap<int, Event>>>& getResults() { return m_results; }
|
||||
std::vector<std::pair<int, std::multimap<int, Event>>>& getResults(int chan) { return m_results[chan]; }
|
||||
std::multimap<int, int>& getTempos() { return m_tempos; }
|
||||
};
|
||||
|
||||
@@ -1031,8 +1036,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||
std::vector<uint8_t>::const_iterator end = it + length;
|
||||
|
||||
MIDIDecoder dec;
|
||||
while (begin != end)
|
||||
begin = dec.receiveBytes(begin, end);
|
||||
dec.receiveBytes(begin, end);
|
||||
|
||||
std::multimap<int, int>& tempos = dec.getTempos();
|
||||
if (tempos.size() == 1)
|
||||
@@ -1065,12 +1069,11 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||
it = end;
|
||||
|
||||
MIDIDecoder dec;
|
||||
while (begin != end)
|
||||
begin = dec.receiveBytes(begin, end);
|
||||
dec.receiveBytes(begin, end);
|
||||
|
||||
std::vector<std::pair<int, std::multimap<int, Event>>>& results = dec.getResults();
|
||||
for (int c = 0; c < 16; ++c)
|
||||
{
|
||||
std::vector<std::pair<int, std::multimap<int, Event>>>& results = dec.getResults(c);
|
||||
int lastTrackStartTick = 0;
|
||||
bool didChanInit = false;
|
||||
for (auto& prog : results)
|
||||
@@ -1177,20 +1180,21 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||
case Event::Type::Pitch:
|
||||
{
|
||||
int newPitch = event.second.pitchBend - 0x2000;
|
||||
EncodeDelta(region.modBuf, eventTick - lastPitchTick, newPitch - lastPitchVal);
|
||||
EncodeDelta(region.pitchBuf, eventTick - lastPitchTick, newPitch - lastPitchVal);
|
||||
lastPitchTick = eventTick;
|
||||
lastPitchVal = newPitch;
|
||||
break;
|
||||
}
|
||||
case Event::Type::Note:
|
||||
{
|
||||
int lenTicks = event.second.length * 384 / header.div;
|
||||
if (version == 1)
|
||||
{
|
||||
EncodeTime(region.eventBuf, uint32_t(eventTick - lastEventTick));
|
||||
lastEventTick = eventTick;
|
||||
region.eventBuf.push_back(event.second.noteOrCtrl);
|
||||
region.eventBuf.push_back(event.second.velOrVal);
|
||||
uint16_t lenBig = SBig(uint16_t(event.second.length));
|
||||
uint16_t lenBig = SBig(uint16_t(lenTicks));
|
||||
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&lenBig)[0]);
|
||||
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&lenBig)[1]);
|
||||
}
|
||||
@@ -1201,7 +1205,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||
uint32_t tickBig = SBig(uint32_t(eventTick - startTick));
|
||||
for (int i = 0; i < 4; ++i)
|
||||
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tickBig)[i]);
|
||||
uint16_t lenBig = SBig(uint16_t(event.second.length));
|
||||
uint16_t lenBig = SBig(uint16_t(lenTicks));
|
||||
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&lenBig)[0]);
|
||||
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&lenBig)[1]);
|
||||
region.eventBuf.push_back(event.second.noteOrCtrl);
|
||||
@@ -1212,7 +1216,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
||||
uint32_t tick = uint32_t(eventTick - startTick);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&tick)[i]);
|
||||
uint16_t len = uint16_t(event.second.length);
|
||||
uint16_t len = uint16_t(lenTicks);
|
||||
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&len)[0]);
|
||||
region.eventBuf.push_back(reinterpret_cast<const uint8_t*>(&len)[1]);
|
||||
region.eventBuf.push_back(event.second.noteOrCtrl);
|
||||
|
||||
@@ -106,8 +106,6 @@ void SongState::Track::Header::swapBig()
|
||||
SongState::Track::Track(SongState& parent, uint8_t midiChan, const TrackRegion* regions)
|
||||
: m_parent(&parent), m_midiChan(midiChan), m_curRegion(nullptr), m_nextRegion(regions)
|
||||
{
|
||||
for (int i = 0; i < 128; ++i)
|
||||
m_remNoteLengths[i] = std::numeric_limits<decltype(m_remNoteLengths)::value_type>::min();
|
||||
}
|
||||
|
||||
void SongState::Track::setRegion(Sequencer* seq, const TrackRegion* region)
|
||||
@@ -406,15 +404,11 @@ bool SongState::Track::advance(Sequencer& seq, int32_t ticks)
|
||||
/* Stop finished notes */
|
||||
for (int i = 0; i < 128; ++i)
|
||||
{
|
||||
constexpr decltype(m_remNoteLengths)::value_type MIN = std::numeric_limits<decltype(MIN)>::min();
|
||||
if (m_remNoteLengths[i] != MIN)
|
||||
if (m_remNoteLengths[i] > 0)
|
||||
{
|
||||
m_remNoteLengths[i] -= ticks;
|
||||
if (m_remNoteLengths[i] <= 0)
|
||||
{
|
||||
seq.keyOff(m_midiChan, i, 0);
|
||||
m_remNoteLengths[i] = MIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -280,7 +280,8 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdWaitTicks::Introspective =
|
||||
CmdType::Structure,
|
||||
"Wait Ticks"sv,
|
||||
"Suspend SoundMacro execution for specified length of time. Value of 65535 "
|
||||
"will wait indefinitely, relying on Key Off or Sample End to signal stop."sv,
|
||||
"will wait indefinitely, relying on Key Off or Sample End to signal stop. "
|
||||
"Absolute mode waits relative to the start of the SoundMacro."sv,
|
||||
{
|
||||
{
|
||||
FIELD_HEAD(SoundMacro::CmdWaitTicks, keyOff),
|
||||
@@ -442,7 +443,8 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdWaitMs::Introspective =
|
||||
CmdType::Structure,
|
||||
"Wait Millisec"sv,
|
||||
"Suspend SoundMacro execution for specified length of time. Value of 65535 "
|
||||
"will wait indefinitely, relying on Key Off or Sample End to signal stop."sv,
|
||||
"will wait indefinitely, relying on Key Off or Sample End to signal stop. "
|
||||
"Absolute mode waits relative to the start of the SoundMacro."sv,
|
||||
{
|
||||
{
|
||||
FIELD_HEAD(SoundMacro::CmdWaitMs, keyOff),
|
||||
@@ -1468,7 +1470,7 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdSetPitchAdsr::Introspective =
|
||||
{
|
||||
CmdType::Pitch,
|
||||
"Set Pitch ADSR"sv,
|
||||
"Define the pitch ADSR from a pool object. The pitch range is "
|
||||
"Define the pitch ADSR from a DLS ADSR pool object. The pitch range is "
|
||||
"specified using Note and Cents parameters."sv,
|
||||
{
|
||||
{
|
||||
|
||||
@@ -117,6 +117,7 @@ bool Voice::_checkSamplePos(bool& looped)
|
||||
|
||||
void Voice::_doKeyOff()
|
||||
{
|
||||
m_voxState = VoiceState::KeyOff;
|
||||
if (m_state.m_inWait && m_state.m_keyoffWait)
|
||||
{
|
||||
if (m_volAdsr.isAdsrSet() || m_state.m_useAdsrControllers)
|
||||
@@ -480,7 +481,7 @@ void Voice::preSupplyAudio(double dt)
|
||||
}
|
||||
if (m_pitchEnv)
|
||||
{
|
||||
newPitch *= m_pitchAdsr.advance(dt);
|
||||
newPitch += m_pitchAdsr.advance(dt) * m_pitchEnvRange;
|
||||
refresh = true;
|
||||
}
|
||||
|
||||
@@ -934,7 +935,6 @@ void Voice::_macroKeyOff()
|
||||
m_sustainKeyOff = true;
|
||||
else
|
||||
_doKeyOff();
|
||||
m_voxState = VoiceState::KeyOff;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1456,10 +1456,7 @@ void Voice::_notifyCtrlChange(uint8_t ctrl, int8_t val)
|
||||
{
|
||||
if (ctrl == 0x40)
|
||||
{
|
||||
if (val >= 0x40)
|
||||
setPedal(true);
|
||||
else
|
||||
setPedal(false);
|
||||
setPedal(val >= 0x40);
|
||||
}
|
||||
else if (ctrl == 0x5b)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user