3 Commits
v1.7 ... v1.9

Author SHA1 Message Date
Jack Andersen
1be5d6e821 Windows fixes 2016-07-06 11:45:41 -10:00
Jack Andersen
2d31313594 Add amuserender executable 2016-07-06 11:30:46 -10:00
Jack Andersen
52cba61f76 Refactored audio supply dispatch across two passes 2016-07-04 15:08:00 -10:00
7 changed files with 575 additions and 83 deletions

View File

@@ -94,4 +94,8 @@ if(TARGET boo)
# Converter # Converter
add_executable(amuseconv driver/amuseconv.cpp) add_executable(amuseconv driver/amuseconv.cpp)
target_link_libraries(amuseconv amuse ${BOO_SYS_LIBS} logvisor athena-core ${ZLIB_LIBRARIES}) target_link_libraries(amuseconv amuse ${BOO_SYS_LIBS} logvisor athena-core ${ZLIB_LIBRARIES})
# Renderer
add_executable(amuserender driver/amuserender.cpp)
target_link_libraries(amuserender amuse boo ${BOO_SYS_LIBS} logvisor athena-core ${ZLIB_LIBRARIES})
endif() endif()

472
driver/amuserender.cpp Normal file
View File

@@ -0,0 +1,472 @@
#include "amuse/amuse.hpp"
#include "amuse/BooBackend.hpp"
#include "athena/FileReader.hpp"
#include "boo/boo.hpp"
#include "boo/audiodev/IAudioVoiceEngine.hpp"
#include "logvisor/logvisor.hpp"
#include "optional.hpp"
#include <stdio.h>
#include <string.h>
#include <thread>
#include <map>
#include <vector>
#include <unordered_map>
#include <stdarg.h>
static logvisor::Module Log("amuserender");
#if __GNUC__
__attribute__((__format__ (__printf__, 3, 4)))
#endif
static inline void SNPrintf(boo::SystemChar* str, size_t maxlen, const boo::SystemChar* format, ...)
{
va_list va;
va_start(va, format);
#if _WIN32
_vsnwprintf(str, maxlen, format, va);
#else
vsnprintf(str, maxlen, format, va);
#endif
va_end(va);
}
#if _WIN32
#include <DbgHelp.h>
#pragma comment(lib, "Dbghelp.lib")
#include <signal.h>
static void abortHandler( int signum )
{
unsigned int i;
void * stack[ 100 ];
unsigned short frames;
SYMBOL_INFO * symbol;
HANDLE process;
process = GetCurrentProcess();
SymInitialize( process, NULL, TRUE );
frames = CaptureStackBackTrace( 0, 100, stack, NULL );
symbol = ( SYMBOL_INFO * )calloc( sizeof( SYMBOL_INFO ) + 256 * sizeof( char ), 1 );
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof( SYMBOL_INFO );
for( i = 0; i < frames; i++ )
{
SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, symbol );
printf( "%i: %s - 0x%0llX", frames - i - 1, symbol->Name, symbol->Address );
DWORD dwDisplacement;
IMAGEHLP_LINE64 line;
SymSetOptions(SYMOPT_LOAD_LINES);
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (SymGetLineFromAddr64(process, ( DWORD64 )( stack[ i ] ), &dwDisplacement, &line))
{
// SymGetLineFromAddr64 returned success
printf(" LINE %d\n", line.LineNumber);
}
else
{
printf("\n");
}
}
free( symbol );
// If you caught one of the above signals, it is likely you just
// want to quit your program right now.
system("PAUSE");
exit( signum );
}
#endif
/* SIGINT will gracefully break write loop */
static bool g_BreakLoop = false;
static void SIGINTHandler(int sig)
{
g_BreakLoop = true;
}
#if _WIN32
int wmain(int argc, const boo::SystemChar** argv)
#else
int main(int argc, const boo::SystemChar** argv)
#endif
{
signal(SIGINT, SIGINTHandler);
logvisor::RegisterConsoleLogger();
std::vector<boo::SystemString> m_args;
m_args.reserve(argc);
double rate = 32000.0;
for (int i=1 ; i<argc ; ++i)
{
#if _WIN32
if (!wcsncmp(argv[i], L"-r", 2))
{
if (argv[i][2])
rate = wcstod(&argv[i][2], nullptr);
else if (argc > (i+1))
{
rate = wcstod(argv[i+1], nullptr);
++i;
}
}
else
m_args.push_back(argv[i]);
#else
if (!strncmp(argv[i], "-r", 2))
{
if (argv[i][2])
rate = strtod(&argv[i][2], nullptr);
else if (argc > (i+1))
{
rate = strtod(argv[i+1], nullptr);
++i;
}
}
else
m_args.push_back(argv[i]);
#endif
}
/* Load data */
if (m_args.size() < 1)
{
Log.report(logvisor::Error, "Usage: amuserender <group-file> [<songs-file>] [-r <sample-rate>]");
exit(1);
}
amuse::ContainerRegistry::Type cType = amuse::ContainerRegistry::DetectContainerType(m_args[0].c_str());
if (cType == amuse::ContainerRegistry::Type::Invalid)
{
Log.report(logvisor::Error, "invalid/no data at path argument");
exit(1);
}
Log.report(logvisor::Info, _S("Found '%s' Audio Group data"), amuse::ContainerRegistry::TypeToName(cType));
std::vector<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>> data =
amuse::ContainerRegistry::LoadContainer(m_args[0].c_str());
if (data.empty())
{
Log.report(logvisor::Error, "invalid/no data at path argument");
exit(1);
}
int m_groupId = -1;
int m_setupId = -1;
const amuse::SystemString* m_groupName = nullptr;
const amuse::SystemString* m_songName = nullptr;
amuse::ContainerRegistry::SongData* m_arrData = nullptr;
bool m_sfxGroup = false;
std::list<amuse::AudioGroupProject> m_projs;
std::map<int, std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*, const amuse::SongGroupIndex*>> allSongGroups;
std::map<int, std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*, const amuse::SFXGroupIndex*>> allSFXGroups;
size_t totalGroups = 0;
for (auto& grp : data)
{
/* Load project to assemble group list */
m_projs.push_back(amuse::AudioGroupProject::CreateAudioGroupProject(grp.second));
amuse::AudioGroupProject& proj = m_projs.back();
totalGroups += proj.sfxGroups().size() + proj.songGroups().size();
for (auto it = proj.songGroups().begin() ; it != proj.songGroups().end() ; ++it)
allSongGroups[it->first] = std::make_pair(&grp, &it->second);
for (auto it = proj.sfxGroups().begin() ; it != proj.sfxGroups().end() ; ++it)
allSFXGroups[it->first] = std::make_pair(&grp, &it->second);
}
/* Attempt loading song */
std::vector<std::pair<amuse::SystemString, amuse::ContainerRegistry::SongData>> songs;
if (m_args.size() > 1)
songs = amuse::ContainerRegistry::LoadSongs(m_args[1].c_str());
else
songs = amuse::ContainerRegistry::LoadSongs(m_args[0].c_str());
if (songs.size())
{
bool play = true;
if (m_args.size() <= 1)
{
bool prompt = true;
while (true)
{
if (prompt)
{
printf("Render Song? (Y/N): ");
prompt = false;
}
char userSel;
if (scanf("%c", &userSel) <= 0 || userSel == '\n')
continue;
userSel = tolower(userSel);
if (userSel == 'n')
play = false;
else if (userSel != 'y')
{
prompt = true;
continue;
}
break;
}
}
if (play)
{
/* Get song selection from user */
if (songs.size() > 1)
{
/* Ask user to specify which song */
printf("Multiple Songs discovered:\n");
int idx = 0;
for (const auto& pair : songs)
{
const amuse::ContainerRegistry::SongData& sngData = pair.second;
int16_t grpId = sngData.m_groupId;
int16_t setupId = sngData.m_setupId;
if (sngData.m_groupId == -1 && sngData.m_setupId != -1)
{
for (const auto& pair : allSongGroups)
{
for (const auto& setup : pair.second.second->m_midiSetups)
{
if (setup.first == sngData.m_setupId)
{
grpId = pair.first;
break;
}
}
if (grpId != -1)
break;
}
}
amuse::Printf(_S(" %d %s (Group %d, Setup %d)\n"), idx++,
pair.first.c_str(), grpId, 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;
m_songName = &songs[userSel].first;
}
else
{
Log.report(logvisor::Error, "unable to find Song %d", userSel);
exit(1);
}
}
else if (songs.size() == 1)
{
m_arrData = &songs[0].second;
m_groupId = m_arrData->m_groupId;
m_setupId = m_arrData->m_setupId;
m_songName = &songs[0].first;
}
}
}
/* Get group selection via setup search */
if (m_groupId == -1 && m_setupId != -1)
{
for (const auto& pair : allSongGroups)
{
for (const auto& setup : pair.second.second->m_midiSetups)
{
if (setup.first == m_setupId)
{
m_groupId = pair.first;
m_groupName = &pair.second.first->first;
break;
}
}
if (m_groupId != -1)
break;
}
}
/* Get group selection via 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)
{
amuse::Printf(_S(" %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)
{
amuse::Printf(_S(" %d %s (SongGroup) %" PRISize " normal-pages, %" PRISize " drum-pages, %" PRISize " MIDI-setups\n"),
pair.first, pair.second.first->first.c_str(),
pair.second.second->m_normPages.size(),
pair.second.second->m_drumPages.size(),
pair.second.second->m_midiSetups.size());
}
int userSel = 0;
printf("Enter Group Number: ");
if (scanf("%d", &userSel) <= 0)
{
Log.report(logvisor::Error, "unable to parse prompt");
exit(1);
}
auto songSearch = allSongGroups.find(userSel);
auto sfxSearch = allSFXGroups.find(userSel);
if (songSearch != allSongGroups.end())
{
m_groupId = userSel;
m_groupName = &songSearch->second.first->first;
m_sfxGroup = false;
}
else if (sfxSearch != allSFXGroups.end())
{
m_groupId = userSel;
m_groupName = &sfxSearch->second.first->first;
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_groupName = &pair.second.first->first;
m_sfxGroup = false;
}
else
{
const auto& pair = *allSFXGroups.cbegin();
m_groupId = pair.first;
m_groupName = &pair.second.first->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);
}
if (m_sfxGroup)
{
Log.report(logvisor::Error, "amuserender is currently only able to render SongGroups");
exit(1);
}
/* WAV out path */
amuse::SystemChar pathOut[1024];
SNPrintf(pathOut, 1024, _S("%s-%s.wav"), m_groupName->c_str(), m_songName->c_str());
Log.report(logvisor::Info, _S("Writing to %s"), pathOut);
/* Build voice engine */
std::unique_ptr<boo::IAudioVoiceEngine> voxEngine = boo::NewWAVAudioVoiceEngine(pathOut, rate);
amuse::BooBackendVoiceAllocator booBackend(*voxEngine);
amuse::Engine engine(booBackend, amuse::AmplitudeMode::PerSample);
/* Load group into engine */
const amuse::AudioGroup* group = engine.addAudioGroup(*selData);
if (!group)
{
Log.report(logvisor::Error, "unable to add audio group");
exit(1);
}
/* Enter playback loop */
std::shared_ptr<amuse::Sequencer> seq = engine.seqPlay(m_groupId, m_setupId, m_arrData->m_data.get());
size_t wroteFrames = 0;
do
{
engine.pumpEngine();
wroteFrames += voxEngine->get5MsFrames();
printf("\rFrame %" PRISize, wroteFrames);
fflush(stdout);
} while (!g_BreakLoop && (seq->state() == amuse::SequencerState::Playing || seq->getVoiceCount() != 0));
printf("\n");
return 0;
}
#if _WIN32
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int)
{
signal( SIGABRT, abortHandler );
signal( SIGSEGV, abortHandler );
signal( SIGILL, abortHandler );
signal( SIGFPE, abortHandler );
int argc = 0;
const boo::SystemChar** argv = (const wchar_t**)(CommandLineToArgvW(lpCmdLine, &argc));
static boo::SystemChar selfPath[1024];
GetModuleFileNameW(nullptr, selfPath, 1024);
static const boo::SystemChar* booArgv[32] = {};
booArgv[0] = selfPath;
for (int i=0 ; i<argc ; ++i)
booArgv[i+1] = argv[i];
logvisor::CreateWin32Console();
SetConsoleOutputCP(65001);
return wmain(argc+1, booArgv);
}
#endif

