mirror of
https://github.com/AxioDL/amuse.git
synced 2025-07-04 12:15:57 +00:00
Merge branch 'master' of https://github.com/AxioDL/amuse
This commit is contained in:
commit
b140b245d7
@ -23,7 +23,7 @@ game archives or raw (`.proj`,`.pool`,`.sdir`,`.samp`) files is provided.
|
|||||||
░░░ │ │ ┃ │ │ │ ┃ │ │ ░░░
|
░░░ │ │ ┃ │ │ │ ┃ │ │ ░░░
|
||||||
░░░ A │ S │ D ┃ F │ G │ H │ J ┃ K │ L │ ; ░░░
|
░░░ A │ S │ D ┃ F │ G │ H │ J ┃ K │ L │ ; ░░░
|
||||||
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
<left/right>: cycle MIDI setup / channel, <up/down>: volume, <space>: PANIC
|
<left/right>: cycle MIDI setup, <up/down>: volume, <space>: PANIC
|
||||||
<tab>: sustain pedal, <window-Y>: pitch wheel, <window-X>: mod wheel
|
<tab>: sustain pedal, <window-Y>: pitch wheel, <window-X>: mod wheel
|
||||||
<Z/X>: octave, <C/V>: velocity, <B/N>: channel, <,/.>: program, <Q>: quit
|
<Z/X>: octave, <C/V>: velocity, <B/N>: channel, <,/.>: program, <Q>: quit
|
||||||
0 Setup 0, Chan 0, Prog 0, Octave: 4, Vel: 64, VOL: 80%
|
0 Setup 0, Chan 0, Prog 0, Octave: 4, Vel: 64, VOL: 80%
|
||||||
|
330
driver/main.cpp
330
driver/main.cpp
@ -123,7 +123,7 @@ struct AppCallback : boo::IApplicationCallback
|
|||||||
int8_t m_octave = 4;
|
int8_t m_octave = 4;
|
||||||
int8_t m_velocity = 64;
|
int8_t m_velocity = 64;
|
||||||
std::shared_ptr<amuse::Sequencer> m_seq;
|
std::shared_ptr<amuse::Sequencer> m_seq;
|
||||||
std::unique_ptr<uint8_t[]> m_arrData;
|
amuse::ContainerRegistry::SongData* m_arrData = nullptr;
|
||||||
|
|
||||||
/* SFX playback selection */
|
/* SFX playback selection */
|
||||||
int m_sfxId = -1;
|
int m_sfxId = -1;
|
||||||
@ -132,13 +132,14 @@ struct AppCallback : boo::IApplicationCallback
|
|||||||
int8_t m_lastChanProg = -1;
|
int8_t m_lastChanProg = -1;
|
||||||
|
|
||||||
/* Control state */
|
/* Control state */
|
||||||
float m_volume = 0.8f;
|
float m_volume = 0.5f;
|
||||||
float m_modulation = 0.f;
|
float m_modulation = 0.f;
|
||||||
float m_pitchBend = 0.f;
|
float m_pitchBend = 0.f;
|
||||||
bool m_updateDisp = false;
|
bool m_updateDisp = false;
|
||||||
bool m_running = true;
|
bool m_running = true;
|
||||||
bool m_wantsNext = false;
|
bool m_wantsNext = false;
|
||||||
bool m_wantsPrev = false;
|
bool m_wantsPrev = false;
|
||||||
|
bool m_breakout = false;
|
||||||
int m_panicCount = 0;
|
int m_panicCount = 0;
|
||||||
|
|
||||||
void UpdateSongDisplay()
|
void UpdateSongDisplay()
|
||||||
@ -168,7 +169,7 @@ struct AppCallback : boo::IApplicationCallback
|
|||||||
m_seq->setVolume(m_volume);
|
m_seq->setVolume(m_volume);
|
||||||
|
|
||||||
if (m_arrData)
|
if (m_arrData)
|
||||||
m_seq->playSong(m_arrData.get(), false);
|
m_seq->playSong(m_arrData->m_data.get(), false);
|
||||||
|
|
||||||
UpdateSongDisplay();
|
UpdateSongDisplay();
|
||||||
}
|
}
|
||||||
@ -182,7 +183,7 @@ struct AppCallback : boo::IApplicationCallback
|
|||||||
"░░░ │ │ ┃ │ │ │ ┃ │ │ ░░░\n"
|
"░░░ │ │ ┃ │ │ │ ┃ │ │ ░░░\n"
|
||||||
"░░░ A │ S │ D ┃ F │ G │ H │ J ┃ K │ L │ ; ░░░\n"
|
"░░░ A │ S │ D ┃ F │ G │ H │ J ┃ K │ L │ ; ░░░\n"
|
||||||
"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░\n"
|
"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░\n"
|
||||||
"<left/right>: cycle MIDI setup / channel, <up/down>: volume, <space>: PANIC\n"
|
"<left/right>: cycle MIDI setup, <up/down>: volume, <space>: PANIC\n"
|
||||||
"<tab>: sustain pedal, <window-Y>: pitch wheel, <window-X>: mod wheel\n"
|
"<tab>: sustain pedal, <window-Y>: pitch wheel, <window-X>: mod wheel\n"
|
||||||
"<Z/X>: octave, <C/V>: velocity, <B/N>: channel, <,/.>: program, <Q>: quit\n");
|
"<Z/X>: octave, <C/V>: velocity, <B/N>: channel, <,/.>: program, <Q>: quit\n");
|
||||||
|
|
||||||
@ -271,6 +272,14 @@ struct AppCallback : boo::IApplicationCallback
|
|||||||
UpdateSongDisplay();
|
UpdateSongDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_breakout)
|
||||||
|
{
|
||||||
|
m_breakout = false;
|
||||||
|
m_seq->allOff(true);
|
||||||
|
m_seq.reset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
m_win->waitForRetrace();
|
m_win->waitForRetrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -350,6 +359,13 @@ struct AppCallback : boo::IApplicationCallback
|
|||||||
UpdateSFXDisplay();
|
UpdateSFXDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_breakout)
|
||||||
|
{
|
||||||
|
m_breakout = false;
|
||||||
|
m_vox.reset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
m_win->waitForRetrace();
|
m_win->waitForRetrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -665,154 +681,186 @@ struct AppCallback : boo::IApplicationCallback
|
|||||||
allSFXGroups[it->first] = std::make_pair(&grp, &it->second);
|
allSFXGroups[it->first] = std::make_pair(&grp, &it->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get group selection from user */
|
while (m_running)
|
||||||
if (m_groupId != -1)
|
|
||||||
{
|
{
|
||||||
if (allSongGroups.find(m_groupId) != allSongGroups.end())
|
m_groupId = -1;
|
||||||
m_sfxGroup = false;
|
m_setupId = -1;
|
||||||
else if (allSFXGroups.find(m_groupId) != allSFXGroups.end())
|
|
||||||
m_sfxGroup = true;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Error, "unable to find Group %d", m_groupId);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (totalGroups > 1)
|
|
||||||
{
|
|
||||||
/* Ask user to specify which group in project */
|
|
||||||
printf("Multiple Audio Groups discovered:\n");
|
|
||||||
for (const auto& pair :allSFXGroups)
|
|
||||||
{
|
|
||||||
printf(" %d %s (SFXGroup) %" PRISize " sfx-entries\n",
|
|
||||||
pair.first, pair.second.first->first.c_str(),
|
|
||||||
pair.second.second->m_sfxEntries.size());
|
|
||||||
}
|
|
||||||
for (const auto& pair : allSongGroups)
|
|
||||||
{
|
|
||||||
printf(" %d %s (SongGroup) %" PRISize " normal-pages, %" PRISize " drum-pages\n",
|
|
||||||
pair.first, pair.second.first->first.c_str(),
|
|
||||||
pair.second.second->m_normPages.size(), pair.second.second->m_drumPages.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
int userSel = 0;
|
/* Attempt loading song */
|
||||||
printf("Enter Group Number: ");
|
std::vector<std::pair<std::string, amuse::ContainerRegistry::SongData>> songs;
|
||||||
if (scanf("%d", &userSel) <= 0)
|
if (m_argc > 2)
|
||||||
{
|
{
|
||||||
Log.report(logvisor::Error, "unable to parse prompt");
|
#if _WIN32
|
||||||
exit(1);
|
char utf8Path[1024];
|
||||||
}
|
WideCharToMultiByte(CP_UTF8, 0, m_argv[2], -1, utf8Path, 1024, nullptr, nullptr);
|
||||||
|
#else
|
||||||
|
const char* utf8Path = m_argv[2];
|
||||||
|
#endif
|
||||||
|
|
||||||
if (allSongGroups.find(userSel) != allSongGroups.end())
|
songs = amuse::ContainerRegistry::LoadSongs(utf8Path);
|
||||||
{
|
|
||||||
m_groupId = userSel;
|
|
||||||
m_sfxGroup = false;
|
|
||||||
}
|
|
||||||
else if (allSFXGroups.find(userSel) != allSFXGroups.end())
|
|
||||||
{
|
|
||||||
m_groupId = userSel;
|
|
||||||
m_sfxGroup = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Error, "unable to find Group %d", userSel);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (totalGroups == 1)
|
|
||||||
{
|
|
||||||
/* Load one and only group */
|
|
||||||
if (allSongGroups.size())
|
|
||||||
{
|
|
||||||
const auto& pair = *allSongGroups.cbegin();
|
|
||||||
m_groupId = pair.first;
|
|
||||||
m_sfxGroup = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto& pair = *allSFXGroups.cbegin();
|
|
||||||
m_groupId = pair.first;
|
|
||||||
m_sfxGroup = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Error, "empty project");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make final group selection */
|
/* Get song selection from user */
|
||||||
amuse::IntrusiveAudioGroupData* selData = nullptr;
|
if (songs.size() > 1)
|
||||||
const amuse::SongGroupIndex* songIndex = nullptr;
|
|
||||||
const amuse::SFXGroupIndex* sfxIndex = nullptr;
|
|
||||||
auto songSearch = allSongGroups.find(m_groupId);
|
|
||||||
if (songSearch != allSongGroups.end())
|
|
||||||
{
|
|
||||||
selData = &songSearch->second.first->second;
|
|
||||||
songIndex = songSearch->second.second;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto sfxSearch = allSFXGroups.find(m_groupId);
|
|
||||||
if (sfxSearch != allSFXGroups.end())
|
|
||||||
{
|
|
||||||
selData = &sfxSearch->second.first->second;
|
|
||||||
sfxIndex = sfxSearch->second.second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!selData)
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Error, "unable to select audio group data");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Attempt loading song */
|
|
||||||
if (m_argc > 2)
|
|
||||||
{
|
|
||||||
std::experimental::optional<athena::io::FileReader> r;
|
|
||||||
r.emplace(m_argv[m_argc-1], 32 * 1024, false);
|
|
||||||
if (!r->hasError())
|
|
||||||
{
|
|
||||||
uint32_t version = r->readUint32Big();
|
|
||||||
if (version == 0x18)
|
|
||||||
{
|
{
|
||||||
/* Raw SON data */
|
/* Ask user to specify which song */
|
||||||
r->seek(0, athena::SeekOrigin::Begin);
|
printf("Multiple Songs discovered:\n");
|
||||||
m_arrData = r->readUBytes(r->length());
|
int idx = 0;
|
||||||
|
for (const auto& pair : songs)
|
||||||
|
{
|
||||||
|
printf(" %d %s (Group %d, Setup %d)\n", idx++,
|
||||||
|
pair.first.c_str(), pair.second.m_groupId, pair.second.m_setupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
int userSel = 0;
|
||||||
|
printf("Enter Song Number: ");
|
||||||
|
if (scanf("%d", &userSel) <= 0)
|
||||||
|
{
|
||||||
|
Log.report(logvisor::Error, "unable to parse prompt");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userSel < songs.size())
|
||||||
|
{
|
||||||
|
m_arrData = &songs[userSel].second;
|
||||||
|
m_groupId = m_arrData->m_groupId;
|
||||||
|
m_setupId = m_arrData->m_setupId;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.report(logvisor::Error, "unable to find Song %d", userSel);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (version == 0x2)
|
else
|
||||||
{
|
{
|
||||||
/* Retro CSNG data */
|
m_arrData = &songs[0].second;
|
||||||
m_setupId = r->readUint32Big();
|
m_groupId = m_arrData->m_groupId;
|
||||||
m_groupId = r->readUint32Big();
|
m_setupId = m_arrData->m_setupId;
|
||||||
r->readUint32Big();
|
|
||||||
uint32_t sonLength = r->readUint32Big();
|
|
||||||
m_arrData = r->readUBytes(sonLength);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get group selection from user */
|
||||||
|
if (m_groupId != -1)
|
||||||
|
{
|
||||||
|
if (allSongGroups.find(m_groupId) != allSongGroups.end())
|
||||||
|
m_sfxGroup = false;
|
||||||
|
else if (allSFXGroups.find(m_groupId) != allSFXGroups.end())
|
||||||
|
m_sfxGroup = true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.report(logvisor::Error, "unable to find Group %d", m_groupId);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (totalGroups > 1)
|
||||||
|
{
|
||||||
|
/* Ask user to specify which group in project */
|
||||||
|
printf("Multiple Audio Groups discovered:\n");
|
||||||
|
for (const auto& pair : allSFXGroups)
|
||||||
|
{
|
||||||
|
printf(" %d %s (SFXGroup) %" PRISize " sfx-entries\n",
|
||||||
|
pair.first, pair.second.first->first.c_str(),
|
||||||
|
pair.second.second->m_sfxEntries.size());
|
||||||
|
}
|
||||||
|
for (const auto& pair : allSongGroups)
|
||||||
|
{
|
||||||
|
printf(" %d %s (SongGroup) %" PRISize " normal-pages, %" PRISize " drum-pages\n",
|
||||||
|
pair.first, pair.second.first->first.c_str(),
|
||||||
|
pair.second.second->m_normPages.size(), pair.second.second->m_drumPages.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
int userSel = 0;
|
||||||
|
printf("Enter Group Number: ");
|
||||||
|
if (scanf("%d", &userSel) <= 0)
|
||||||
|
{
|
||||||
|
Log.report(logvisor::Error, "unable to parse prompt");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allSongGroups.find(userSel) != allSongGroups.end())
|
||||||
|
{
|
||||||
|
m_groupId = userSel;
|
||||||
|
m_sfxGroup = false;
|
||||||
|
}
|
||||||
|
else if (allSFXGroups.find(userSel) != allSFXGroups.end())
|
||||||
|
{
|
||||||
|
m_groupId = userSel;
|
||||||
|
m_sfxGroup = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.report(logvisor::Error, "unable to find Group %d", userSel);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (totalGroups == 1)
|
||||||
|
{
|
||||||
|
/* Load one and only group */
|
||||||
|
if (allSongGroups.size())
|
||||||
|
{
|
||||||
|
const auto& pair = *allSongGroups.cbegin();
|
||||||
|
m_groupId = pair.first;
|
||||||
|
m_sfxGroup = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto& pair = *allSFXGroups.cbegin();
|
||||||
|
m_groupId = pair.first;
|
||||||
|
m_sfxGroup = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.report(logvisor::Error, "empty project");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make final group selection */
|
||||||
|
amuse::IntrusiveAudioGroupData* selData = nullptr;
|
||||||
|
const amuse::SongGroupIndex* songIndex = nullptr;
|
||||||
|
const amuse::SFXGroupIndex* sfxIndex = nullptr;
|
||||||
|
auto songSearch = allSongGroups.find(m_groupId);
|
||||||
|
if (songSearch != allSongGroups.end())
|
||||||
|
{
|
||||||
|
selData = &songSearch->second.first->second;
|
||||||
|
songIndex = songSearch->second.second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto sfxSearch = allSFXGroups.find(m_groupId);
|
||||||
|
if (sfxSearch != allSFXGroups.end())
|
||||||
|
{
|
||||||
|
selData = &sfxSearch->second.first->second;
|
||||||
|
sfxIndex = sfxSearch->second.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selData)
|
||||||
|
{
|
||||||
|
Log.report(logvisor::Error, "unable to select audio group data");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Build voice engine */
|
||||||
|
std::unique_ptr<boo::IAudioVoiceEngine> voxEngine = boo::NewAudioVoiceEngine();
|
||||||
|
amuse::BooBackendVoiceAllocator booBackend(*voxEngine);
|
||||||
|
m_engine.emplace(booBackend, amuse::AmplitudeMode::PerSample);
|
||||||
|
|
||||||
|
/* Load group into engine */
|
||||||
|
const amuse::AudioGroup* group = m_engine->addAudioGroup(*selData);
|
||||||
|
if (!group)
|
||||||
|
{
|
||||||
|
Log.report(logvisor::Error, "unable to add audio group");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enter playback loop */
|
||||||
|
if (m_sfxGroup)
|
||||||
|
SFXLoop(*sfxIndex);
|
||||||
|
else
|
||||||
|
SongLoop(*songIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Build voice engine */
|
|
||||||
std::unique_ptr<boo::IAudioVoiceEngine> voxEngine = boo::NewAudioVoiceEngine();
|
|
||||||
amuse::BooBackendVoiceAllocator booBackend(*voxEngine);
|
|
||||||
m_engine.emplace(booBackend, amuse::AmplitudeMode::BlockLinearized);
|
|
||||||
|
|
||||||
/* Load group into engine */
|
|
||||||
const amuse::AudioGroup* group = m_engine->addAudioGroup(*selData);
|
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Error, "unable to add audio group");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Enter playback loop */
|
|
||||||
if (m_sfxGroup)
|
|
||||||
SFXLoop(*sfxIndex);
|
|
||||||
else
|
|
||||||
SongLoop(*songIndex);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -866,6 +914,8 @@ void EventCallback::specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods,
|
|||||||
m_app.m_seq->setVolume(m_app.m_volume);
|
m_app.m_seq->setVolume(m_app.m_volume);
|
||||||
m_app.m_updateDisp = true;
|
m_app.m_updateDisp = true;
|
||||||
break;
|
break;
|
||||||
|
case boo::ESpecialKey::Esc:
|
||||||
|
m_app.m_breakout = true;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ public:
|
|||||||
double sampleRate, bool dynamicPitch);
|
double sampleRate, bool dynamicPitch);
|
||||||
void resetSampleRate(double sampleRate);
|
void resetSampleRate(double sampleRate);
|
||||||
void setMatrixCoefficients(const float coefs[8], bool slew);
|
void setMatrixCoefficients(const float coefs[8], bool slew);
|
||||||
|
void setSubmixMatrixCoefficients(const float coefs[8], bool slew);
|
||||||
void setPitchRatio(double ratio, bool slew);
|
void setPitchRatio(double ratio, bool slew);
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "AudioGroupData.hpp"
|
#include "AudioGroupData.hpp"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
@ -24,9 +25,19 @@ public:
|
|||||||
RogueSquadron2,
|
RogueSquadron2,
|
||||||
RogueSquadron3
|
RogueSquadron3
|
||||||
};
|
};
|
||||||
|
struct SongData
|
||||||
|
{
|
||||||
|
std::unique_ptr<uint8_t[]> m_data;
|
||||||
|
size_t m_size;
|
||||||
|
int16_t m_groupId;
|
||||||
|
int16_t m_setupId;
|
||||||
|
SongData(std::unique_ptr<uint8_t[]>&& data, size_t size, int16_t groupId, int16_t setupId)
|
||||||
|
: m_data(std::move(data)), m_size(size), m_groupId(groupId), m_setupId(setupId) {}
|
||||||
|
};
|
||||||
static const char* TypeToName(Type tp);
|
static const char* TypeToName(Type tp);
|
||||||
static Type DetectContainerType(const char* path);
|
static Type DetectContainerType(const char* path);
|
||||||
static std::vector<std::pair<std::string, IntrusiveAudioGroupData>> LoadContainer(const char* path);
|
static std::vector<std::pair<std::string, IntrusiveAudioGroupData>> LoadContainer(const char* path);
|
||||||
|
static std::vector<std::pair<std::string, SongData>> LoadSongs(const char* path);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ class EffectReverbHiImp : public EffectBase<T>, public EffectReverbHi
|
|||||||
int32_t x1a4_preDelayTime = 0; /**< Sample count of pre-delay */
|
int32_t x1a4_preDelayTime = 0; /**< Sample count of pre-delay */
|
||||||
std::unique_ptr<float[]> x1ac_preDelayLine[8]; /**< Dedicated pre-delay buffers */
|
std::unique_ptr<float[]> x1ac_preDelayLine[8]; /**< Dedicated pre-delay buffers */
|
||||||
float* x1b8_preDelayPtr[8] = {}; /**< Current pre-delay pointers */
|
float* x1b8_preDelayPtr[8] = {}; /**< Current pre-delay pointers */
|
||||||
float x1a8_internalCrosstalk;
|
float x1a8_internalCrosstalk = 0.f;
|
||||||
|
|
||||||
double m_sampleRate; /**< copy of sample rate */
|
double m_sampleRate; /**< copy of sample rate */
|
||||||
void _update();
|
void _update();
|
||||||
|
@ -50,15 +50,17 @@ class Engine
|
|||||||
std::pair<AudioGroup*, const SongGroupIndex*> _findSongGroup(int groupId) const;
|
std::pair<AudioGroup*, const SongGroupIndex*> _findSongGroup(int groupId) const;
|
||||||
std::pair<AudioGroup*, const SFXGroupIndex*> _findSFXGroup(int groupId) const;
|
std::pair<AudioGroup*, const SFXGroupIndex*> _findSFXGroup(int groupId) const;
|
||||||
|
|
||||||
std::shared_ptr<Voice> _allocateVoice(const AudioGroup& group, int groupId, double sampleRate,
|
std::list<std::shared_ptr<Voice>>::iterator
|
||||||
bool dynamicPitch, bool emitter, Submix* smx);
|
_allocateVoice(const AudioGroup& group, int groupId, double sampleRate,
|
||||||
std::shared_ptr<Sequencer> _allocateSequencer(const AudioGroup& group, int groupId,
|
bool dynamicPitch, bool emitter, Submix* smx);
|
||||||
int setupId, Submix* smx);
|
std::list<std::shared_ptr<Sequencer>>::iterator
|
||||||
Submix* _allocateSubmix(Submix* smx);
|
_allocateSequencer(const AudioGroup& group, int groupId,
|
||||||
std::list<std::shared_ptr<Voice>>::iterator _destroyVoice(Voice* voice);
|
int setupId, Submix* smx);
|
||||||
std::list<std::shared_ptr<Sequencer>>::iterator _destroySequencer(Sequencer* sequencer);
|
std::list<Submix>::iterator _allocateSubmix(Submix* smx);
|
||||||
std::list<Submix>::iterator _destroySubmix(Submix* smx);
|
std::list<std::shared_ptr<Voice>>::iterator _destroyVoice(std::list<std::shared_ptr<Voice>>::iterator it);
|
||||||
std::list<Submix>::iterator _removeSubmix(Submix* smx);
|
std::list<std::shared_ptr<Sequencer>>::iterator _destroySequencer(std::list<std::shared_ptr<Sequencer>>::iterator it);
|
||||||
|
std::list<Submix>::iterator _destroySubmix(std::list<Submix>::iterator it);
|
||||||
|
std::list<Submix>::iterator _removeSubmix(std::list<Submix>::iterator it);
|
||||||
void _bringOutYourDead();
|
void _bringOutYourDead();
|
||||||
void _5MsCallback(double dt);
|
void _5MsCallback(double dt);
|
||||||
public:
|
public:
|
||||||
|
@ -20,10 +20,10 @@ public:
|
|||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
State m_phase = State::Attack; /**< Current envelope state */
|
State m_phase = State::Attack; /**< Current envelope state */
|
||||||
double m_attackTime = 0.005; /**< Time of attack in seconds */
|
double m_attackTime = 0.02; /**< Time of attack in seconds */
|
||||||
double m_decayTime = 0.0; /**< Time of decay in seconds */
|
double m_decayTime = 0.0; /**< Time of decay in seconds */
|
||||||
double m_sustainFactor = 1.0; /**< Evaluated sustain percentage */
|
double m_sustainFactor = 1.0; /**< Evaluated sustain percentage */
|
||||||
double m_releaseTime = 0.005; /**< Time of release in seconds */
|
double m_releaseTime = 0.02; /**< Time of release in seconds */
|
||||||
double m_releaseStartFactor = 0.0; /**< Level at whenever release event occurs */
|
double m_releaseStartFactor = 0.0; /**< Level at whenever release event occurs */
|
||||||
double m_curTime = 0.0; /**< Current time of envelope stage in seconds */
|
double m_curTime = 0.0; /**< Current time of envelope stage in seconds */
|
||||||
public:
|
public:
|
||||||
|
@ -37,6 +37,9 @@ public:
|
|||||||
/** Set channel-gains for audio source (AudioChannel enum for array index) */
|
/** Set channel-gains for audio source (AudioChannel enum for array index) */
|
||||||
virtual void setMatrixCoefficients(const float coefs[8], bool slew)=0;
|
virtual void setMatrixCoefficients(const float coefs[8], bool slew)=0;
|
||||||
|
|
||||||
|
/** Set submix-channel-gains for audio source (AudioChannel enum for array index) */
|
||||||
|
virtual void setSubmixMatrixCoefficients(const float coefs[8], bool slew)=0;
|
||||||
|
|
||||||
/** Called by client to dynamically adjust the pitch of voices with dynamic pitch enabled */
|
/** Called by client to dynamically adjust the pitch of voices with dynamic pitch enabled */
|
||||||
virtual void setPitchRatio(double ratio, bool slew)=0;
|
virtual void setPitchRatio(double ratio, bool slew)=0;
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@ class Sequencer : public Entity
|
|||||||
const SongGroupIndex& m_songGroup; /**< Quick access to song group project index */
|
const SongGroupIndex& m_songGroup; /**< Quick access to song group project index */
|
||||||
const SongGroupIndex::MIDISetup* m_midiSetup = nullptr; /**< Selected MIDI setup */
|
const SongGroupIndex::MIDISetup* m_midiSetup = nullptr; /**< Selected MIDI setup */
|
||||||
Submix* m_submix = nullptr; /**< Submix this sequencer outputs to (or NULL for the main output mix) */
|
Submix* m_submix = nullptr; /**< Submix this sequencer outputs to (or NULL for the main output mix) */
|
||||||
std::list<std::shared_ptr<Sequencer>>::iterator m_engineIt; /**< Iterator to self within Engine's list for quick deletion */
|
|
||||||
|
|
||||||
const unsigned char* m_arrData = nullptr; /**< Current playing arrangement data */
|
const unsigned char* m_arrData = nullptr; /**< Current playing arrangement data */
|
||||||
SongState m_songState; /**< State of current arrangement playback */
|
SongState m_songState; /**< State of current arrangement playback */
|
||||||
@ -47,7 +46,6 @@ class Sequencer : public Entity
|
|||||||
uint8_t m_chanId;
|
uint8_t m_chanId;
|
||||||
const SongGroupIndex::MIDISetup& m_setup;
|
const SongGroupIndex::MIDISetup& m_setup;
|
||||||
const SongGroupIndex::PageEntry* m_page = nullptr;
|
const SongGroupIndex::PageEntry* m_page = nullptr;
|
||||||
Submix* m_submix = nullptr;
|
|
||||||
~ChannelState();
|
~ChannelState();
|
||||||
ChannelState(Sequencer& parent, uint8_t chanId);
|
ChannelState(Sequencer& parent, uint8_t chanId);
|
||||||
|
|
||||||
|
@ -24,7 +24,6 @@ class Submix
|
|||||||
friend class Sequencer;
|
friend class Sequencer;
|
||||||
Engine& m_root;
|
Engine& m_root;
|
||||||
Submix* m_submix = nullptr; /**< Parent submix of this submix (or NULL if mixing to main output) */
|
Submix* m_submix = nullptr; /**< Parent submix of this submix (or NULL if mixing to main output) */
|
||||||
std::list<Submix>::iterator m_engineIt; /**< Iterator to self within Engine's list for quick deletion */
|
|
||||||
std::unique_ptr<IBackendSubmix> m_backendSubmix; /**< Handle to client-implemented backend submix */
|
std::unique_ptr<IBackendSubmix> m_backendSubmix; /**< Handle to client-implemented backend submix */
|
||||||
std::vector<std::unique_ptr<EffectBaseTypeless>> m_effectStack; /**< Ordered list of effects to apply to submix */
|
std::vector<std::unique_ptr<EffectBaseTypeless>> m_effectStack; /**< Ordered list of effects to apply to submix */
|
||||||
bool m_destroyed = false;
|
bool m_destroyed = false;
|
||||||
|
@ -35,7 +35,6 @@ class Voice : public Entity
|
|||||||
int m_vid; /**< VoiceID of this voice instance */
|
int m_vid; /**< VoiceID of this voice instance */
|
||||||
bool m_emitter; /**< Voice is part of an Emitter */
|
bool m_emitter; /**< Voice is part of an Emitter */
|
||||||
Submix* m_submix = nullptr; /**< Submix this voice outputs to (or NULL for the main output mix) */
|
Submix* m_submix = nullptr; /**< Submix this voice outputs to (or NULL for the main output mix) */
|
||||||
std::list<std::shared_ptr<Voice>>::iterator m_engineIt; /**< Iterator to self within Engine's list for quick deletion */
|
|
||||||
|
|
||||||
std::unique_ptr<IBackendVoice> m_backendVoice; /**< Handle to client-implemented backend voice */
|
std::unique_ptr<IBackendVoice> m_backendVoice; /**< Handle to client-implemented backend voice */
|
||||||
SoundMacroState m_state; /**< State container for SoundMacro playback */
|
SoundMacroState m_state; /**< State container for SoundMacro playback */
|
||||||
@ -48,11 +47,11 @@ class Voice : public Entity
|
|||||||
|
|
||||||
enum class SampleFormat : uint8_t
|
enum class SampleFormat : uint8_t
|
||||||
{
|
{
|
||||||
DSP, /**< GCN DSP-ucode ADPCM (very common for GameCube games) */
|
DSP, /**< GCN DSP-ucode ADPCM (very common for GameCube games) */
|
||||||
IMA, /**< IMA-ADPCM (rarely used within MusyX itself) */
|
DSP_DRUM, /**< GCN DSP-ucode ADPCM (seems to be set into drum samples for expanding their amplitude appropriately) */
|
||||||
PCM, /**< Big-endian PCM found in MusyX2 demo GM instruments */
|
PCM, /**< Big-endian PCM found in MusyX2 demo GM instruments */
|
||||||
N64, /**< 2-stage VADPCM coding with SAMP-embedded codebooks */
|
N64, /**< 2-stage VADPCM coding with SAMP-embedded codebooks */
|
||||||
PCM_PC /**< Little-endian PCM found in PC Rogue Squadron (actually enum 0 which conflicts with DSP-ADPCM) */
|
PCM_PC /**< Little-endian PCM found in PC Rogue Squadron (actually enum 0 which conflicts with DSP-ADPCM) */
|
||||||
};
|
};
|
||||||
const Sample* m_curSample = nullptr; /**< Current sample entry playing */
|
const Sample* m_curSample = nullptr; /**< Current sample entry playing */
|
||||||
const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */
|
const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */
|
||||||
@ -142,8 +141,8 @@ class Voice : public Entity
|
|||||||
std::shared_ptr<Voice> _findVoice(int vid, std::weak_ptr<Voice> thisPtr);
|
std::shared_ptr<Voice> _findVoice(int vid, std::weak_ptr<Voice> thisPtr);
|
||||||
std::unique_ptr<int8_t[]>& _ensureCtrlVals();
|
std::unique_ptr<int8_t[]>& _ensureCtrlVals();
|
||||||
|
|
||||||
std::shared_ptr<Voice> _allocateVoice(double sampleRate, bool dynamicPitch);
|
std::list<std::shared_ptr<Voice>>::iterator _allocateVoice(double sampleRate, bool dynamicPitch);
|
||||||
std::list<std::shared_ptr<Voice>>::iterator _destroyVoice(Voice* voice);
|
std::list<std::shared_ptr<Voice>>::iterator _destroyVoice(std::list<std::shared_ptr<Voice>>::iterator it);
|
||||||
|
|
||||||
bool _loadSoundMacro(const unsigned char* macroData, int macroStep, double ticksPerSec,
|
bool _loadSoundMacro(const unsigned char* macroData, int macroStep, double ticksPerSec,
|
||||||
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc=false);
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc=false);
|
||||||
@ -320,6 +319,9 @@ public:
|
|||||||
/** Get count of all voices in hierarchy, including this one */
|
/** Get count of all voices in hierarchy, including this one */
|
||||||
size_t getTotalVoices() const;
|
size_t getTotalVoices() const;
|
||||||
|
|
||||||
|
/** Recursively mark voice as dead for Engine to deallocate on next cycle */
|
||||||
|
void kill();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,11 @@ void BooBackendVoice::setMatrixCoefficients(const float coefs[8], bool slew)
|
|||||||
m_booVoice->setMonoMatrixCoefficients(coefs, slew);
|
m_booVoice->setMonoMatrixCoefficients(coefs, slew);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BooBackendVoice::setSubmixMatrixCoefficients(const float coefs[8], bool slew)
|
||||||
|
{
|
||||||
|
m_booVoice->setMonoSubmixMatrixCoefficients(coefs, slew);
|
||||||
|
}
|
||||||
|
|
||||||
void BooBackendVoice::setPitchRatio(double ratio, bool slew)
|
void BooBackendVoice::setPitchRatio(double ratio, bool slew)
|
||||||
{
|
{
|
||||||
m_booVoice->setPitchRatio(ratio, slew);
|
m_booVoice->setPitchRatio(ratio, slew);
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
@ -100,6 +101,21 @@ static bool IsChunkExtension(const char* path, const char*& dotOut)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool IsSongExtension(const char* path, const char*& dotOut)
|
||||||
|
{
|
||||||
|
const char* ext = strrchr(path, '.');
|
||||||
|
if (ext)
|
||||||
|
{
|
||||||
|
if (!CompareCaseInsensitive(ext, ".son") ||
|
||||||
|
!CompareCaseInsensitive(ext, ".sng"))
|
||||||
|
{
|
||||||
|
dotOut = ext;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static bool ValidateMP1(FILE* fp)
|
static bool ValidateMP1(FILE* fp)
|
||||||
{
|
{
|
||||||
FileLength(fp);
|
FileLength(fp);
|
||||||
@ -244,6 +260,147 @@ static std::vector<std::pair<std::string, IntrusiveAudioGroupData>> LoadMP1(FILE
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool ValidateMP1Songs(FILE* fp)
|
||||||
|
{
|
||||||
|
FileLength(fp);
|
||||||
|
|
||||||
|
uint32_t magic;
|
||||||
|
fread(&magic, 1, 4, fp);
|
||||||
|
magic = SBig(magic);
|
||||||
|
|
||||||
|
if (magic == 0x00030005)
|
||||||
|
{
|
||||||
|
FSeek(fp, 8, SEEK_SET);
|
||||||
|
|
||||||
|
uint32_t nameCount;
|
||||||
|
fread(&nameCount, 1, 4, fp);
|
||||||
|
nameCount = SBig(nameCount);
|
||||||
|
for (uint32_t i=0 ; i<nameCount ; ++i)
|
||||||
|
{
|
||||||
|
FSeek(fp, 8, SEEK_CUR);
|
||||||
|
uint32_t nameLen;
|
||||||
|
fread(&nameLen, 1, 4, fp);
|
||||||
|
nameLen = SBig(nameLen);
|
||||||
|
FSeek(fp, nameLen, SEEK_CUR);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t resCount;
|
||||||
|
fread(&resCount, 1, 4, fp);
|
||||||
|
resCount = SBig(resCount);
|
||||||
|
for (uint32_t i=0 ; i<resCount ; ++i)
|
||||||
|
{
|
||||||
|
FSeek(fp, 4, SEEK_CUR);
|
||||||
|
uint32_t type;
|
||||||
|
fread(&type, 1, 4, fp);
|
||||||
|
type = SBig(type);
|
||||||
|
FSeek(fp, 8, SEEK_CUR);
|
||||||
|
uint32_t offset;
|
||||||
|
fread(&offset, 1, 4, fp);
|
||||||
|
offset = SBig(offset);
|
||||||
|
|
||||||
|
if (type == 0x43534E47)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<std::pair<std::string, ContainerRegistry::SongData>> LoadMP1Songs(FILE* fp)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<std::string, ContainerRegistry::SongData>> ret;
|
||||||
|
FileLength(fp);
|
||||||
|
|
||||||
|
uint32_t magic;
|
||||||
|
fread(&magic, 1, 4, fp);
|
||||||
|
magic = SBig(magic);
|
||||||
|
|
||||||
|
if (magic == 0x00030005)
|
||||||
|
{
|
||||||
|
FSeek(fp, 8, SEEK_SET);
|
||||||
|
|
||||||
|
uint32_t nameCount;
|
||||||
|
fread(&nameCount, 1, 4, fp);
|
||||||
|
nameCount = SBig(nameCount);
|
||||||
|
|
||||||
|
std::unordered_map<uint32_t, std::string> names;
|
||||||
|
names.reserve(nameCount);
|
||||||
|
for (uint32_t i=0 ; i<nameCount ; ++i)
|
||||||
|
{
|
||||||
|
FSeek(fp, 4, SEEK_CUR);
|
||||||
|
uint32_t id;
|
||||||
|
fread(&id, 1, 4, fp);
|
||||||
|
id = SBig(id);
|
||||||
|
uint32_t nameLen;
|
||||||
|
fread(&nameLen, 1, 4, fp);
|
||||||
|
nameLen = SBig(nameLen);
|
||||||
|
std::string str(nameLen, '\0');
|
||||||
|
fread(&str[0], 1, nameLen, fp);
|
||||||
|
names[id] = std::move(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t resCount;
|
||||||
|
fread(&resCount, 1, 4, fp);
|
||||||
|
resCount = SBig(resCount);
|
||||||
|
ret.reserve(resCount);
|
||||||
|
for (uint32_t i=0 ; i<resCount ; ++i)
|
||||||
|
{
|
||||||
|
FSeek(fp, 4, SEEK_CUR);
|
||||||
|
uint32_t type;
|
||||||
|
fread(&type, 1, 4, fp);
|
||||||
|
type = SBig(type);
|
||||||
|
uint32_t id;
|
||||||
|
fread(&id, 1, 4, fp);
|
||||||
|
id = SBig(id);
|
||||||
|
uint32_t size;
|
||||||
|
fread(&size, 1, 4, fp);
|
||||||
|
size = SBig(size);
|
||||||
|
uint32_t offset;
|
||||||
|
fread(&offset, 1, 4, fp);
|
||||||
|
offset = SBig(offset);
|
||||||
|
|
||||||
|
if (type == 0x43534E47)
|
||||||
|
{
|
||||||
|
int64_t origPos = FTell(fp);
|
||||||
|
FSeek(fp, offset + 4, SEEK_SET);
|
||||||
|
|
||||||
|
uint32_t midiSetup;
|
||||||
|
fread(&midiSetup, 1, 4, fp);
|
||||||
|
midiSetup = SBig(midiSetup);
|
||||||
|
|
||||||
|
uint32_t groupId;
|
||||||
|
fread(&groupId, 1, 4, fp);
|
||||||
|
groupId = SBig(groupId);
|
||||||
|
|
||||||
|
FSeek(fp, 4, SEEK_CUR);
|
||||||
|
|
||||||
|
uint32_t sonLength;
|
||||||
|
fread(&sonLength, 1, 4, fp);
|
||||||
|
sonLength = SBig(sonLength);
|
||||||
|
|
||||||
|
std::unique_ptr<uint8_t[]> song(new uint8_t[sonLength]);
|
||||||
|
fread(song.get(), 1, sonLength, fp);
|
||||||
|
|
||||||
|
auto search = names.find(id);
|
||||||
|
if (search != names.end())
|
||||||
|
ret.emplace_back(std::move(search->second),
|
||||||
|
ContainerRegistry::SongData(std::move(song), sonLength, groupId, midiSetup));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char name[128];
|
||||||
|
snprintf(name, 128, "%08X", id);
|
||||||
|
ret.emplace_back(name, ContainerRegistry::SongData(std::move(song), sonLength, groupId, midiSetup));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FSeek(fp, origPos, SEEK_SET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static bool ValidateMP2(FILE* fp)
|
static bool ValidateMP2(FILE* fp)
|
||||||
{
|
{
|
||||||
FileLength(fp);
|
FileLength(fp);
|
||||||
@ -962,6 +1119,13 @@ struct RS23GroupHead
|
|||||||
uint32_t sampOff;
|
uint32_t sampOff;
|
||||||
uint32_t sampLen;
|
uint32_t sampLen;
|
||||||
|
|
||||||
|
uint32_t unkOff;
|
||||||
|
uint32_t unkLen;
|
||||||
|
|
||||||
|
uint32_t sonCount;
|
||||||
|
uint32_t sonIdxBeginOff;
|
||||||
|
uint32_t sonIdxEndOff;
|
||||||
|
|
||||||
void swapBig()
|
void swapBig()
|
||||||
{
|
{
|
||||||
projOff = SBig(projOff);
|
projOff = SBig(projOff);
|
||||||
@ -972,6 +1136,27 @@ struct RS23GroupHead
|
|||||||
sdirLen = SBig(sdirLen);
|
sdirLen = SBig(sdirLen);
|
||||||
sampOff = SBig(sampOff);
|
sampOff = SBig(sampOff);
|
||||||
sampLen = SBig(sampLen);
|
sampLen = SBig(sampLen);
|
||||||
|
unkOff = SBig(unkOff);
|
||||||
|
unkLen = SBig(unkLen);
|
||||||
|
sonCount = SBig(sonCount);
|
||||||
|
sonIdxBeginOff = SBig(sonIdxBeginOff);
|
||||||
|
sonIdxEndOff = SBig(sonIdxEndOff);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RS23SONHead
|
||||||
|
{
|
||||||
|
uint32_t offset;
|
||||||
|
uint32_t length;
|
||||||
|
uint16_t groupId;
|
||||||
|
uint16_t setupId;
|
||||||
|
|
||||||
|
void swapBig()
|
||||||
|
{
|
||||||
|
offset = SBig(offset);
|
||||||
|
length = SBig(length);
|
||||||
|
groupId = SBig(groupId);
|
||||||
|
setupId = SBig(setupId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1066,6 +1251,68 @@ static std::vector<std::pair<std::string, IntrusiveAudioGroupData>> LoadRS2(FILE
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<std::pair<std::string, ContainerRegistry::SongData>> LoadRS2Songs(FILE* fp)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<std::string, ContainerRegistry::SongData>> ret;
|
||||||
|
size_t endPos = FileLength(fp);
|
||||||
|
|
||||||
|
uint64_t fstOff;
|
||||||
|
fread(&fstOff, 1, 8, fp);
|
||||||
|
fstOff = SBig(fstOff);
|
||||||
|
uint64_t fstSz;
|
||||||
|
fread(&fstSz, 1, 8, fp);
|
||||||
|
fstSz = SBig(fstSz);
|
||||||
|
|
||||||
|
if (fstOff + fstSz > endPos)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
FSeek(fp, int64_t(fstOff), SEEK_SET);
|
||||||
|
for (size_t i=0 ; i<fstSz/64 ; ++i)
|
||||||
|
{
|
||||||
|
RS2FSTEntry entry;
|
||||||
|
fread(&entry, 1, 64, fp);
|
||||||
|
entry.swapBig();
|
||||||
|
if (!strncmp("data", entry.name, 32))
|
||||||
|
{
|
||||||
|
FSeek(fp, int64_t(entry.offset), SEEK_SET);
|
||||||
|
std::unique_ptr<uint8_t[]> audData(new uint8_t[entry.decompSz]);
|
||||||
|
fread(audData.get(), 1, entry.decompSz, fp);
|
||||||
|
|
||||||
|
uint32_t indexOff = SBig(*reinterpret_cast<uint32_t*>(audData.get() + 4));
|
||||||
|
uint32_t groupCount = SBig(*reinterpret_cast<uint32_t*>(audData.get() + indexOff));
|
||||||
|
const uint32_t* groupOffs = reinterpret_cast<const uint32_t*>(audData.get() + indexOff + 4);
|
||||||
|
|
||||||
|
for (uint32_t j=0 ; j<groupCount ; ++j)
|
||||||
|
{
|
||||||
|
const uint8_t* groupData = audData.get() + SBig(groupOffs[j]);
|
||||||
|
RS23GroupHead head = *reinterpret_cast<const RS23GroupHead*>(groupData);
|
||||||
|
head.swapBig();
|
||||||
|
|
||||||
|
if (!head.sonCount)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const RS23SONHead* sonData = reinterpret_cast<const RS23SONHead*>(audData.get() + head.sonIdxBeginOff);
|
||||||
|
for (int s=0 ; s<head.sonCount ; ++s)
|
||||||
|
{
|
||||||
|
RS23SONHead sonHead = sonData[s];
|
||||||
|
sonHead.swapBig();
|
||||||
|
|
||||||
|
char name[128];
|
||||||
|
snprintf(name, 128, "GroupFile%u-%u", j, s);
|
||||||
|
std::unique_ptr<uint8_t[]> song(new uint8_t[sonHead.length]);
|
||||||
|
memcpy(song.get(), audData.get() + sonHead.offset, sonHead.length);
|
||||||
|
ret.emplace_back(name, ContainerRegistry::SongData(std::move(song), sonHead.length,
|
||||||
|
sonHead.groupId, sonHead.setupId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
struct RS3FSTEntry
|
struct RS3FSTEntry
|
||||||
{
|
{
|
||||||
uint64_t offset;
|
uint64_t offset;
|
||||||
@ -1467,4 +1714,83 @@ ContainerRegistry::LoadContainer(const char* path)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, ContainerRegistry::SongData>>
|
||||||
|
ContainerRegistry::LoadSongs(const char* path)
|
||||||
|
{
|
||||||
|
FILE* fp;
|
||||||
|
|
||||||
|
/* See if provided file is a raw song */
|
||||||
|
const char* dot = nullptr;
|
||||||
|
if (IsSongExtension(path, dot))
|
||||||
|
{
|
||||||
|
fp = fopen(path, "rb");
|
||||||
|
size_t fLen = FileLength(fp);
|
||||||
|
if (!fLen)
|
||||||
|
{
|
||||||
|
fclose(fp);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
std::unique_ptr<uint8_t[]> song(new uint8_t[fLen]);
|
||||||
|
fread(song.get(), 1, fLen, fp);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, SongData>> ret;
|
||||||
|
ret.emplace_back("Song", SongData(std::move(song), fLen, -1, -1));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now attempt archive-file case */
|
||||||
|
fp = fopen(path, "rb");
|
||||||
|
if (fp)
|
||||||
|
{
|
||||||
|
if (ValidateMP1Songs(fp))
|
||||||
|
{
|
||||||
|
auto ret = LoadMP1Songs(fp);
|
||||||
|
fclose(fp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (ValidateRS1PCSongs(fp))
|
||||||
|
{
|
||||||
|
auto ret = LoadRS1PCSongs(fp);
|
||||||
|
fclose(fp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ValidateRS1N64Songs(fp))
|
||||||
|
{
|
||||||
|
auto ret = LoadRS1N64Songs(fp);
|
||||||
|
fclose(fp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ValidateBFNPCSongs(fp))
|
||||||
|
{
|
||||||
|
auto ret = LoadBFNPCSongs(fp);
|
||||||
|
fclose(fp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ValidateBFNN64Songs(fp))
|
||||||
|
{
|
||||||
|
auto ret = LoadBFNN64Songs(fp);
|
||||||
|
fclose(fp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ValidateRS2(fp))
|
||||||
|
{
|
||||||
|
auto ret = LoadRS2Songs(fp);
|
||||||
|
fclose(fp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ void EffectReverbStdImp<T>::applyEffect(T* audio, size_t frameCount, const Chann
|
|||||||
float dampWet = x118_level * 0.6f;
|
float dampWet = x118_level * 0.6f;
|
||||||
float dampDry = 0.6f - dampWet;
|
float dampDry = 0.6f - dampWet;
|
||||||
|
|
||||||
for (size_t f=0 ; f<frameCount ;)
|
for (size_t f=0 ; f<frameCount ; f+=160)
|
||||||
{
|
{
|
||||||
for (int c=0 ; c<chanMap.m_channelCount ; ++c)
|
for (int c=0 ; c<chanMap.m_channelCount ; ++c)
|
||||||
{
|
{
|
||||||
@ -153,7 +153,8 @@ void EffectReverbStdImp<T>::applyEffect(T* audio, size_t frameCount, const Chann
|
|||||||
ReverbDelayLine* linesC = x78_C[c];
|
ReverbDelayLine* linesC = x78_C[c];
|
||||||
ReverbDelayLine* linesAP = x0_AP[c];
|
ReverbDelayLine* linesAP = x0_AP[c];
|
||||||
|
|
||||||
for (int s=0 ; s<160 && f<frameCount ; ++s, ++f)
|
int procSamples = std::min(160ul, frameCount - f);
|
||||||
|
for (int s=0 ; s<procSamples ; ++s)
|
||||||
{
|
{
|
||||||
float sample = audio[s * chanMap.m_channelCount + c];
|
float sample = audio[s * chanMap.m_channelCount + c];
|
||||||
|
|
||||||
@ -255,7 +256,7 @@ void EffectReverbHiImp<T>::_update()
|
|||||||
size_t tapDelay = CTapDelays[t] * m_sampleRate / 32000.0;
|
size_t tapDelay = CTapDelays[t] * m_sampleRate / 32000.0;
|
||||||
combLine.allocate(tapDelay);
|
combLine.allocate(tapDelay);
|
||||||
combLine.setdelay(tapDelay);
|
combLine.setdelay(tapDelay);
|
||||||
x16c_combCoef[c][t] = std::pow(10.f, tapDelay * -3 / timeSamples);
|
x16c_combCoef[c][t] = std::pow(10.f, tapDelay * -3.f / timeSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int t=0 ; t<2 ; ++t)
|
for (int t=0 ; t<2 ; ++t)
|
||||||
@ -301,6 +302,7 @@ void EffectReverbHiImp<T>::_update()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
x1a8_internalCrosstalk = x1dc_crosstalk;
|
||||||
m_dirty = false;
|
m_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,9 +326,10 @@ void EffectReverbHiImp<T>::_handleReverb(T* audio, int c, int chanCount, int sam
|
|||||||
float damping = x1a0_damping;
|
float damping = x1a0_damping;
|
||||||
int32_t preDelayTime = x1a4_preDelayTime;
|
int32_t preDelayTime = x1a4_preDelayTime;
|
||||||
|
|
||||||
float sample = audio[c];
|
for (int s=0 ; s<sampleCount ; ++s)
|
||||||
for (int s=1 ; s<sampleCount ; ++s)
|
|
||||||
{
|
{
|
||||||
|
float sample = audio[s * chanCount + c];
|
||||||
|
|
||||||
/* Pre-delay stage */
|
/* Pre-delay stage */
|
||||||
float sample2 = sample;
|
float sample2 = sample;
|
||||||
if (preDelayTime != 0)
|
if (preDelayTime != 0)
|
||||||
@ -420,8 +423,7 @@ void EffectReverbHiImp<T>::_handleReverb(T* audio, int c, int chanCount, int sam
|
|||||||
lineLP.x4_outPoint = 0;
|
lineLP.x4_outPoint = 0;
|
||||||
|
|
||||||
/* Mix out */
|
/* Mix out */
|
||||||
audio[(s-1) * chanCount + c] = ClampFull<T>(dampWet * allPass + dampDry * sample);
|
audio[s * chanCount + c] = ClampFull<T>(dampWet * allPass + dampDry * sample);
|
||||||
sample = audio[s * chanCount + c];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
x1b8_preDelayPtr[c] = preDelayPtr;
|
x1b8_preDelayPtr[c] = preDelayPtr;
|
||||||
|
@ -15,7 +15,7 @@ Emitter::Emitter(Engine& engine, const AudioGroup& group, std::shared_ptr<Voice>
|
|||||||
void Emitter::_destroy()
|
void Emitter::_destroy()
|
||||||
{
|
{
|
||||||
Entity::_destroy();
|
Entity::_destroy();
|
||||||
m_engine._destroyVoice(m_vox.get());
|
m_vox->kill();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emitter::setPos(const Vector3f& pos)
|
void Emitter::setPos(const Vector3f& pos)
|
||||||
|
149
lib/Engine.cpp
149
lib/Engine.cpp
@ -14,7 +14,8 @@ namespace amuse
|
|||||||
Engine::~Engine()
|
Engine::~Engine()
|
||||||
{
|
{
|
||||||
for (std::shared_ptr<Sequencer>& seq : m_activeSequencers)
|
for (std::shared_ptr<Sequencer>& seq : m_activeSequencers)
|
||||||
seq->_destroy();
|
if (!seq->m_destroyed)
|
||||||
|
seq->_destroy();
|
||||||
while (m_activeSubmixes.size())
|
while (m_activeSubmixes.size())
|
||||||
removeSubmix(&m_activeSubmixes.front());
|
removeSubmix(&m_activeSubmixes.front());
|
||||||
for (std::shared_ptr<Emitter>& emitter : m_activeEmitters)
|
for (std::shared_ptr<Emitter>& emitter : m_activeEmitters)
|
||||||
@ -52,68 +53,71 @@ std::pair<AudioGroup*, const SFXGroupIndex*> Engine::_findSFXGroup(int groupId)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Voice> Engine::_allocateVoice(const AudioGroup& group, int groupId, double sampleRate,
|
std::list<std::shared_ptr<Voice>>::iterator
|
||||||
bool dynamicPitch, bool emitter, Submix* smx)
|
Engine::_allocateVoice(const AudioGroup& group, int groupId, double sampleRate,
|
||||||
|
bool dynamicPitch, bool emitter, Submix* smx)
|
||||||
{
|
{
|
||||||
auto it = m_activeVoices.emplace(m_activeVoices.end(),
|
auto it = m_activeVoices.emplace(m_activeVoices.end(),
|
||||||
new Voice(*this, group, groupId, m_nextVid++, emitter, smx));
|
new Voice(*this, group, groupId, m_nextVid++, emitter, smx));
|
||||||
m_activeVoices.back()->m_backendVoice =
|
if (smx)
|
||||||
m_backend.allocateVoice(*m_activeVoices.back(), sampleRate, dynamicPitch);
|
m_activeVoices.back()->m_backendVoice =
|
||||||
m_activeVoices.back()->m_engineIt = it;
|
smx->m_backendSubmix->allocateVoice(*m_activeVoices.back(), sampleRate, dynamicPitch);
|
||||||
return m_activeVoices.back();
|
else
|
||||||
|
m_activeVoices.back()->m_backendVoice =
|
||||||
|
m_backend.allocateVoice(*m_activeVoices.back(), sampleRate, dynamicPitch);
|
||||||
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Sequencer> Engine::_allocateSequencer(const AudioGroup& group, int groupId,
|
std::list<std::shared_ptr<Sequencer>>::iterator
|
||||||
int setupId, Submix* smx)
|
Engine::_allocateSequencer(const AudioGroup& group, int groupId,
|
||||||
|
int setupId, Submix* smx)
|
||||||
{
|
{
|
||||||
const SongGroupIndex* songGroup = group.getProj().getSongGroupIndex(groupId);
|
const SongGroupIndex* songGroup = group.getProj().getSongGroupIndex(groupId);
|
||||||
if (!songGroup)
|
if (!songGroup)
|
||||||
return {};
|
return {};
|
||||||
auto it = m_activeSequencers.emplace(m_activeSequencers.end(),
|
auto it = m_activeSequencers.emplace(m_activeSequencers.end(),
|
||||||
new Sequencer(*this, group, groupId, *songGroup, setupId, smx));
|
new Sequencer(*this, group, groupId, *songGroup, setupId, smx));
|
||||||
m_activeSequencers.back()->m_engineIt = it;
|
return it;
|
||||||
return m_activeSequencers.back();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Submix* Engine::_allocateSubmix(Submix* smx)
|
std::list<Submix>::iterator Engine::_allocateSubmix(Submix* smx)
|
||||||
{
|
{
|
||||||
auto it = m_activeSubmixes.emplace(m_activeSubmixes.end(), *this, smx);
|
auto it = m_activeSubmixes.emplace(m_activeSubmixes.end(), *this, smx);
|
||||||
m_activeSubmixes.back().m_backendSubmix = m_backend.allocateSubmix(m_activeSubmixes.back());
|
m_activeSubmixes.back().m_backendSubmix = m_backend.allocateSubmix(m_activeSubmixes.back());
|
||||||
m_activeSubmixes.back().m_engineIt = it;
|
return it;
|
||||||
return &m_activeSubmixes.back();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<std::shared_ptr<Voice>>::iterator Engine::_destroyVoice(Voice* voice)
|
std::list<std::shared_ptr<Voice>>::iterator Engine::_destroyVoice(std::list<std::shared_ptr<Voice>>::iterator it)
|
||||||
{
|
{
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
assert(this == &voice->getEngine());
|
assert(this == &(*it)->getEngine());
|
||||||
#endif
|
#endif
|
||||||
if (voice->m_destroyed)
|
if ((*it)->m_destroyed)
|
||||||
return m_activeVoices.begin();
|
return m_activeVoices.begin();
|
||||||
voice->_destroy();
|
(*it)->_destroy();
|
||||||
return m_activeVoices.erase(voice->m_engineIt);
|
return m_activeVoices.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<std::shared_ptr<Sequencer>>::iterator Engine::_destroySequencer(Sequencer* sequencer)
|
std::list<std::shared_ptr<Sequencer>>::iterator Engine::_destroySequencer(std::list<std::shared_ptr<Sequencer>>::iterator it)
|
||||||
{
|
{
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
assert(this == &sequencer->getEngine());
|
assert(this == &(*it)->getEngine());
|
||||||
#endif
|
#endif
|
||||||
if (sequencer->m_destroyed)
|
if ((*it)->m_destroyed)
|
||||||
return m_activeSequencers.begin();
|
return m_activeSequencers.begin();
|
||||||
sequencer->_destroy();
|
(*it)->_destroy();
|
||||||
return m_activeSequencers.erase(sequencer->m_engineIt);
|
return m_activeSequencers.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<Submix>::iterator Engine::_destroySubmix(Submix* smx)
|
std::list<Submix>::iterator Engine::_destroySubmix(std::list<Submix>::iterator it)
|
||||||
{
|
{
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
assert(this == &smx->getEngine());
|
assert(this == &it->getEngine());
|
||||||
#endif
|
#endif
|
||||||
if (smx->m_destroyed)
|
if (it->m_destroyed)
|
||||||
return m_activeSubmixes.begin();
|
return m_activeSubmixes.begin();
|
||||||
smx->_destroy();
|
it->_destroy();
|
||||||
return m_activeSubmixes.erase(smx->m_engineIt);
|
return m_activeSubmixes.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::_bringOutYourDead()
|
void Engine::_bringOutYourDead()
|
||||||
@ -136,7 +140,7 @@ void Engine::_bringOutYourDead()
|
|||||||
vox->_bringOutYourDead();
|
vox->_bringOutYourDead();
|
||||||
if (vox->_isRecursivelyDead())
|
if (vox->_isRecursivelyDead())
|
||||||
{
|
{
|
||||||
it = _destroyVoice(vox);
|
it = _destroyVoice(it);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
++it;
|
++it;
|
||||||
@ -148,7 +152,7 @@ void Engine::_bringOutYourDead()
|
|||||||
seq->_bringOutYourDead();
|
seq->_bringOutYourDead();
|
||||||
if (seq->m_state == SequencerState::Dead)
|
if (seq->m_state == SequencerState::Dead)
|
||||||
{
|
{
|
||||||
it = _destroySequencer(seq);
|
it = _destroySequencer(it);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
++it;
|
++it;
|
||||||
@ -276,48 +280,36 @@ void Engine::removeAudioGroup(const AudioGroupData& data)
|
|||||||
/** Create new Submix (a.k.a 'Studio') within root mix engine */
|
/** Create new Submix (a.k.a 'Studio') within root mix engine */
|
||||||
Submix* Engine::addSubmix(Submix* smx)
|
Submix* Engine::addSubmix(Submix* smx)
|
||||||
{
|
{
|
||||||
return _allocateSubmix(smx);
|
return &*_allocateSubmix(smx);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<Submix>::iterator Engine::_removeSubmix(Submix* smx)
|
std::list<Submix>::iterator Engine::_removeSubmix(std::list<Submix>::iterator smx)
|
||||||
{
|
{
|
||||||
/* Delete all voices bound to submix */
|
/* Delete all voices bound to submix */
|
||||||
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ;)
|
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ; ++it)
|
||||||
{
|
{
|
||||||
Voice* vox = it->get();
|
Voice* vox = it->get();
|
||||||
|
|
||||||
Submix* vsmx = vox->getSubmix();
|
Submix* vsmx = vox->getSubmix();
|
||||||
if (vsmx == smx)
|
if (vsmx == &*smx)
|
||||||
{
|
vox->kill();
|
||||||
vox->_destroy();
|
|
||||||
it = m_activeVoices.erase(it);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Delete all sequencers bound to submix */
|
/* Delete all sequencers bound to submix */
|
||||||
for (auto it = m_activeSequencers.begin() ; it != m_activeSequencers.end() ;)
|
for (auto it = m_activeSequencers.begin() ; it != m_activeSequencers.end() ; ++it)
|
||||||
{
|
{
|
||||||
Sequencer* seq = it->get();
|
Sequencer* seq = it->get();
|
||||||
|
|
||||||
Submix* ssmx = seq->getSubmix();
|
Submix* ssmx = seq->getSubmix();
|
||||||
if (ssmx == smx)
|
if (ssmx == &*smx)
|
||||||
{
|
seq->kill();
|
||||||
seq->_destroy();
|
|
||||||
it = m_activeSequencers.erase(it);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Delete all submixes bound to submix */
|
/* Delete all submixes bound to submix */
|
||||||
for (auto it = m_activeSubmixes.begin() ; it != m_activeSubmixes.end() ;)
|
for (auto it = m_activeSubmixes.begin() ; it != m_activeSubmixes.end() ;)
|
||||||
{
|
{
|
||||||
Submix* ssmx = it->getParentSubmix();
|
Submix* ssmx = it->getParentSubmix();
|
||||||
if (ssmx == smx)
|
if (ssmx == &*smx)
|
||||||
{
|
{
|
||||||
it = _removeSubmix(&*it);
|
it = _removeSubmix(it);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
++it;
|
++it;
|
||||||
@ -332,7 +324,16 @@ void Engine::removeSubmix(Submix* smx)
|
|||||||
{
|
{
|
||||||
if (!smx)
|
if (!smx)
|
||||||
return;
|
return;
|
||||||
_removeSubmix(smx);
|
|
||||||
|
for (auto it = m_activeSubmixes.begin() ; it != m_activeSubmixes.end() ;)
|
||||||
|
{
|
||||||
|
if (&*it == &*smx)
|
||||||
|
{
|
||||||
|
it = _removeSubmix(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start soundFX playing from loaded audio groups */
|
/** Start soundFX playing from loaded audio groups */
|
||||||
@ -347,19 +348,20 @@ std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, Submix*
|
|||||||
if (!grp)
|
if (!grp)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
std::shared_ptr<Voice> ret = _allocateVoice(*grp, std::get<1>(search->second),
|
std::list<std::shared_ptr<Voice>>::iterator ret =
|
||||||
32000.0, true, false, smx);
|
_allocateVoice(*grp, std::get<1>(search->second),
|
||||||
|
32000.0, true, false, smx);
|
||||||
|
|
||||||
ObjectId oid = (grp->getDataFormat() == DataFormat::PC) ? entry->objId : SBig(entry->objId);
|
ObjectId oid = (grp->getDataFormat() == DataFormat::PC) ? entry->objId : SBig(entry->objId);
|
||||||
if (!ret->loadSoundObject(oid, 0, 1000.f, entry->defKey, entry->defVel, 0))
|
if (!(*ret)->loadSoundObject(oid, 0, 1000.f, entry->defKey, entry->defVel, 0))
|
||||||
{
|
{
|
||||||
_destroyVoice(ret.get());
|
_destroyVoice(ret);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ret->setVolume(vol);
|
(*ret)->setVolume(vol);
|
||||||
ret->setPan(pan);
|
(*ret)->setPan(pan);
|
||||||
return ret;
|
return *ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
|
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
|
||||||
@ -375,20 +377,21 @@ std::shared_ptr<Emitter> Engine::addEmitter(const Vector3f& pos, const Vector3f&
|
|||||||
if (!grp)
|
if (!grp)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
std::shared_ptr<Voice> vox = _allocateVoice(*grp, std::get<1>(search->second),
|
std::list<std::shared_ptr<Voice>>::iterator vox =
|
||||||
32000.0, true, true, smx);
|
_allocateVoice(*grp, std::get<1>(search->second),
|
||||||
m_activeEmitters.emplace(m_activeEmitters.end(), new Emitter(*this, *grp, std::move(vox)));
|
32000.0, true, true, smx);
|
||||||
Emitter& ret = *m_activeEmitters.back();
|
auto emitIt = m_activeEmitters.emplace(m_activeEmitters.end(), new Emitter(*this, *grp, std::move(*vox)));
|
||||||
|
Emitter& ret = *(*emitIt);
|
||||||
|
|
||||||
ObjectId oid = (grp->getDataFormat() == DataFormat::PC) ? entry->objId : SBig(entry->objId);
|
ObjectId oid = (grp->getDataFormat() == DataFormat::PC) ? entry->objId : SBig(entry->objId);
|
||||||
if (!ret.getVoice()->loadSoundObject(oid, 0, 1000.f, entry->defKey, entry->defVel, 0))
|
if (!ret.getVoice()->loadSoundObject(oid, 0, 1000.f, entry->defKey, entry->defVel, 0))
|
||||||
{
|
{
|
||||||
ret._destroy();
|
ret._destroy();
|
||||||
m_activeEmitters.pop_back();
|
m_activeEmitters.erase(emitIt);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
vox->setPan(entry->panning);
|
(*vox)->setPan(entry->panning);
|
||||||
ret.setPos(pos);
|
ret.setPos(pos);
|
||||||
ret.setDir(dir);
|
ret.setDir(dir);
|
||||||
ret.setMaxDist(maxDist);
|
ret.setMaxDist(maxDist);
|
||||||
@ -396,7 +399,7 @@ std::shared_ptr<Emitter> Engine::addEmitter(const Vector3f& pos, const Vector3f&
|
|||||||
ret.setMinVol(minVol);
|
ret.setMinVol(minVol);
|
||||||
ret.setMaxVol(maxVol);
|
ret.setMaxVol(maxVol);
|
||||||
|
|
||||||
return m_activeEmitters.back();
|
return *emitIt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start song playing from loaded audio groups */
|
/** Start song playing from loaded audio groups */
|
||||||
@ -407,13 +410,13 @@ std::shared_ptr<Sequencer> Engine::seqPlay(int groupId, int songId,
|
|||||||
if (!songGrp.second)
|
if (!songGrp.second)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
std::shared_ptr<Sequencer> ret = _allocateSequencer(*songGrp.first, groupId, songId, smx);
|
std::list<std::shared_ptr<Sequencer>>::iterator ret = _allocateSequencer(*songGrp.first, groupId, songId, smx);
|
||||||
if (!ret)
|
if (!*ret)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (arrData)
|
if (arrData)
|
||||||
ret->playSong(arrData);
|
(*ret)->playSong(arrData);
|
||||||
return ret;
|
return *ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Find voice from VoiceId */
|
/** Find voice from VoiceId */
|
||||||
@ -446,7 +449,7 @@ void Engine::killKeygroup(uint8_t kg, bool now)
|
|||||||
{
|
{
|
||||||
if (now)
|
if (now)
|
||||||
{
|
{
|
||||||
it = _destroyVoice(vox);
|
it = _destroyVoice(it);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
vox->keyOff();
|
vox->keyOff();
|
||||||
|
@ -33,6 +33,7 @@ void Envelope::keyOff()
|
|||||||
|
|
||||||
float Envelope::advance(double dt)
|
float Envelope::advance(double dt)
|
||||||
{
|
{
|
||||||
|
double thisTime = m_curTime;
|
||||||
m_curTime += dt;
|
m_curTime += dt;
|
||||||
|
|
||||||
switch (m_phase)
|
switch (m_phase)
|
||||||
@ -46,7 +47,7 @@ float Envelope::advance(double dt)
|
|||||||
m_releaseStartFactor = 1.f;
|
m_releaseStartFactor = 1.f;
|
||||||
return 1.f;
|
return 1.f;
|
||||||
}
|
}
|
||||||
double attackFac = m_curTime / m_attackTime;
|
double attackFac = thisTime / m_attackTime;
|
||||||
if (attackFac >= 1.0)
|
if (attackFac >= 1.0)
|
||||||
{
|
{
|
||||||
m_phase = State::Decay;
|
m_phase = State::Decay;
|
||||||
@ -66,7 +67,7 @@ float Envelope::advance(double dt)
|
|||||||
m_releaseStartFactor = m_sustainFactor;
|
m_releaseStartFactor = m_sustainFactor;
|
||||||
return m_sustainFactor;
|
return m_sustainFactor;
|
||||||
}
|
}
|
||||||
double decayFac = m_curTime / m_decayTime;
|
double decayFac = thisTime / m_decayTime;
|
||||||
if (decayFac >= 1.0)
|
if (decayFac >= 1.0)
|
||||||
{
|
{
|
||||||
m_phase = State::Sustain;
|
m_phase = State::Sustain;
|
||||||
@ -88,7 +89,7 @@ float Envelope::advance(double dt)
|
|||||||
m_phase = State::Complete;
|
m_phase = State::Complete;
|
||||||
return 0.f;
|
return 0.f;
|
||||||
}
|
}
|
||||||
double releaseFac = m_curTime / m_releaseTime;
|
double releaseFac = thisTime / m_releaseTime;
|
||||||
if (releaseFac >= 1.0)
|
if (releaseFac >= 1.0)
|
||||||
{
|
{
|
||||||
m_phase = State::Complete;
|
m_phase = State::Complete;
|
||||||
|
@ -51,35 +51,31 @@ void Sequencer::_destroy()
|
|||||||
m_engine.removeSubmix(m_submix);
|
m_engine.removeSubmix(m_submix);
|
||||||
m_submix = nullptr;
|
m_submix = nullptr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (auto& chan : m_chanStates)
|
Sequencer::~Sequencer()
|
||||||
|
{
|
||||||
|
if (m_submix)
|
||||||
{
|
{
|
||||||
if (chan)
|
m_engine.removeSubmix(m_submix);
|
||||||
{
|
m_submix = nullptr;
|
||||||
if (chan->m_submix)
|
|
||||||
{
|
|
||||||
m_engine.removeSubmix(chan->m_submix);
|
|
||||||
chan->m_submix = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Sequencer::~Sequencer() {}
|
|
||||||
|
|
||||||
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId,
|
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId,
|
||||||
const SongGroupIndex& songGroup, int setupId, Submix* smx)
|
const SongGroupIndex& songGroup, int setupId, Submix* smx)
|
||||||
: Entity(engine, group, groupId), m_songGroup(songGroup), m_submix(smx)
|
: Entity(engine, group, groupId), m_songGroup(songGroup)
|
||||||
{
|
{
|
||||||
auto it = m_songGroup.m_midiSetups.find(setupId);
|
auto it = m_songGroup.m_midiSetups.find(setupId);
|
||||||
if (it != m_songGroup.m_midiSetups.cend())
|
if (it != m_songGroup.m_midiSetups.cend())
|
||||||
m_midiSetup = it->second->data();
|
m_midiSetup = it->second->data();
|
||||||
|
|
||||||
|
m_submix = m_engine.addSubmix(smx);
|
||||||
|
m_submix->makeReverbHi(0.2f, 1.f, 1.f, 0.5f, 0.f, 0.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
Sequencer::ChannelState::~ChannelState()
|
Sequencer::ChannelState::~ChannelState()
|
||||||
{
|
{
|
||||||
if (m_submix)
|
|
||||||
m_parent.m_engine.removeSubmix(m_submix);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Sequencer::ChannelState::ChannelState(Sequencer& parent, uint8_t chanId)
|
Sequencer::ChannelState::ChannelState(Sequencer& parent, uint8_t chanId)
|
||||||
@ -98,14 +94,10 @@ Sequencer::ChannelState::ChannelState(Sequencer& parent, uint8_t chanId)
|
|||||||
m_page = it->second;
|
m_page = it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_submix = m_parent.m_engine.addSubmix(m_parent.m_submix);
|
|
||||||
if (m_setup.reverb)
|
|
||||||
m_submix->makeReverbStd(0.5f, m_setup.reverb / 127.f, 5.f, 0.5f, 0.f);
|
|
||||||
if (m_setup.chorus)
|
|
||||||
m_submix->makeChorus(15, m_setup.chorus * 5 / 127, 5000);
|
|
||||||
|
|
||||||
m_curVol = m_setup.volume / 127.f;
|
m_curVol = m_setup.volume / 127.f;
|
||||||
m_curPan = m_setup.panning / 64.f - 1.f;
|
m_curPan = m_setup.panning / 64.f - 1.f;
|
||||||
|
m_ctrlVals[0x5b] = m_setup.reverb;
|
||||||
|
m_ctrlVals[0x5d] = m_setup.chorus;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sequencer::advance(double dt)
|
void Sequencer::advance(double dt)
|
||||||
@ -153,29 +145,31 @@ std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo
|
|||||||
m_chanVoxs.erase(keySearch);
|
m_chanVoxs.erase(keySearch);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Voice> ret = m_parent.m_engine._allocateVoice(m_parent.m_audioGroup,
|
std::list<std::shared_ptr<Voice>>::iterator ret =
|
||||||
m_parent.m_groupId, 32000.0,
|
m_parent.m_engine._allocateVoice(m_parent.m_audioGroup,
|
||||||
true, false, m_submix);
|
m_parent.m_groupId, 32000.0,
|
||||||
if (ret)
|
true, false, m_parent.m_submix);
|
||||||
|
if (*ret)
|
||||||
{
|
{
|
||||||
m_chanVoxs[note] = ret;
|
m_chanVoxs[note] = *ret;
|
||||||
ret->installCtrlValues(m_ctrlVals);
|
(*ret)->installCtrlValues(m_ctrlVals);
|
||||||
|
|
||||||
ObjectId oid = (m_parent.m_audioGroup.getDataFormat() == DataFormat::PC) ? m_page->objId : SBig(m_page->objId);
|
ObjectId oid = (m_parent.m_audioGroup.getDataFormat() == DataFormat::PC) ? m_page->objId : SBig(m_page->objId);
|
||||||
if (!ret->loadSoundObject(oid, 0, 1000.f, note, velocity, m_ctrlVals[1]))
|
if (!(*ret)->loadSoundObject(oid, 0, 1000.f, note, velocity, m_ctrlVals[1]))
|
||||||
{
|
{
|
||||||
m_parent.m_engine._destroyVoice(ret.get());
|
m_parent.m_engine._destroyVoice(ret);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
ret->setVolume(m_parent.m_curVol * m_curVol);
|
(*ret)->setVolume(m_parent.m_curVol * m_curVol);
|
||||||
ret->setPan(m_curPan);
|
(*ret)->setReverbVol(m_ctrlVals[0x5b] / 127.f);
|
||||||
ret->setPitchWheel(m_curPitchWheel);
|
(*ret)->setPan(m_curPan);
|
||||||
|
(*ret)->setPitchWheel(m_curPitchWheel);
|
||||||
|
|
||||||
if (m_ctrlVals[64] > 64)
|
if (m_ctrlVals[64] > 64)
|
||||||
ret->setPedal(true);
|
(*ret)->setPedal(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return *ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Voice> Sequencer::keyOn(uint8_t chan, uint8_t note, uint8_t velocity)
|
std::shared_ptr<Voice> Sequencer::keyOn(uint8_t chan, uint8_t note, uint8_t velocity)
|
||||||
@ -323,9 +317,9 @@ void Sequencer::allOff(bool now)
|
|||||||
if (chan)
|
if (chan)
|
||||||
{
|
{
|
||||||
for (const auto& vox : chan->m_chanVoxs)
|
for (const auto& vox : chan->m_chanVoxs)
|
||||||
m_engine._destroyVoice(vox.second.get());
|
vox.second->kill();
|
||||||
for (const auto& vox : chan->m_keyoffVoxs)
|
for (const auto& vox : chan->m_keyoffVoxs)
|
||||||
m_engine._destroyVoice(vox.get());
|
vox->kill();
|
||||||
chan->m_chanVoxs.clear();
|
chan->m_chanVoxs.clear();
|
||||||
chan->m_keyoffVoxs.clear();
|
chan->m_keyoffVoxs.clear();
|
||||||
}
|
}
|
||||||
@ -344,9 +338,9 @@ void Sequencer::allOff(uint8_t chan, bool now)
|
|||||||
if (now)
|
if (now)
|
||||||
{
|
{
|
||||||
for (const auto& vox : m_chanStates[chan]->m_chanVoxs)
|
for (const auto& vox : m_chanStates[chan]->m_chanVoxs)
|
||||||
m_engine._destroyVoice(vox.second.get());
|
vox.second->kill();
|
||||||
for (const auto& vox : m_chanStates[chan]->m_keyoffVoxs)
|
for (const auto& vox : m_chanStates[chan]->m_keyoffVoxs)
|
||||||
m_engine._destroyVoice(vox.get());
|
vox->kill();
|
||||||
m_chanStates[chan]->m_chanVoxs.clear();
|
m_chanStates[chan]->m_chanVoxs.clear();
|
||||||
m_chanStates[chan]->m_keyoffVoxs.clear();
|
m_chanStates[chan]->m_keyoffVoxs.clear();
|
||||||
}
|
}
|
||||||
|
110
lib/Voice.cpp
110
lib/Voice.cpp
@ -155,7 +155,7 @@ void Voice::_bringOutYourDead()
|
|||||||
vox->_bringOutYourDead();
|
vox->_bringOutYourDead();
|
||||||
if (vox->_isRecursivelyDead())
|
if (vox->_isRecursivelyDead())
|
||||||
{
|
{
|
||||||
it = _destroyVoice(vox);
|
it = _destroyVoice(it);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
++it;
|
++it;
|
||||||
@ -186,23 +186,26 @@ std::unique_ptr<int8_t[]>& Voice::_ensureCtrlVals()
|
|||||||
return m_ctrlValsSelf;
|
return m_ctrlValsSelf;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Voice> Voice::_allocateVoice(double sampleRate, bool dynamicPitch)
|
std::list<std::shared_ptr<Voice>>::iterator Voice::_allocateVoice(double sampleRate, bool dynamicPitch)
|
||||||
{
|
{
|
||||||
auto it = m_childVoices.emplace(m_childVoices.end(), new Voice(m_engine, m_audioGroup,
|
auto it = m_childVoices.emplace(m_childVoices.end(), new Voice(m_engine, m_audioGroup,
|
||||||
m_groupId, m_engine.m_nextVid++, m_emitter, m_submix));
|
m_groupId, m_engine.m_nextVid++, m_emitter, m_submix));
|
||||||
m_childVoices.back()->m_backendVoice =
|
if (m_submix)
|
||||||
m_engine.getBackend().allocateVoice(*m_childVoices.back(), sampleRate, dynamicPitch);
|
m_childVoices.back()->m_backendVoice =
|
||||||
m_childVoices.back()->m_engineIt = it;
|
m_submix->m_backendSubmix->allocateVoice(*m_childVoices.back(), sampleRate, dynamicPitch);
|
||||||
return m_childVoices.back();
|
else
|
||||||
|
m_childVoices.back()->m_backendVoice =
|
||||||
|
m_engine.getBackend().allocateVoice(*m_childVoices.back(), sampleRate, dynamicPitch);
|
||||||
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<std::shared_ptr<Voice>>::iterator Voice::_destroyVoice(Voice* voice)
|
std::list<std::shared_ptr<Voice>>::iterator Voice::_destroyVoice(std::list<std::shared_ptr<Voice>>::iterator it)
|
||||||
{
|
{
|
||||||
if (voice->m_destroyed)
|
if ((*it)->m_destroyed)
|
||||||
return m_childVoices.begin();
|
return m_childVoices.begin();
|
||||||
|
|
||||||
voice->_destroy();
|
(*it)->_destroy();
|
||||||
return m_childVoices.erase(voice->m_engineIt);
|
return m_childVoices.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ApplyVolume(float vol, int16_t& samp)
|
static void ApplyVolume(float vol, int16_t& samp)
|
||||||
@ -250,12 +253,12 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
|
|||||||
if (m_envelopeTime >= 0.0)
|
if (m_envelopeTime >= 0.0)
|
||||||
{
|
{
|
||||||
m_envelopeTime += dt;
|
m_envelopeTime += dt;
|
||||||
float start = m_envelopeStart / 127.f;
|
float start = m_envelopeStart;
|
||||||
float end = m_envelopeEnd / 127.f;
|
float end = m_envelopeEnd;
|
||||||
double t = std::max(0.0, std::min(1.0, m_envelopeTime / m_envelopeDur));
|
float t = std::max(0.f, std::min(1.f, float(m_envelopeTime / m_envelopeDur)));
|
||||||
if (m_envelopeCurve)
|
if (m_envelopeCurve)
|
||||||
t = (*m_envelopeCurve)[int(t*127.f)] / 127.f;
|
t = (*m_envelopeCurve)[int(t*127.f)] / 127.f;
|
||||||
m_curVol = (start * (1.0f - t)) + (end * t);
|
m_curVol = clamp(0.f, (start * (1.0f - t)) + (end * t), 1.f);
|
||||||
|
|
||||||
/* Done with envelope */
|
/* Done with envelope */
|
||||||
if (m_envelopeTime > m_envelopeDur)
|
if (m_envelopeTime > m_envelopeDur)
|
||||||
@ -373,20 +376,9 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
|||||||
{
|
{
|
||||||
uint32_t samplesRem = samples;
|
uint32_t samplesRem = samples;
|
||||||
size_t samplesProc = 0;
|
size_t samplesProc = 0;
|
||||||
bool dead = true;
|
|
||||||
|
|
||||||
/* Attempt to load stopped sample for immediate decoding */
|
/* Process SoundMacro; bootstrapping sample if needed */
|
||||||
if (!m_curSample)
|
bool dead = m_state.advance(*this, samples / m_sampleRate);
|
||||||
{
|
|
||||||
dead = m_state.advance(*this, samples / m_sampleRate);
|
|
||||||
if (!dead)
|
|
||||||
{
|
|
||||||
memset(data, 0, sizeof(int16_t) * samples);
|
|
||||||
return samples;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
dead = m_state.advance(*this, samples / m_sampleRate);
|
|
||||||
|
|
||||||
if (m_curSample)
|
if (m_curSample)
|
||||||
{
|
{
|
||||||
@ -537,7 +529,7 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
|||||||
else
|
else
|
||||||
memset(data, 0, sizeof(int16_t) * samples);
|
memset(data, 0, sizeof(int16_t) * samples);
|
||||||
|
|
||||||
if (dead && m_voxState == VoiceState::KeyOff &&
|
if (dead && (!m_curSample || m_voxState == VoiceState::KeyOff) &&
|
||||||
m_sampleEndTrap.macroId == 0xffff &&
|
m_sampleEndTrap.macroId == 0xffff &&
|
||||||
m_messageTrap.macroId == 0xffff &&
|
m_messageTrap.macroId == 0xffff &&
|
||||||
(!m_curSample || (m_curSample && m_volAdsr.isComplete())))
|
(!m_curSample || (m_curSample && m_volAdsr.isComplete())))
|
||||||
@ -560,17 +552,17 @@ int Voice::maxVid() const
|
|||||||
std::shared_ptr<Voice> Voice::_startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec,
|
std::shared_ptr<Voice> Voice::_startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec,
|
||||||
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc)
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc)
|
||||||
{
|
{
|
||||||
std::shared_ptr<Voice> vox = _allocateVoice(32000.0, true);
|
std::list<std::shared_ptr<Voice>>::iterator vox = _allocateVoice(32000.0, true);
|
||||||
if (!vox->loadSoundObject(macroId, macroStep, ticksPerSec, midiKey,
|
if (!(*vox)->loadSoundObject(macroId, macroStep, ticksPerSec, midiKey,
|
||||||
midiVel, midiMod, pushPc))
|
midiVel, midiMod, pushPc))
|
||||||
{
|
{
|
||||||
_destroyVoice(vox.get());
|
_destroyVoice(vox);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
vox->setVolume(m_userVol);
|
(*vox)->setVolume(m_userVol);
|
||||||
vox->setPan(m_userPan);
|
(*vox)->setPan(m_userPan);
|
||||||
vox->setSurroundPan(m_userSpan);
|
(*vox)->setSurroundPan(m_userSpan);
|
||||||
return vox;
|
return *vox;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Voice> Voice::startChildMacro(int8_t addNote, ObjectId macroId, int macroStep)
|
std::shared_ptr<Voice> Voice::startChildMacro(int8_t addNote, ObjectId macroId, int macroStep)
|
||||||
@ -744,22 +736,18 @@ void Voice::startSample(int16_t sampId, int32_t offset)
|
|||||||
m_curSampleData = m_audioGroup.getSampleData(m_curSample->first.m_sampleOff);
|
m_curSampleData = m_audioGroup.getSampleData(m_curSample->first.m_sampleOff);
|
||||||
m_prev1 = 0;
|
m_prev1 = 0;
|
||||||
m_prev2 = 0;
|
m_prev2 = 0;
|
||||||
|
|
||||||
if (m_audioGroup.getDataFormat() == DataFormat::PC)
|
if (m_audioGroup.getDataFormat() == DataFormat::PC)
|
||||||
m_curFormat = SampleFormat::PCM_PC;
|
m_curFormat = SampleFormat::PCM_PC;
|
||||||
else
|
else
|
||||||
m_curFormat = SampleFormat(m_curSample->first.m_numSamples >> 24);
|
m_curFormat = SampleFormat(m_curSample->first.m_numSamples >> 24);
|
||||||
|
|
||||||
|
if (m_curFormat == SampleFormat::DSP_DRUM)
|
||||||
|
m_curFormat = SampleFormat::DSP;
|
||||||
|
|
||||||
m_lastSamplePos = m_curSample->first.m_loopLengthSamples ?
|
m_lastSamplePos = m_curSample->first.m_loopLengthSamples ?
|
||||||
(m_curSample->first.m_loopStartSample + m_curSample->first.m_loopLengthSamples) : numSamples;
|
(m_curSample->first.m_loopStartSample + m_curSample->first.m_loopLengthSamples) : numSamples;
|
||||||
|
|
||||||
if (m_curFormat != SampleFormat::DSP &&
|
|
||||||
m_curFormat != SampleFormat::PCM &&
|
|
||||||
m_curFormat != SampleFormat::N64 &&
|
|
||||||
m_curFormat != SampleFormat::PCM_PC)
|
|
||||||
{
|
|
||||||
m_curSample = nullptr;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool looped;
|
bool looped;
|
||||||
_checkSamplePos(looped);
|
_checkSamplePos(looped);
|
||||||
|
|
||||||
@ -829,6 +817,10 @@ void Voice::_setPan(float pan)
|
|||||||
coefs[7] *= 1.f - std::fabs(totalSpan);
|
coefs[7] *= 1.f - std::fabs(totalSpan);
|
||||||
|
|
||||||
m_backendVoice->setMatrixCoefficients(coefs, true);
|
m_backendVoice->setMatrixCoefficients(coefs, true);
|
||||||
|
|
||||||
|
for (int i=0 ; i<8 ; ++i)
|
||||||
|
coefs[i] *= m_curReverbVol;
|
||||||
|
m_backendVoice->setSubmixMatrixCoefficients(coefs, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setPan(float pan)
|
void Voice::setPan(float pan)
|
||||||
@ -855,7 +847,7 @@ void Voice::setSurroundPan(float span)
|
|||||||
|
|
||||||
void Voice::startEnvelope(double dur, float vol, const Curve* envCurve)
|
void Voice::startEnvelope(double dur, float vol, const Curve* envCurve)
|
||||||
{
|
{
|
||||||
m_envelopeTime = m_voiceTime;
|
m_envelopeTime = 0.f;
|
||||||
m_envelopeDur = dur;
|
m_envelopeDur = dur;
|
||||||
m_envelopeStart = clamp(0.f, m_curVol, 1.f);
|
m_envelopeStart = clamp(0.f, m_curVol, 1.f);
|
||||||
m_envelopeEnd = clamp(0.f, vol, 1.f);
|
m_envelopeEnd = clamp(0.f, vol, 1.f);
|
||||||
@ -864,7 +856,7 @@ void Voice::startEnvelope(double dur, float vol, const Curve* envCurve)
|
|||||||
|
|
||||||
void Voice::startFadeIn(double dur, float vol, const Curve* envCurve)
|
void Voice::startFadeIn(double dur, float vol, const Curve* envCurve)
|
||||||
{
|
{
|
||||||
m_envelopeTime = m_voiceTime;
|
m_envelopeTime = 0.f;
|
||||||
m_envelopeDur = dur;
|
m_envelopeDur = dur;
|
||||||
m_envelopeStart = 0.f;
|
m_envelopeStart = 0.f;
|
||||||
m_envelopeEnd = clamp(0.f, vol, 1.f);
|
m_envelopeEnd = clamp(0.f, vol, 1.f);
|
||||||
@ -946,7 +938,10 @@ void Voice::setPitchSweep2(uint8_t times, int16_t add)
|
|||||||
|
|
||||||
void Voice::setReverbVol(float rvol)
|
void Voice::setReverbVol(float rvol)
|
||||||
{
|
{
|
||||||
m_curReverbVol = rvol;
|
m_curReverbVol = clamp(0.f, rvol, 1.f);
|
||||||
|
_setPan(m_curPan);
|
||||||
|
for (std::shared_ptr<Voice>& vox : m_childVoices)
|
||||||
|
vox->setReverbVol(rvol);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setAdsr(ObjectId adsrId, bool dls)
|
void Voice::setAdsr(ObjectId adsrId, bool dls)
|
||||||
@ -1022,13 +1017,21 @@ void Voice::setAftertouch(uint8_t aftertouch)
|
|||||||
|
|
||||||
void Voice::_notifyCtrlChange(uint8_t ctrl, int8_t val)
|
void Voice::_notifyCtrlChange(uint8_t ctrl, int8_t val)
|
||||||
{
|
{
|
||||||
if (ctrl == 64)
|
if (ctrl == 0x40)
|
||||||
{
|
{
|
||||||
if (val >= 64)
|
if (val >= 0x40)
|
||||||
setPedal(true);
|
setPedal(true);
|
||||||
else
|
else
|
||||||
setPedal(false);
|
setPedal(false);
|
||||||
}
|
}
|
||||||
|
else if (ctrl == 0x41)
|
||||||
|
{
|
||||||
|
printf("PORTAMENTO %d\n", val);
|
||||||
|
}
|
||||||
|
else if (ctrl == 0x5b)
|
||||||
|
{
|
||||||
|
setReverbVol(val / 127.f);
|
||||||
|
}
|
||||||
|
|
||||||
for (std::shared_ptr<Voice>& vox : m_childVoices)
|
for (std::shared_ptr<Voice>& vox : m_childVoices)
|
||||||
vox->_notifyCtrlChange(ctrl, val);
|
vox->_notifyCtrlChange(ctrl, val);
|
||||||
@ -1042,4 +1045,11 @@ size_t Voice::getTotalVoices() const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Voice::kill()
|
||||||
|
{
|
||||||
|
m_voxState = VoiceState::Dead;
|
||||||
|
for (const std::shared_ptr<Voice>& vox : m_childVoices)
|
||||||
|
vox->kill();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user