mirror of https://github.com/AxioDL/amuse.git
588 lines
24 KiB
C++
588 lines
24 KiB
C++
#include "AudioGroupFilePresenter.hpp"
|
|
#include "VSTBackend.hpp"
|
|
#include <Shellapi.h>
|
|
#include <Shlwapi.h>
|
|
|
|
namespace amuse
|
|
{
|
|
|
|
static const wchar_t* const GMNames[128] = {L"Acoustic Grand Piano",
|
|
L"Bright Acoustic Piano",
|
|
L"Electric Grand Piano",
|
|
L"Honky-tonk Piano",
|
|
L"Rhodes Piano",
|
|
L"Chorused Piano",
|
|
L"Harpsichord",
|
|
L"Clavinet",
|
|
L"Celesta",
|
|
L"Glockenspiel",
|
|
L"Music Box",
|
|
L"Vibraphone",
|
|
L"Marimba",
|
|
L"Xylophone",
|
|
L"Tubular Bells",
|
|
L"Dulcimer",
|
|
L"Drawbar Organ",
|
|
L"Percussive Organ",
|
|
L"Rock Organ",
|
|
L"Church Organ",
|
|
L"Reed Organ",
|
|
L"Accordion",
|
|
L"Harmonica",
|
|
L"Tango Accordion",
|
|
L"Acoustic Guitar (nylon)",
|
|
L"Acoustic Guitar (steel)",
|
|
L"Electric Guitar (jazz)",
|
|
L"Electric Guitar (clean)",
|
|
L"Electric Guitar (muted)",
|
|
L"Overdriven Guitar",
|
|
L"Distortion Guitar",
|
|
L"Guitar Harmonics",
|
|
L"Acoustic Bass",
|
|
L"Electric Bass (finger)",
|
|
L"Electric Bass (pick)",
|
|
L"Fretless Bass",
|
|
L"Slap Bass 1",
|
|
L"Slap Bass 2",
|
|
L"Synth Bass 1",
|
|
L"Synth Bass 2",
|
|
L"Violin",
|
|
L"Viola",
|
|
L"Cello",
|
|
L"Contrabass",
|
|
L"Tremelo Strings",
|
|
L"Pizzicato Strings",
|
|
L"Orchestral Harp",
|
|
L"Timpani",
|
|
L"String Ensemble 1",
|
|
L"String Ensemble 2",
|
|
L"SynthStrings 1",
|
|
L"SynthStrings 2",
|
|
L"Choir Aahs",
|
|
L"Voice Oohs",
|
|
L"Synth Voice",
|
|
L"Orchestra Hit",
|
|
L"Trumpet",
|
|
L"Trombone",
|
|
L"Tuba",
|
|
L"Muted Trumpet",
|
|
L"French Horn",
|
|
L"Brass Section",
|
|
L"Synth Brass 1",
|
|
L"Synth Brass 2",
|
|
L"Soprano Sax",
|
|
L"Alto Sax",
|
|
L"Tenor Sax",
|
|
L"Baritone Sax",
|
|
L"Oboe",
|
|
L"English Horn",
|
|
L"Bassoon",
|
|
L"Clarinet",
|
|
L"Piccolo",
|
|
L"Flute",
|
|
L"Recorder",
|
|
L"Pan Flute",
|
|
L"Bottle Blow",
|
|
L"Shakuhachi",
|
|
L"Whistle",
|
|
L"Ocarina",
|
|
L"Lead 1 (square)",
|
|
L"Lead 2 (sawtooth)",
|
|
L"Lead 3 (calliope lead)",
|
|
L"Lead 4 (chiff lead)",
|
|
L"Lead 5 (charang)",
|
|
L"Lead 6 (voice)",
|
|
L"Lead 7 (fifths)",
|
|
L"Lead 8 (bass + lead)",
|
|
L"Pad 1 (new age)",
|
|
L"Pad 2 (warm)",
|
|
L"Pad 3 (polysynth)",
|
|
L"Pad 4 (choir)",
|
|
L"Pad 5 (bowed)",
|
|
L"Pad 6 (metallic)",
|
|
L"Pad 7 (halo)",
|
|
L"Pad 8 (sweep)",
|
|
L"FX 1 (rain)",
|
|
L"FX 2 (soundtrack)",
|
|
L"FX 3 (crystal)",
|
|
L"FX 4 (atmosphere)",
|
|
L"FX 5 (brightness)",
|
|
L"FX 6 (goblins)",
|
|
L"FX 7 (echoes)",
|
|
L"FX 8 (sci-fi)",
|
|
L"Sitar",
|
|
L"Banjo",
|
|
L"Shamisen",
|
|
L"Koto",
|
|
L"Kalimba",
|
|
L"Bagpipe",
|
|
L"Fiddle",
|
|
L"Shanai",
|
|
L"Tinkle Bell",
|
|
L"Agogo",
|
|
L"Steel Drums",
|
|
L"Woodblock",
|
|
L"Taiko Drum",
|
|
L"Melodic Tom",
|
|
L"Synth Drum",
|
|
L"Reverse Cymbal",
|
|
L"Guitar Fret Noise",
|
|
L"Breath Noise",
|
|
L"Seashore",
|
|
L"Bird Tweet",
|
|
L"Telephone Ring",
|
|
L"Helicopter",
|
|
L"Applause",
|
|
L"Gunshot"};
|
|
|
|
static const wchar_t* const GMPercNames[128] = {
|
|
nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
nullptr, nullptr, nullptr, nullptr, L"Acoustic Bass Drum",
|
|
L"Bass Drum 1", L"Side Stick", L"Acoustic Snare", L"Hand Clap", L"Electric Snare",
|
|
L"Low Floor Tom", L"Closed Hi-Hat", L"High Floor Tom", L"Pedal Hi-Hat", L"Low Tom",
|
|
L"Open Hi-Hat", L"Low-Mid Tom", L"Hi-Mid Tom", L"Crash Cymbal 1", L"High Tom",
|
|
L"Ride Cymbal 1", L"Chinese Cymbal", L"Ride Bell", L"Tambourine", L"Splash Cymbal",
|
|
L"Cowbell", L"Crash Cymbal 2", L"Vibraslap", L"Ride Cymbal 2", L"Hi Bongo",
|
|
L"Low Bongo", L"Mute Hi Conga", L"Open Hi Conga", L"Low Conga", L"High Timbale",
|
|
L"Low Timbale", L"High Agogo", L"Low Agogo", L"Cabasa", L"Maracas",
|
|
L"Short Whistle", L"Long Whistle", L"Short Guiro", L"Long Guiro", L"Claves",
|
|
L"Hi Wood Block", L"Low Wood Block", L"Mute Cuica", L"Open Cuica", L"Mute Triangle",
|
|
L"Open Triangle"};
|
|
|
|
bool AudioGroupDataCollection::loadProj()
|
|
{
|
|
std::wstring path = m_path + L"\\proj";
|
|
athena::io::FileReader r(path, 1024 * 32, false);
|
|
if (r.hasError())
|
|
return false;
|
|
std::vector<uint8_t>& ret = m_projData;
|
|
size_t len = r.length();
|
|
ret.resize(len);
|
|
r.readUBytesToBuf(ret.data(), len);
|
|
return ret.size() != 0;
|
|
}
|
|
|
|
bool AudioGroupDataCollection::loadPool()
|
|
{
|
|
std::wstring path = m_path + L"\\pool";
|
|
athena::io::FileReader r(path, 1024 * 32, false);
|
|
if (r.hasError())
|
|
return false;
|
|
std::vector<uint8_t>& ret = m_poolData;
|
|
size_t len = r.length();
|
|
ret.resize(len);
|
|
r.readUBytesToBuf(ret.data(), len);
|
|
return ret.size() != 0;
|
|
}
|
|
|
|
bool AudioGroupDataCollection::loadSdir()
|
|
{
|
|
std::wstring path = m_path + L"\\sdir";
|
|
athena::io::FileReader r(path, 1024 * 32, false);
|
|
if (r.hasError())
|
|
return false;
|
|
std::vector<uint8_t>& ret = m_sdirData;
|
|
size_t len = r.length();
|
|
ret.resize(len);
|
|
r.readUBytesToBuf(ret.data(), len);
|
|
return ret.size() != 0;
|
|
}
|
|
|
|
bool AudioGroupDataCollection::loadSamp()
|
|
{
|
|
std::wstring path = m_path + L"\\samp";
|
|
athena::io::FileReader r(path, 1024 * 32, false);
|
|
if (r.hasError())
|
|
return false;
|
|
std::vector<uint8_t>& ret = m_sampData;
|
|
size_t len = r.length();
|
|
ret.resize(len);
|
|
r.readUBytesToBuf(ret.data(), len);
|
|
return ret.size() != 0;
|
|
}
|
|
|
|
bool AudioGroupDataCollection::loadMeta()
|
|
{
|
|
std::wstring path = m_path + L"\\meta";
|
|
athena::io::FileReader r(path, 1024 * 32, false);
|
|
if (r.hasError())
|
|
return false;
|
|
std::experimental::optional<MetaData>& ret = m_metaData;
|
|
ret.emplace(r);
|
|
return ret.operator bool();
|
|
}
|
|
|
|
AudioGroupDataCollection::AudioGroupDataCollection(std::wstring_view path, std::wstring_view name)
|
|
: m_path(path), m_name(name)
|
|
{
|
|
}
|
|
|
|
bool AudioGroupDataCollection::_attemptLoad()
|
|
{
|
|
if (m_metaData && m_loadedData && m_loadedGroup)
|
|
return true;
|
|
if (!loadProj())
|
|
return false;
|
|
if (!loadPool())
|
|
return false;
|
|
if (!loadSdir())
|
|
return false;
|
|
if (!loadSamp())
|
|
return false;
|
|
if (!loadMeta())
|
|
return false;
|
|
|
|
return _indexData();
|
|
}
|
|
|
|
bool AudioGroupDataCollection::_indexData()
|
|
{
|
|
switch (m_metaData->fmt)
|
|
{
|
|
case amuse::DataFormat::GCN:
|
|
default:
|
|
m_loadedData.emplace(m_projData.data(), m_projData.size(), m_poolData.data(), m_poolData.size(),
|
|
m_sdirData.data(), m_sdirData.size(), m_sampData.data(), m_sampData.size(),
|
|
amuse::GCNDataTag{});
|
|
break;
|
|
case amuse::DataFormat::N64:
|
|
m_loadedData.emplace(m_projData.data(), m_projData.size(), m_poolData.data(), m_poolData.size(),
|
|
m_sdirData.data(), m_sdirData.size(), m_sampData.data(), m_sampData.size(),
|
|
m_metaData->absOffs, amuse::N64DataTag{});
|
|
break;
|
|
case amuse::DataFormat::PC:
|
|
m_loadedData.emplace(m_projData.data(), m_projData.size(), m_poolData.data(), m_poolData.size(),
|
|
m_sdirData.data(), m_sdirData.size(), m_sampData.data(), m_sampData.size(),
|
|
m_metaData->absOffs, amuse::PCDataTag{});
|
|
break;
|
|
}
|
|
|
|
return m_loadedData.operator bool();
|
|
}
|
|
|
|
void AudioGroupDataCollection::addToEngine(amuse::Engine& engine)
|
|
{
|
|
m_loadedGroup = engine.addAudioGroup(*m_loadedData);
|
|
m_groupTokens.clear();
|
|
if (m_loadedGroup)
|
|
{
|
|
m_groupTokens.reserve(m_loadedGroup->getProj().songGroups().size() +
|
|
m_loadedGroup->getProj().sfxGroups().size());
|
|
|
|
{
|
|
const auto& songGroups = m_loadedGroup->getProj().songGroups();
|
|
std::map<int, const amuse::SongGroupIndex*> sortGroups;
|
|
for (const auto& pair : songGroups)
|
|
sortGroups[pair.first] = &pair.second;
|
|
for (const auto& pair : sortGroups)
|
|
m_groupTokens.emplace_back(pair.first, pair.second);
|
|
}
|
|
{
|
|
const auto& sfxGroups = m_loadedGroup->getProj().sfxGroups();
|
|
std::map<int, const amuse::SFXGroupIndex*> sortGroups;
|
|
for (const auto& pair : sfxGroups)
|
|
sortGroups[pair.first] = &pair.second;
|
|
for (const auto& pair : sortGroups)
|
|
m_groupTokens.emplace_back(pair.first, pair.second);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AudioGroupDataCollection::removeFromEngine(amuse::Engine& engine) const { engine.removeAudioGroup(*m_loadedData); }
|
|
|
|
AudioGroupCollection::AudioGroupCollection(std::wstring_view path, std::wstring_view name)
|
|
: m_path(path), m_name(name)
|
|
{
|
|
}
|
|
|
|
void AudioGroupCollection::addCollection(
|
|
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection)
|
|
{
|
|
for (std::pair<std::wstring, amuse::IntrusiveAudioGroupData>& pair : collection)
|
|
{
|
|
std::wstring collectionPath = m_path + L'\\' + pair.first;
|
|
|
|
amuse::IntrusiveAudioGroupData& dataIn = pair.second;
|
|
auto search = m_groups.find(pair.first);
|
|
if (search == m_groups.end())
|
|
{
|
|
search =
|
|
m_groups.emplace(pair.first, std::make_unique<AudioGroupDataCollection>(collectionPath, pair.first))
|
|
.first;
|
|
}
|
|
|
|
AudioGroupDataCollection& dataCollection = *search->second;
|
|
dataCollection.m_projData.resize(dataIn.getProjSize());
|
|
memmove(dataCollection.m_projData.data(), dataIn.getProj(), dataIn.getProjSize());
|
|
|
|
dataCollection.m_poolData.resize(dataIn.getPoolSize());
|
|
memmove(dataCollection.m_poolData.data(), dataIn.getPool(), dataIn.getPoolSize());
|
|
|
|
dataCollection.m_sdirData.resize(dataIn.getSdirSize());
|
|
memmove(dataCollection.m_sdirData.data(), dataIn.getSdir(), dataIn.getSdirSize());
|
|
|
|
dataCollection.m_sampData.resize(dataIn.getSampSize());
|
|
memmove(dataCollection.m_sampData.data(), dataIn.getSamp(), dataIn.getSampSize());
|
|
|
|
dataCollection.m_metaData.emplace(dataIn.getDataFormat(), dataIn.getAbsoluteProjOffsets(), true);
|
|
dataCollection._indexData();
|
|
}
|
|
}
|
|
|
|
void AudioGroupCollection::update(AudioGroupFilePresenter& presenter)
|
|
{
|
|
std::wstring path = m_path + L"\\*";
|
|
|
|
WIN32_FIND_DATAW d;
|
|
HANDLE dir = FindFirstFileW(path.c_str(), &d);
|
|
if (dir == INVALID_HANDLE_VALUE)
|
|
return;
|
|
do
|
|
{
|
|
if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L".."))
|
|
continue;
|
|
|
|
if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
std::wstring nameStr(d.cFileName);
|
|
auto search = m_groups.find(nameStr);
|
|
if (search == m_groups.end())
|
|
{
|
|
search =
|
|
m_groups
|
|
.emplace(nameStr, std::make_unique<AudioGroupDataCollection>(m_path + L'\\' + nameStr, nameStr))
|
|
.first;
|
|
search->second->_attemptLoad();
|
|
}
|
|
}
|
|
} while (FindNextFileW(dir, &d));
|
|
|
|
FindClose(dir);
|
|
}
|
|
|
|
void AudioGroupFilePresenter::update()
|
|
{
|
|
std::wstring path = m_backend.getUserDir() + L"\\*";
|
|
std::map<std::wstring, std::unique_ptr<AudioGroupCollection>>& theMap = m_audioGroupCollections;
|
|
|
|
WIN32_FIND_DATAW d;
|
|
HANDLE dir = FindFirstFileW(path.c_str(), &d);
|
|
if (dir == INVALID_HANDLE_VALUE)
|
|
return;
|
|
do
|
|
{
|
|
if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L".."))
|
|
continue;
|
|
|
|
if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
std::wstring nameStr(d.cFileName);
|
|
auto search = theMap.find(nameStr);
|
|
if (search == theMap.end())
|
|
{
|
|
search = theMap
|
|
.emplace(nameStr, std::make_unique<AudioGroupCollection>(
|
|
m_backend.getUserDir() + L'\\' + nameStr, nameStr))
|
|
.first;
|
|
search->second->update(*this);
|
|
}
|
|
}
|
|
} while (FindNextFileW(dir, &d));
|
|
|
|
FindClose(dir);
|
|
}
|
|
|
|
void AudioGroupFilePresenter::addCollection(
|
|
std::wstring_view name, std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection)
|
|
{
|
|
std::wstring path = m_backend.getUserDir() + L'\\' + name;
|
|
AudioGroupCollection& insert =
|
|
*m_audioGroupCollections.emplace(name, std::make_unique<AudioGroupCollection>(path, name)).first->second;
|
|
CreateDirectory(insert.m_path.c_str(), nullptr);
|
|
insert.addCollection(std::move(collection));
|
|
|
|
for (std::pair<const std::wstring, std::unique_ptr<AudioGroupDataCollection>>& pair : insert.m_groups)
|
|
{
|
|
std::wstring collectionPath = insert.m_path + L'\\' + pair.first;
|
|
CreateDirectory(collectionPath.c_str(), nullptr);
|
|
|
|
FILE* fp = _wfopen((collectionPath + L"\\proj").c_str(), L"wb");
|
|
if (fp)
|
|
{
|
|
fwrite(pair.second->m_projData.data(), 1, pair.second->m_projData.size(), fp);
|
|
fclose(fp);
|
|
}
|
|
|
|
fp = _wfopen((collectionPath + L"\\pool").c_str(), L"wb");
|
|
if (fp)
|
|
{
|
|
fwrite(pair.second->m_poolData.data(), 1, pair.second->m_poolData.size(), fp);
|
|
fclose(fp);
|
|
}
|
|
|
|
fp = _wfopen((collectionPath + L"\\sdir").c_str(), L"wb");
|
|
if (fp)
|
|
{
|
|
fwrite(pair.second->m_sdirData.data(), 1, pair.second->m_sdirData.size(), fp);
|
|
fclose(fp);
|
|
}
|
|
|
|
fp = _wfopen((collectionPath + L"\\samp").c_str(), L"wb");
|
|
if (fp)
|
|
{
|
|
fwrite(pair.second->m_sampData.data(), 1, pair.second->m_sampData.size(), fp);
|
|
fclose(fp);
|
|
}
|
|
|
|
fp = _wfopen((collectionPath + L"\\meta").c_str(), L"wb");
|
|
if (fp)
|
|
{
|
|
fwrite(&*pair.second->m_metaData, 1, sizeof(*pair.second->m_metaData), fp);
|
|
fclose(fp);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AudioGroupFilePresenter::removeCollection(unsigned idx)
|
|
{
|
|
if (idx < m_iteratorVec.size())
|
|
{
|
|
CollectionIterator& it = m_iteratorVec[idx];
|
|
std::wstring collectionPath = it->second->m_path + L'\0';
|
|
SHFILEOPSTRUCT op = {};
|
|
op.wFunc = FO_DELETE;
|
|
op.pFrom = collectionPath.c_str();
|
|
op.fFlags = FOF_NO_UI;
|
|
SHFileOperation(&op);
|
|
m_audioGroupCollections.erase(it);
|
|
}
|
|
}
|
|
|
|
void AudioGroupCollection::populateFiles(VSTEditor& editor, HTREEITEM colHandle, size_t parentIdx)
|
|
{
|
|
TVINSERTSTRUCT ins = {};
|
|
ins.item.mask = TVIF_TEXT | TVIF_PARAM;
|
|
ins.hParent = colHandle;
|
|
ins.hInsertAfter = TVI_LAST;
|
|
|
|
m_iteratorVec.clear();
|
|
m_iteratorVec.reserve(m_groups.size());
|
|
for (auto it = m_groups.begin(); it != m_groups.end(); ++it)
|
|
{
|
|
ins.item.pszText = LPWSTR(it->first.c_str());
|
|
ins.item.lParam = LPARAM(0x80000000 | (parentIdx << 16) | m_iteratorVec.size());
|
|
HTREEITEM item = TreeView_InsertItem(editor.m_collectionTree, &ins);
|
|
if (editor.m_selCollectionIdx == parentIdx && editor.m_selFileIdx == m_iteratorVec.size())
|
|
editor.m_deferredCollectionSel = item;
|
|
m_iteratorVec.push_back(it);
|
|
}
|
|
}
|
|
|
|
void AudioGroupFilePresenter::populateCollectionColumn(VSTEditor& editor)
|
|
{
|
|
TreeView_DeleteAllItems(editor.m_collectionTree);
|
|
TVINSERTSTRUCT ins = {};
|
|
ins.hParent = TVI_ROOT;
|
|
ins.hInsertAfter = TVI_LAST;
|
|
ins.item.mask = TVIF_CHILDREN | TVIF_TEXT | TVIF_PARAM;
|
|
|
|
m_iteratorVec.clear();
|
|
m_iteratorVec.reserve(m_audioGroupCollections.size());
|
|
for (auto it = m_audioGroupCollections.begin(); it != m_audioGroupCollections.end(); ++it)
|
|
{
|
|
ins.item.cChildren = it->second->m_groups.size() ? 1 : 0;
|
|
ins.item.pszText = LPWSTR(it->first.c_str());
|
|
ins.item.lParam = LPARAM(m_iteratorVec.size() << 16);
|
|
HTREEITEM item = TreeView_InsertItem(editor.m_collectionTree, &ins);
|
|
it->second->populateFiles(editor, item, m_iteratorVec.size());
|
|
if (editor.m_selCollectionIdx == m_iteratorVec.size() && editor.m_selFileIdx == -1)
|
|
editor.m_deferredCollectionSel = item;
|
|
m_iteratorVec.push_back(it);
|
|
}
|
|
}
|
|
|
|
void AudioGroupFilePresenter::populateGroupColumn(VSTEditor& editor, int collectionIdx, int fileIdx)
|
|
{
|
|
LVITEM item = {};
|
|
item.mask = LVIF_TEXT;
|
|
|
|
ListView_DeleteAllItems(editor.m_groupListView);
|
|
ListView_DeleteAllItems(editor.m_pageListView);
|
|
if (collectionIdx < m_iteratorVec.size())
|
|
{
|
|
CollectionIterator& it = m_iteratorVec[collectionIdx];
|
|
if (fileIdx < it->second->m_iteratorVec.size())
|
|
{
|
|
size_t idx = 0;
|
|
AudioGroupCollection::GroupIterator& git = it->second->m_iteratorVec[fileIdx];
|
|
for (AudioGroupDataCollection::GroupToken& gtok : git->second->m_groupTokens)
|
|
{
|
|
wchar_t name[256];
|
|
wnsprintf(name, 256, L"%d (%s)", gtok.m_groupId, gtok.m_song ? L"Song" : L"SFX");
|
|
item.pszText = name;
|
|
item.iItem = idx++;
|
|
ListView_InsertItem(editor.m_groupListView, &item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AudioGroupFilePresenter::populatePageColumn(VSTEditor& editor, int collectionIdx, int fileIdx, int groupIdx)
|
|
{
|
|
LVITEM item = {};
|
|
item.mask = LVIF_TEXT | LVIF_PARAM;
|
|
|
|
ListView_DeleteAllItems(editor.m_pageListView);
|
|
if (collectionIdx < m_iteratorVec.size())
|
|
{
|
|
CollectionIterator& it = m_iteratorVec[collectionIdx];
|
|
if (fileIdx < it->second->m_iteratorVec.size())
|
|
{
|
|
AudioGroupCollection::GroupIterator& git = it->second->m_iteratorVec[fileIdx];
|
|
if (groupIdx < git->second->m_groupTokens.size())
|
|
{
|
|
AudioGroupDataCollection::GroupToken& groupTok = git->second->m_groupTokens[groupIdx];
|
|
if (groupTok.m_song)
|
|
{
|
|
std::map<uint8_t, const amuse::SongGroupIndex::PageEntry*> sortPages;
|
|
for (auto& pair : groupTok.m_song->m_normPages)
|
|
sortPages[pair.first] = pair.second;
|
|
|
|
size_t idx = 0;
|
|
for (auto& pair : sortPages)
|
|
{
|
|
wchar_t name[256];
|
|
wnsprintf(name, 256, L"%d (%s)", pair.first,
|
|
GMNames[pair.first] ? GMNames[pair.first] : L"???");
|
|
item.pszText = name;
|
|
item.iItem = idx++;
|
|
item.lParam = pair.first;
|
|
ListView_InsertItem(editor.m_pageListView, &item);
|
|
}
|
|
|
|
sortPages.clear();
|
|
for (auto& pair : groupTok.m_song->m_drumPages)
|
|
sortPages[pair.first] = pair.second;
|
|
|
|
for (auto& pair : sortPages)
|
|
{
|
|
wchar_t name[256];
|
|
wnsprintf(name, 256, L"%d (%s)", pair.first,
|
|
GMPercNames[pair.first] ? GMPercNames[pair.first] : L"???");
|
|
item.pszText = name;
|
|
item.iItem = idx++;
|
|
item.lParam = 0x80000000 | pair.first;
|
|
ListView_InsertItem(editor.m_pageListView, &item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|