View File

@@ -22,6 +22,7 @@ class BooBackendVoice : public IBackendVoice
struct VoiceCallback : boo::IAudioVoiceCallback struct VoiceCallback : boo::IAudioVoiceCallback
{ {
BooBackendVoice& m_parent; BooBackendVoice& m_parent;
void preSupplyAudio(boo::IAudioVoice& voice, double dt);
size_t supplyAudio(boo::IAudioVoice& voice, size_t frames, int16_t* data); size_t supplyAudio(boo::IAudioVoice& voice, size_t frames, int16_t* data);
VoiceCallback(BooBackendVoice& parent) : m_parent(parent) {} VoiceCallback(BooBackendVoice& parent) : m_parent(parent) {}
} m_cb; } m_cb;

View File

@@ -21,10 +21,10 @@ public:
}; };
private: private:
State m_phase = State::Attack; /**< Current envelope state */ State m_phase = State::Attack; /**< Current envelope state */
double m_attackTime = 0.02; /**< Time of attack in seconds */ double m_attackTime = 0.0; /**< 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.02; /**< Time of release in seconds */ double m_releaseTime = 0.0; /**< 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 */
bool m_adsrSet = false; bool m_adsrSet = false;

View File

@@ -86,6 +86,7 @@ class Voice : public Entity
int32_t m_pitchWheelVal = 0; /**< Current resolved pitchwheel delta for control */ int32_t m_pitchWheelVal = 0; /**< Current resolved pitchwheel delta for control */
int32_t m_curPitch; /**< Current base pitch in cents */ int32_t m_curPitch; /**< Current base pitch in cents */
bool m_pitchDirty = true; /**< m_curPitch has been updated and needs sending to voice */ bool m_pitchDirty = true; /**< m_curPitch has been updated and needs sending to voice */
bool m_needsSlew = false; /**< next _setTotalPitch will be slewed */
Envelope m_volAdsr; /**< Volume envelope */ Envelope m_volAdsr; /**< Volume envelope */
double m_envelopeTime = -1.f; /**< time since last ENVELOPE command, -1 for no active volume-sweep */ double m_envelopeTime = -1.f; /**< time since last ENVELOPE command, -1 for no active volume-sweep */
@@ -137,7 +138,7 @@ class Voice : public Entity
void _doKeyOff(); void _doKeyOff();
void _macroKeyOff(); void _macroKeyOff();
void _macroSampleEnd(); void _macroSampleEnd();
bool _advanceSample(int16_t& samp, int32_t& curPitch); void _advanceSample(int16_t& samp);
void _setTotalPitch(int32_t cents, bool slew); void _setTotalPitch(int32_t cents, bool slew);
bool _isRecursivelyDead(); bool _isRecursivelyDead();
void _bringOutYourDead(); void _bringOutYourDead();
@@ -166,6 +167,10 @@ public:
Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, Submix* smx); Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, Submix* smx);
Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter, Submix* smx); Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter, Submix* smx);
/** Called before each supplyAudio invocation to prepare voice
* backend for possible parameter updates */
void preSupplyAudio(double dt);
/** Request specified count of audio frames (samples) from voice, /** Request specified count of audio frames (samples) from voice,
* internally advancing the voice stream */ * internally advancing the voice stream */
size_t supplyAudio(size_t frames, int16_t* data); size_t supplyAudio(size_t frames, int16_t* data);

