Clipboard support and various bug fixes

This commit is contained in:
Jack Andersen
2018-08-24 22:34:04 -10:00
parent cefa0ac18c
commit 27cdee0c14
38 changed files with 3022 additions and 854 deletions

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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<> \

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}
}
}

View File

@@ -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,
{
{

View File

@@ -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)
{