View File

@@ -6,6 +6,12 @@
namespace amuse namespace amuse
{ {
void BooBackendVoice::VoiceCallback::preSupplyAudio(boo::IAudioVoice&,
double dt)
{
m_parent.m_clientVox.preSupplyAudio(dt);
}
size_t BooBackendVoice::VoiceCallback::supplyAudio(boo::IAudioVoice&, size_t BooBackendVoice::VoiceCallback::supplyAudio(boo::IAudioVoice&,
size_t frames, int16_t* data) size_t frames, int16_t* data)
{ {

View File

@@ -115,7 +115,7 @@ void Voice::_doKeyOff()
void Voice::_setTotalPitch(int32_t cents, bool slew) void Voice::_setTotalPitch(int32_t cents, bool slew)
{ {
//fprintf(stderr, "PITCH %d\n", cents); //fprintf(stderr, "PITCH %d %d \n", cents, slew);
int32_t interval = cents - m_curSample->first.m_pitch * 100; int32_t interval = cents - m_curSample->first.m_pitch * 100;
double ratio = std::exp2(interval / 1200.0); double ratio = std::exp2(interval / 1200.0);
m_sampleRate = m_curSample->first.m_sampleRate * ratio; m_sampleRate = m_curSample->first.m_sampleRate * ratio;
@@ -199,7 +199,7 @@ static void ApplyVolume(float vol, int16_t& samp)
samp *= VolumeLUT[int(vol * 65536)]; samp *= VolumeLUT[int(vol * 65536)];
} }
bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch) void Voice::_advanceSample(int16_t& samp)
{ {
double dt; double dt;
@@ -223,7 +223,7 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
/* Apply total volume to sample using decibel scale */ /* Apply total volume to sample using decibel scale */
ApplyVolume(l, samp); ApplyVolume(l, samp);
return false; return;
} }
dt = 160.0 / m_sampleRate; dt = 160.0 / m_sampleRate;
@@ -232,7 +232,6 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
} }
m_voiceTime += dt; m_voiceTime += dt;
bool refresh = false;
/* Process active envelope */ /* Process active envelope */
if (m_envelopeTime >= 0.0) if (m_envelopeTime >= 0.0)
@@ -255,40 +254,6 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
/* Dynamically evaluate per-sample SoundMacro parameters */ /* Dynamically evaluate per-sample SoundMacro parameters */
float evalVol = m_state.m_volumeSel ? (m_state.m_volumeSel.evaluate(*this, m_state) / 2.f * m_curVol) : m_curVol; float evalVol = m_state.m_volumeSel ? (m_state.m_volumeSel.evaluate(*this, m_state) / 2.f * m_curVol) : m_curVol;
bool panDirty = false;
if (m_state.m_panSel)
{
float evalPan = m_state.m_panSel.evaluate(*this, m_state);
if (evalPan != m_curPan)
{
m_curPan = evalPan;
panDirty = true;
}
}
if (m_state.m_spanSel)
{
float evalSpan = m_state.m_spanSel.evaluate(*this, m_state);
if (evalSpan != m_curSpan)
{
m_curSpan = evalSpan;
panDirty = true;
}
}
if (m_state.m_reverbSel)
{
float evalRev = m_state.m_reverbSel.evaluate(*this, m_state) / 2.f;
if (evalRev != m_curReverbVol)
{
m_curReverbVol = evalRev;
panDirty = true;
}
}
if (panDirty)
_setPan(m_curPan);
if (m_state.m_pitchWheelSel)
_setPitchWheel(m_state.m_pitchWheelSel.evaluate(*this, m_state));
/* Process user volume slew */ /* Process user volume slew */
if (m_engine.m_ampMode == AmplitudeMode::PerSample) if (m_engine.m_ampMode == AmplitudeMode::PerSample)
{ {
@@ -351,8 +316,70 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
/* Apply total volume to sample using decibel scale */ /* Apply total volume to sample using decibel scale */
ApplyVolume(m_nextLevel, samp); ApplyVolume(m_nextLevel, samp);
}
uint32_t Voice::_GetBlockSampleCount(SampleFormat fmt)
{
switch (fmt)
{
default:
return 1;
case Voice::SampleFormat::DSP:
return 14;
case Voice::SampleFormat::N64:
return 64;
}
}
void Voice::preSupplyAudio(double dt)
{
/* Process SoundMacro; bootstrapping sample if needed */
bool dead = m_state.advance(*this, dt);
/* Process per-block evaluators here */
if (m_state.m_pedalSel)
{
bool pedal = m_state.m_pedalSel.evaluate(*this, m_state) >= 1.f;
if (pedal != m_sustained)
setPedal(pedal);
}
bool panDirty = false;
if (m_state.m_panSel)
{
float evalPan = m_state.m_panSel.evaluate(*this, m_state);
if (evalPan != m_curPan)
{
m_curPan = evalPan;
panDirty = true;
}
}
if (m_state.m_spanSel)
{
float evalSpan = m_state.m_spanSel.evaluate(*this, m_state);
if (evalSpan != m_curSpan)
{
m_curSpan = evalSpan;
panDirty = true;
}
}
if (m_state.m_reverbSel)
{
float evalRev = m_state.m_reverbSel.evaluate(*this, m_state) / 2.f;
if (evalRev != m_curReverbVol)
{
m_curReverbVol = evalRev;
panDirty = true;
}
}
if (panDirty)
_setPan(m_curPan);
if (m_state.m_pitchWheelSel)
_setPitchWheel(m_state.m_pitchWheelSel.evaluate(*this, m_state));
/* Process active pan-sweep */ /* Process active pan-sweep */
bool refresh = false;
if (m_panningTime >= 0.f) if (m_panningTime >= 0.f)
{ {
m_panningTime += dt; m_panningTime += dt;
@@ -383,7 +410,7 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
} }
/* Calculate total pitch */ /* Calculate total pitch */
newPitch = m_curPitch; int32_t newPitch = m_curPitch;
refresh |= m_pitchDirty; refresh |= m_pitchDirty;
m_pitchDirty = false; m_pitchDirty = false;
if (m_portamentoTime >= 0.f) if (m_portamentoTime >= 0.f)
@@ -423,20 +450,19 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
refresh = true; refresh = true;
} }
/* True if backend voice needs reconfiguration before next sample */ if (m_curSample && refresh)
return refresh; {
_setTotalPitch(newPitch + m_pitchSweep1 + m_pitchSweep2 + m_pitchWheelVal, m_needsSlew);
m_needsSlew = true;
} }
uint32_t Voice::_GetBlockSampleCount(SampleFormat fmt) if (dead && (!m_curSample || m_voxState == VoiceState::KeyOff) &&
m_sampleEndTrap.macroId == 0xffff &&
m_messageTrap.macroId == 0xffff &&
(!m_curSample || (m_curSample && m_volAdsr.isComplete())))
{ {
switch (fmt) m_voxState = VoiceState::Dead;
{ m_backendVoice->stop();
default:
return 1;
case Voice::SampleFormat::DSP:
return 14;
case Voice::SampleFormat::N64:
return 64;
} }
} }
@@ -445,23 +471,10 @@ 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;
/* Process SoundMacro; bootstrapping sample if needed */
bool dead = m_state.advance(*this, samples / m_sampleRate);
/* Process per-block evaluators here */
if (m_state.m_pedalSel)
{
bool pedal = m_state.m_pedalSel.evaluate(*this, m_state) >= 1.f;
if (pedal != m_sustained)
setPedal(pedal);
}
if (m_curSample) if (m_curSample)
{ {
uint32_t blockSampleCount = _GetBlockSampleCount(m_curFormat); uint32_t blockSampleCount = _GetBlockSampleCount(m_curFormat);
uint32_t block; uint32_t block;
int32_t curPitch = m_curPitch;
bool refresh = false;
bool looped = true; bool looped = true;
while (looped && samplesRem) while (looped && samplesRem)
@@ -517,7 +530,7 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
{ {
++samplesProc; ++samplesProc;
++m_curSamplePos; ++m_curSamplePos;
refresh |= _advanceSample(data[i], curPitch); _advanceSample(data[i]);
} }
samplesRem -= decSamples; samplesRem -= decSamples;
@@ -582,7 +595,7 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
{ {
++samplesProc; ++samplesProc;
++m_curSamplePos; ++m_curSamplePos;
refresh |= _advanceSample(data[i], curPitch); _advanceSample(data[i]);
} }
samplesRem -= decSamples; samplesRem -= decSamples;
@@ -598,22 +611,12 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
break; break;
} }
} }
if (refresh)
_setTotalPitch(curPitch + m_pitchSweep1 + m_pitchSweep2 + m_pitchWheelVal, true);
} }
else else
memset(data, 0, sizeof(int16_t) * samples); memset(data, 0, sizeof(int16_t) * samples);
if (dead && (!m_curSample || m_voxState == VoiceState::KeyOff) && if (m_voxState == VoiceState::Dead)
m_sampleEndTrap.macroId == 0xffff &&
m_messageTrap.macroId == 0xffff &&
(!m_curSample || (m_curSample && m_volAdsr.isComplete())))
{
m_curSample = nullptr; m_curSample = nullptr;
m_voxState = VoiceState::Dead;
m_backendVoice->stop();
}
return samples; return samples;
} }
@@ -795,6 +798,7 @@ void Voice::startSample(int16_t sampId, int32_t offset)
m_pitchDirty = true; m_pitchDirty = true;
_setPitchWheel(m_curPitchWheel); _setPitchWheel(m_curPitchWheel);
m_backendVoice->resetSampleRate(m_curSample->first.m_sampleRate); m_backendVoice->resetSampleRate(m_curSample->first.m_sampleRate);
m_needsSlew = false;
int32_t numSamples = m_curSample->first.m_numSamples & 0xffffff; int32_t numSamples = m_curSample->first.m_numSamples & 0xffffff;
if (offset) if (offset)
@@ -1001,7 +1005,7 @@ void Voice::setPitchSweep1(uint8_t times, int16_t add)
{ {
m_pitchSweep1 = 0; m_pitchSweep1 = 0;
m_pitchSweep1It = 0; m_pitchSweep1It = 0;
m_pitchSweep1Times = times * 160; m_pitchSweep1Times = times;
m_pitchSweep1Add = add; m_pitchSweep1Add = add;
} }
@@ -1009,7 +1013,7 @@ void Voice::setPitchSweep2(uint8_t times, int16_t add)
{ {
m_pitchSweep2 = 0; m_pitchSweep2 = 0;
m_pitchSweep2It = 0; m_pitchSweep2It = 0;
m_pitchSweep2Times = times * 160; m_pitchSweep2Times = times;
m_pitchSweep2Add = add; m_pitchSweep2Add = add;
} }