mirror of https://github.com/AxioDL/amuse.git
Work on Voice state and SurroundProfiles
This commit is contained in:
parent
60f873e76e
commit
1102d50f8f
|
@ -16,7 +16,9 @@ set(SOURCES
|
||||||
lib/EffectReverbHi.cpp
|
lib/EffectReverbHi.cpp
|
||||||
lib/EffectReverbStd.cpp
|
lib/EffectReverbStd.cpp
|
||||||
lib/EffectChorus.cpp
|
lib/EffectChorus.cpp
|
||||||
lib/EffectDelay.cpp)
|
lib/EffectDelay.cpp
|
||||||
|
lib/SurroundProfiles.cpp
|
||||||
|
lib/dsp.c)
|
||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
include/amuse/AudioGroup.hpp
|
include/amuse/AudioGroup.hpp
|
||||||
|
@ -41,12 +43,14 @@ set(HEADERS
|
||||||
include/amuse/EffectReverbStd.hpp
|
include/amuse/EffectReverbStd.hpp
|
||||||
include/amuse/EffectChorus.hpp
|
include/amuse/EffectChorus.hpp
|
||||||
include/amuse/EffectDelay.hpp
|
include/amuse/EffectDelay.hpp
|
||||||
|
include/amuse/SurroundProfiles.hpp
|
||||||
include/amuse/Common.hpp
|
include/amuse/Common.hpp
|
||||||
include/amuse/amuse.hpp)
|
include/amuse/amuse.hpp
|
||||||
|
include/amuse/dsp.h)
|
||||||
|
|
||||||
unset(EXTRAS)
|
unset(EXTRAS)
|
||||||
if(TARGET boo)
|
if(TARGET boo)
|
||||||
include_directories(${BOO_INCLUDE_DIR} ${LOGVISOR_INCLUDE_DIR})
|
include_directories(${BOO_INCLUDE_DIR} ${LOGVISOR_INCLUDE_DIR} ${ATHENA_INCLUDE_DIR})
|
||||||
list(APPEND EXTRAS lib/BooBackend.cpp include/amuse/BooBackend.hpp)
|
list(APPEND EXTRAS lib/BooBackend.cpp include/amuse/BooBackend.hpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -59,6 +63,6 @@ add_library(amuse
|
||||||
${EXTRAS})
|
${EXTRAS})
|
||||||
|
|
||||||
if(TARGET boo)
|
if(TARGET boo)
|
||||||
add_executable(amusetool driver/main.cpp)
|
add_executable(amusetool WIN32 driver/main.cpp)
|
||||||
target_link_libraries(amusetool amuse boo ${BOO_SYS_LIBS} logvisor)
|
target_link_libraries(amusetool amuse boo ${BOO_SYS_LIBS} logvisor athena-core)
|
||||||
endif()
|
endif()
|
||||||
|
|
501
driver/main.cpp
501
driver/main.cpp
|
@ -1,110 +1,423 @@
|
||||||
#include "amuse/amuse.hpp"
|
#include "amuse/amuse.hpp"
|
||||||
#include "amuse/BooBackend.hpp"
|
#include "amuse/BooBackend.hpp"
|
||||||
|
#include "athena/FileReader.hpp"
|
||||||
|
#include "boo/boo.hpp"
|
||||||
#include "boo/audiodev/IAudioVoiceEngine.hpp"
|
#include "boo/audiodev/IAudioVoiceEngine.hpp"
|
||||||
#include "logvisor/logvisor.hpp"
|
#include "logvisor/logvisor.hpp"
|
||||||
|
#include "optional.hpp"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#else
|
||||||
|
#include <conio.h>
|
||||||
|
#endif
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
static logvisor::Module Log("amusetool");
|
static logvisor::Module Log("amusetool");
|
||||||
|
|
||||||
static amuse::IntrusiveAudioGroupData LoadFromArgs(int argc, char* argv[],
|
static amuse::IntrusiveAudioGroupData LoadFromArgs(int argc, const boo::SystemChar** argv,
|
||||||
std::string& descOut, bool& good)
|
std::string& descOut, bool& good)
|
||||||
{
|
{
|
||||||
|
if (argc > 1)
|
||||||
|
{
|
||||||
|
/* First attempt single-file case */
|
||||||
|
std::experimental::optional<athena::io::FileReader> r;
|
||||||
|
r.emplace(argv[1], 32 * 1024, false);
|
||||||
|
if (!r->hasError())
|
||||||
|
{
|
||||||
|
char testBuf[6];
|
||||||
|
if (r->readBytesToBuf(testBuf, 6) == 6)
|
||||||
|
{
|
||||||
|
if (!memcmp(testBuf, "Audio/", 6))
|
||||||
|
{
|
||||||
|
/* Metroid Prime 1 container */
|
||||||
|
r->seek(0, athena::SeekOrigin::Begin);
|
||||||
|
r->readString();
|
||||||
|
descOut = "Metroid Prime (" + r->readString() + ")";
|
||||||
|
|
||||||
|
std::unique_ptr<uint8_t[]> pool = r->readUBytes(r->readUint32Big());
|
||||||
|
std::unique_ptr<uint8_t[]> proj = r->readUBytes(r->readUint32Big());
|
||||||
|
std::unique_ptr<uint8_t[]> samp = r->readUBytes(r->readUint32Big());
|
||||||
|
std::unique_ptr<uint8_t[]> sdir = r->readUBytes(r->readUint32Big());
|
||||||
|
|
||||||
|
good = true;
|
||||||
|
return {proj.release(), pool.release(), sdir.release(), samp.release()};
|
||||||
|
}
|
||||||
|
else if (amuse::SBig(*reinterpret_cast<uint32_t*>(testBuf)) == 0x1)
|
||||||
|
{
|
||||||
|
/* Metroid Prime 2 container */
|
||||||
|
r->seek(0, athena::SeekOrigin::Begin);
|
||||||
|
r->readUint32Big();
|
||||||
|
descOut = "Metroid Prime 2 (" + r->readString() + ")";
|
||||||
|
|
||||||
|
r->readUint16Big();
|
||||||
|
uint32_t poolSz = r->readUint32Big();
|
||||||
|
uint32_t projSz = r->readUint32Big();
|
||||||
|
uint32_t sdirSz = r->readUint32Big();
|
||||||
|
uint32_t sampSz = r->readUint32Big();
|
||||||
|
|
||||||
|
std::unique_ptr<uint8_t[]> pool = r->readUBytes(poolSz);
|
||||||
|
std::unique_ptr<uint8_t[]> proj = r->readUBytes(projSz);
|
||||||
|
std::unique_ptr<uint8_t[]> sdir = r->readUBytes(sdirSz);
|
||||||
|
std::unique_ptr<uint8_t[]> samp = r->readUBytes(sampSz);
|
||||||
|
|
||||||
|
good = true;
|
||||||
|
return {proj.release(), pool.release(), sdir.release(), samp.release()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Now attempt multi-file case */
|
||||||
|
char path[1024];
|
||||||
|
|
||||||
|
/* Project */
|
||||||
|
snprintf(path, 1024, "%s.pro", argv[1]);
|
||||||
|
r.emplace(path, 32 * 1024, false);
|
||||||
|
if (r->hasError())
|
||||||
|
{
|
||||||
|
snprintf(path, 1024, "%s.proj", argv[1]);
|
||||||
|
r.emplace(path, 32 * 1024, false);
|
||||||
|
if (r->hasError())
|
||||||
|
return {nullptr, nullptr, nullptr, nullptr};
|
||||||
|
}
|
||||||
|
std::unique_ptr<uint8_t[]> proj = r->readUBytes(r->length());
|
||||||
|
|
||||||
|
/* Pool */
|
||||||
|
snprintf(path, 1024, "%s.poo", argv[1]);
|
||||||
|
r.emplace(path, 32 * 1024, false);
|
||||||
|
if (r->hasError())
|
||||||
|
{
|
||||||
|
snprintf(path, 1024, "%s.pool", argv[1]);
|
||||||
|
r.emplace(path, 32 * 1024, false);
|
||||||
|
if (r->hasError())
|
||||||
|
return {nullptr, nullptr, nullptr, nullptr};
|
||||||
|
}
|
||||||
|
std::unique_ptr<uint8_t[]> pool = r->readUBytes(r->length());
|
||||||
|
|
||||||
|
/* Sdir */
|
||||||
|
snprintf(path, 1024, "%s.sdi", argv[1]);
|
||||||
|
r.emplace(path, 32 * 1024, false);
|
||||||
|
if (r->hasError())
|
||||||
|
{
|
||||||
|
snprintf(path, 1024, "%s.sdir", argv[1]);
|
||||||
|
r.emplace(path, 32 * 1024, false);
|
||||||
|
if (r->hasError())
|
||||||
|
return {nullptr, nullptr, nullptr, nullptr};
|
||||||
|
}
|
||||||
|
std::unique_ptr<uint8_t[]> sdir = r->readUBytes(r->length());
|
||||||
|
|
||||||
|
/* Samp */
|
||||||
|
snprintf(path, 1024, "%s.sam", argv[1]);
|
||||||
|
r.emplace(path, 32 * 1024, false);
|
||||||
|
if (r->hasError())
|
||||||
|
{
|
||||||
|
snprintf(path, 1024, "%s.samp", argv[1]);
|
||||||
|
r.emplace(path, 32 * 1024, false);
|
||||||
|
if (r->hasError())
|
||||||
|
return {nullptr, nullptr, nullptr, nullptr};
|
||||||
|
}
|
||||||
|
std::unique_ptr<uint8_t[]> samp = r->readUBytes(r->length());
|
||||||
|
|
||||||
|
descOut = "4 RAW Chunk Files";
|
||||||
|
good = true;
|
||||||
|
return {proj.release(), pool.release(), sdir.release(), samp.release()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {nullptr, nullptr, nullptr, nullptr};
|
return {nullptr, nullptr, nullptr, nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
struct AppCallback;
|
||||||
|
|
||||||
|
struct EventCallback : boo::IWindowCallback
|
||||||
|
{
|
||||||
|
AppCallback& m_app;
|
||||||
|
public:
|
||||||
|
void charKeyDown(unsigned long charCode, boo::EModifierKey mods, bool isRepeat);
|
||||||
|
void charKeyUp(unsigned long charCode, boo::EModifierKey mods);
|
||||||
|
void specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods, bool isRepeat);
|
||||||
|
void specialKeyUp(boo::ESpecialKey key, boo::EModifierKey mods);
|
||||||
|
void resized(const boo::SWindowRect&, const boo::SWindowRect&) {}
|
||||||
|
|
||||||
|
EventCallback(AppCallback& app) : m_app(app) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AppCallback : boo::IApplicationCallback
|
||||||
|
{
|
||||||
|
int m_argc;
|
||||||
|
const boo::SystemChar** m_argv;
|
||||||
|
|
||||||
|
/* Boo window and events */
|
||||||
|
EventCallback m_eventRec;
|
||||||
|
boo::DeferredWindowEvents<EventCallback> m_events;
|
||||||
|
boo::IWindow* m_win = nullptr;
|
||||||
|
|
||||||
|
/* Amuse engine */
|
||||||
|
std::experimental::optional<amuse::Engine> m_engine;
|
||||||
|
int m_groupId;
|
||||||
|
bool m_sfxGroup;
|
||||||
|
|
||||||
|
/* Song playback selection */
|
||||||
|
int m_setupId = -1;
|
||||||
|
int m_chanId = -1;
|
||||||
|
|
||||||
|
/* SFX playback selection */
|
||||||
|
int m_sfxId = -1;
|
||||||
|
amuse::Voice* m_vox = nullptr;
|
||||||
|
|
||||||
|
/* Control state */
|
||||||
|
bool m_running = true;
|
||||||
|
bool m_wantsNext = false;
|
||||||
|
bool m_wantsPrev = false;
|
||||||
|
|
||||||
|
void SongLoop(const amuse::AudioGroup& group,
|
||||||
|
const amuse::SongGroupIndex& index)
|
||||||
|
{
|
||||||
|
printf("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░\n"
|
||||||
|
"░░░ ████ ████ ┃ ████ ████ ████ ┃ ████ ████ ░░░\n"
|
||||||
|
"░░░ ████ ████ ┃ ████ ████ ████ ┃ ████ ████ ░░░\n"
|
||||||
|
"░░░ ▌W▐█ ▌E▐█ ┃ ▌T▐█ ▌Y▐█ ▌U▐█ ┃ ▌O▐█ ▌P▐█ ░░░\n"
|
||||||
|
"░░░ │ │ ┃ │ │ │ ┃ │ │ ░░░\n"
|
||||||
|
"░░░ A │ S │ D ┃ F │ G │ H │ J ┃ K │ L │ ; ░░░\n"
|
||||||
|
"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░\n"
|
||||||
|
"<left/right>: cycle MIDI setup / channel, <up/down>: volume\n"
|
||||||
|
"<tab>: sustain pedal, <1/2>: pitch wheel, <3-8>: mod wheel\n"
|
||||||
|
"<Z/X>: octave, <C/V>: velocity\n"
|
||||||
|
"<Q>: quit\n");
|
||||||
|
|
||||||
|
std::map<int, const std::array<amuse::SongGroupIndex::MIDISetup, 16>*> sortEntries
|
||||||
|
(index.m_midiSetups.cbegin(), index.m_midiSetups.cend());
|
||||||
|
auto setupIt = sortEntries.cbegin();
|
||||||
|
if (setupIt != sortEntries.cend())
|
||||||
|
{
|
||||||
|
m_setupId = setupIt->first;
|
||||||
|
m_chanId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (m_running)
|
||||||
|
{
|
||||||
|
m_events.dispatchEvents();
|
||||||
|
|
||||||
|
if (m_wantsNext)
|
||||||
|
{
|
||||||
|
m_wantsNext = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
m_engine->pumpEngine();
|
||||||
|
m_win->waitForRetrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SFXLoop(const amuse::AudioGroup& group,
|
||||||
|
const amuse::SFXGroupIndex& index)
|
||||||
|
{
|
||||||
|
printf("<space>: keyon/keyon, <left/right>: cycle SFX, <up/down>: volume\n"
|
||||||
|
"<Q>: quit\n");
|
||||||
|
|
||||||
|
std::map<uint16_t, const amuse::SFXGroupIndex::SFXEntry*> sortEntries
|
||||||
|
(index.m_sfxEntries.cbegin(), index.m_sfxEntries.cend());
|
||||||
|
auto sfxIt = sortEntries.cbegin();
|
||||||
|
if (sfxIt != sortEntries.cend())
|
||||||
|
{
|
||||||
|
m_sfxId = sfxIt->first;
|
||||||
|
m_vox = m_engine->fxStart(m_sfxId, 1.f, 0.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (m_running)
|
||||||
|
{
|
||||||
|
m_events.dispatchEvents();
|
||||||
|
m_engine->pumpEngine();
|
||||||
|
m_win->waitForRetrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void charKeyDown(unsigned long charCode)
|
||||||
|
{
|
||||||
|
if (m_sfxGroup)
|
||||||
|
{
|
||||||
|
switch (charCode)
|
||||||
|
{
|
||||||
|
case ' ':
|
||||||
|
if (m_vox && m_vox->state() == amuse::VoiceState::Playing)
|
||||||
|
m_vox->keyOff();
|
||||||
|
else if (m_sfxId != -1)
|
||||||
|
m_vox = m_engine->fxStart(m_sfxId, 1.f, 0.f);
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void charKeyUp(unsigned long charCode)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int appMain(boo::IApplication* app)
|
||||||
|
{
|
||||||
|
/* Event window */
|
||||||
|
m_win = app->newWindow("amusetool", 1);
|
||||||
|
m_win->setWindowFrame(100, 100, 100, 100);
|
||||||
|
m_win->setCallback(&m_events);
|
||||||
|
m_win->showWindow();
|
||||||
|
|
||||||
|
/* Load data */
|
||||||
|
std::string desc;
|
||||||
|
bool good = false;
|
||||||
|
amuse::IntrusiveAudioGroupData data = LoadFromArgs(m_argc, m_argv, desc, good);
|
||||||
|
if (!good)
|
||||||
|
Log.report(logvisor::Fatal, "incomplete data in args");
|
||||||
|
printf("Found '%s' Audio Group data\n", desc.c_str());
|
||||||
|
|
||||||
|
/* Load project to assemble group list */
|
||||||
|
amuse::AudioGroupProject proj(data.getProj());
|
||||||
|
|
||||||
|
/* Get group selection from user */
|
||||||
|
size_t totalGroups = proj.sfxGroups().size() + proj.songGroups().size();
|
||||||
|
if (totalGroups > 1)
|
||||||
|
{
|
||||||
|
/* Ask user to specify which group in project */
|
||||||
|
printf("Multiple Audio Groups discovered:\n");
|
||||||
|
for (const auto& pair : proj.songGroups())
|
||||||
|
{
|
||||||
|
printf(" %d (SongGroup) %" PRISize " normal-pages, %" PRISize " drum-pages\n",
|
||||||
|
pair.first, pair.second.m_normPages.size(), pair.second.m_drumPages.size());
|
||||||
|
}
|
||||||
|
for (const auto& pair : proj.sfxGroups())
|
||||||
|
{
|
||||||
|
printf(" %d (SFXGroup) %" PRISize " sfx-entries\n",
|
||||||
|
pair.first, pair.second.m_sfxEntries.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
int userSel = 0;
|
||||||
|
printf("Enter Group Number: ");
|
||||||
|
if (scanf("%d", &userSel) <= 0)
|
||||||
|
Log.report(logvisor::Fatal, "unable to parse prompt");
|
||||||
|
|
||||||
|
if (proj.getSongGroupIndex(userSel))
|
||||||
|
{
|
||||||
|
m_groupId = userSel;
|
||||||
|
m_sfxGroup = false;
|
||||||
|
}
|
||||||
|
else if (proj.getSFXGroupIndex(userSel))
|
||||||
|
{
|
||||||
|
m_groupId = userSel;
|
||||||
|
m_sfxGroup = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Log.report(logvisor::Fatal, "unable to find Group %d", userSel);
|
||||||
|
}
|
||||||
|
else if (totalGroups == 1)
|
||||||
|
{
|
||||||
|
/* Load one and only group */
|
||||||
|
if (proj.songGroups().size())
|
||||||
|
{
|
||||||
|
const auto& pair = *proj.songGroups().cbegin();
|
||||||
|
m_groupId = pair.first;
|
||||||
|
m_sfxGroup = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto& pair = *proj.sfxGroups().cbegin();
|
||||||
|
m_groupId = pair.first;
|
||||||
|
m_sfxGroup = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Log.report(logvisor::Fatal, "empty project");
|
||||||
|
|
||||||
|
/* Build voice engine */
|
||||||
|
std::unique_ptr<boo::IAudioVoiceEngine> voxEngine = boo::NewAudioVoiceEngine();
|
||||||
|
amuse::BooBackendVoiceAllocator booBackend(*voxEngine);
|
||||||
|
m_engine.emplace(booBackend);
|
||||||
|
|
||||||
|
/* Load group into engine */
|
||||||
|
const amuse::AudioGroup* group = m_engine->addAudioGroup(m_groupId, data);
|
||||||
|
if (!group)
|
||||||
|
Log.report(logvisor::Fatal, "unable to add audio group");
|
||||||
|
|
||||||
|
/* Enter playback loop */
|
||||||
|
if (m_sfxGroup)
|
||||||
|
SFXLoop(*group, *proj.getSFXGroupIndex(m_groupId));
|
||||||
|
else
|
||||||
|
SongLoop(*group, *proj.getSongGroupIndex(m_groupId));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void appQuitting(boo::IApplication*)
|
||||||
|
{
|
||||||
|
m_running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppCallback(int argc, const boo::SystemChar** argv)
|
||||||
|
: m_argc(argc), m_argv(argv), m_eventRec(*this), m_events(m_eventRec) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void EventCallback::charKeyDown(unsigned long charCode, boo::EModifierKey mods, bool isRepeat)
|
||||||
|
{
|
||||||
|
if (isRepeat)
|
||||||
|
return;
|
||||||
|
m_app.charKeyDown(charCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventCallback::charKeyUp(unsigned long charCode, boo::EModifierKey mods)
|
||||||
|
{
|
||||||
|
m_app.charKeyUp(charCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventCallback::specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods, bool isRepeat)
|
||||||
|
{
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case boo::ESpecialKey::Left:
|
||||||
|
m_app.m_wantsPrev = true;
|
||||||
|
break;
|
||||||
|
case boo::ESpecialKey::Right:
|
||||||
|
m_app.m_wantsNext = true;
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventCallback::specialKeyUp(boo::ESpecialKey key, boo::EModifierKey mods)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
int wmain(int argc, const boo::SystemChar** argv)
|
||||||
|
#else
|
||||||
|
int main(int argc, const boo::SystemChar** argv)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
logvisor::RegisterConsoleLogger();
|
logvisor::RegisterConsoleLogger();
|
||||||
|
AppCallback app(argc, argv);
|
||||||
/* Load data */
|
int ret = boo::ApplicationRun(boo::IApplication::EPlatformType::Auto,
|
||||||
std::string desc;
|
app, _S("amusetool"), _S("Amuse Tool"), argc, argv);
|
||||||
bool good = false;
|
printf("IM DYING!!\n");
|
||||||
amuse::IntrusiveAudioGroupData data = LoadFromArgs(argc, argv, desc, good);
|
return ret;
|
||||||
if (!good)
|
|
||||||
Log.report(logvisor::Fatal, "incomplete data in args");
|
|
||||||
printf("Found '%s' Audio Group data\n", desc.c_str());
|
|
||||||
|
|
||||||
/* Load project to assemble group list */
|
|
||||||
amuse::AudioGroupProject proj(data.getProj());
|
|
||||||
|
|
||||||
/* Get group selection from user */
|
|
||||||
int groupId;
|
|
||||||
bool sfxGroup;
|
|
||||||
size_t totalGroups = proj.sfxGroups().size() + proj.songGroups().size();
|
|
||||||
if (totalGroups > 1)
|
|
||||||
{
|
|
||||||
/* Ask user to specify which group in project */
|
|
||||||
printf("Multiple Audio Groups discovered:\n");
|
|
||||||
for (const auto& pair : proj.songGroups())
|
|
||||||
{
|
|
||||||
printf(" %d (SongGroup) %" PRISize " normal-pages, %" PRISize " drum-pages\n",
|
|
||||||
pair.first, pair.second.m_normPages.size(), pair.second.m_drumPages.size());
|
|
||||||
}
|
|
||||||
for (const auto& pair : proj.sfxGroups())
|
|
||||||
{
|
|
||||||
printf(" %d (SFXGroup) %" PRISize " sfx-entries\n",
|
|
||||||
pair.first, pair.second.m_sfxEntries.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
int userSel = 0;
|
|
||||||
printf("Enter Group Number: ");
|
|
||||||
if (scanf("%d", &userSel) <= 0)
|
|
||||||
Log.report(logvisor::Fatal, "unable to parse prompt");
|
|
||||||
|
|
||||||
if (proj.getSongGroupIndex(userSel))
|
|
||||||
{
|
|
||||||
groupId = userSel;
|
|
||||||
sfxGroup = false;
|
|
||||||
}
|
|
||||||
else if (proj.getSFXGroupIndex(userSel))
|
|
||||||
{
|
|
||||||
groupId = userSel;
|
|
||||||
sfxGroup = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Log.report(logvisor::Fatal, "unable to find Group %d", userSel);
|
|
||||||
}
|
|
||||||
else if (totalGroups == 1)
|
|
||||||
{
|
|
||||||
/* Load one and only group */
|
|
||||||
if (proj.songGroups().size())
|
|
||||||
{
|
|
||||||
const auto& pair = *proj.songGroups().cbegin();
|
|
||||||
groupId = pair.first;
|
|
||||||
sfxGroup = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto& pair = *proj.sfxGroups().cbegin();
|
|
||||||
groupId = pair.first;
|
|
||||||
sfxGroup = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Log.report(logvisor::Fatal, "empty project");
|
|
||||||
|
|
||||||
/* Build voice engine */
|
|
||||||
std::unique_ptr<boo::IAudioVoiceEngine> voxEngine = boo::NewAudioVoiceEngine();
|
|
||||||
amuse::BooBackendVoiceAllocator booBackend(*voxEngine);
|
|
||||||
amuse::Engine engine(booBackend);
|
|
||||||
|
|
||||||
engine.addAudioGroup(groupId, data);
|
|
||||||
|
|
||||||
amuse::Voice* vox = engine.fxStart(1, 1.f, 0.f);
|
|
||||||
|
|
||||||
for (int f=0 ; f<300 ; ++f)
|
|
||||||
{
|
|
||||||
engine.pumpEngine();
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(15));
|
|
||||||
}
|
|
||||||
|
|
||||||
vox->keyOff();
|
|
||||||
|
|
||||||
for (int f=0 ; f<120 ; ++f)
|
|
||||||
{
|
|
||||||
engine.pumpEngine();
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(15));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int)
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
return wmain(argc+1, booArgv);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -9,11 +9,14 @@ namespace amuse
|
||||||
{
|
{
|
||||||
class AudioGroupData;
|
class AudioGroupData;
|
||||||
|
|
||||||
|
using Sample = std::pair<AudioGroupSampleDirectory::Entry,
|
||||||
|
AudioGroupSampleDirectory::ADPCMParms>;
|
||||||
|
|
||||||
class AudioGroup
|
class AudioGroup
|
||||||
{
|
{
|
||||||
int m_groupId;
|
int m_groupId;
|
||||||
AudioGroupPool m_pool;
|
|
||||||
AudioGroupProject m_proj;
|
AudioGroupProject m_proj;
|
||||||
|
AudioGroupPool m_pool;
|
||||||
AudioGroupSampleDirectory m_sdir;
|
AudioGroupSampleDirectory m_sdir;
|
||||||
const unsigned char* m_samp;
|
const unsigned char* m_samp;
|
||||||
bool m_valid;
|
bool m_valid;
|
||||||
|
@ -25,8 +28,10 @@ public:
|
||||||
bool sfxInGroup(int sfxId) const;
|
bool sfxInGroup(int sfxId) const;
|
||||||
bool songInGroup(int songId) const;
|
bool songInGroup(int songId) const;
|
||||||
|
|
||||||
const AudioGroupPool& getPool() const;
|
const Sample* getSample(int sfxId) const;
|
||||||
const AudioGroupSampleDirectory::Entry* getSfxEntry(int sfxId) const;
|
const unsigned char* getSampleData(uint32_t offset) const;
|
||||||
|
const AudioGroupProject& getProj() const {return m_proj;}
|
||||||
|
const AudioGroupPool& getPool() const {return m_pool;}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,14 @@
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
|
|
||||||
struct AudioGroupIndex {};
|
/** Common index members of SongGroups and SFXGroups */
|
||||||
|
struct AudioGroupIndex
|
||||||
|
{
|
||||||
|
const uint16_t* m_soundMacroIndex;
|
||||||
|
const uint16_t* m_tablesIndex;
|
||||||
|
const uint16_t* m_keymapsIndex;
|
||||||
|
const uint16_t* m_layersIndex;
|
||||||
|
};
|
||||||
|
|
||||||
/** Root index of SongGroup */
|
/** Root index of SongGroup */
|
||||||
struct SongGroupIndex : AudioGroupIndex
|
struct SongGroupIndex : AudioGroupIndex
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef __AMUSE_AUDIOGROUPSAMPLEDIR_HPP__
|
#ifndef __AMUSE_AUDIOGROUPSAMPLEDIR_HPP__
|
||||||
#define __AMUSE_AUDIOGROUPSAMPLEDIR_HPP__
|
#define __AMUSE_AUDIOGROUPSAMPLEDIR_HPP__
|
||||||
|
|
||||||
#include <vector>
|
#include <unordered_map>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
|
@ -31,11 +31,11 @@ public:
|
||||||
uint8_t m_lps;
|
uint8_t m_lps;
|
||||||
int16_t m_hist1;
|
int16_t m_hist1;
|
||||||
int16_t m_hist2;
|
int16_t m_hist2;
|
||||||
int16_t m_coefs[16];
|
int16_t m_coefs[8][2];
|
||||||
void swapBig();
|
void swapBig();
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
std::vector<std::pair<Entry, ADPCMParms>> m_entries;
|
std::unordered_map<uint16_t, std::pair<Entry, ADPCMParms>> m_entries;
|
||||||
public:
|
public:
|
||||||
AudioGroupSampleDirectory(const unsigned char* data);
|
AudioGroupSampleDirectory(const unsigned char* data);
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,6 +27,7 @@ public:
|
||||||
double sampleRate, bool dynamicPitch);
|
double sampleRate, bool dynamicPitch);
|
||||||
BooBackendVoice(boo::IAudioSubmix& submix, Voice& clientVox,
|
BooBackendVoice(boo::IAudioSubmix& submix, Voice& clientVox,
|
||||||
double sampleRate, bool dynamicPitch);
|
double sampleRate, bool dynamicPitch);
|
||||||
|
void resetSampleRate(double sampleRate);
|
||||||
void setMatrixCoefficients(const float coefs[8]);
|
void setMatrixCoefficients(const float coefs[8]);
|
||||||
void setPitchRatio(double ratio);
|
void setPitchRatio(double ratio);
|
||||||
void start();
|
void start();
|
||||||
|
@ -66,6 +67,7 @@ public:
|
||||||
BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine);
|
BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine);
|
||||||
std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch);
|
std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch);
|
||||||
std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx);
|
std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx);
|
||||||
|
AudioChannelSet getAvailableSet();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ class Engine
|
||||||
std::list<Emitter> m_activeEmitters;
|
std::list<Emitter> m_activeEmitters;
|
||||||
std::list<Sequencer> m_activeSequencers;
|
std::list<Sequencer> m_activeSequencers;
|
||||||
std::list<Submix> m_activeSubmixes;
|
std::list<Submix> m_activeSubmixes;
|
||||||
|
std::unordered_map<uint16_t, std::pair<AudioGroup*, ObjectId>> m_sfxLookup;
|
||||||
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
|
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
|
||||||
int m_nextVid = 0;
|
int m_nextVid = 0;
|
||||||
Voice* _allocateVoice(const AudioGroup& group, double sampleRate,
|
Voice* _allocateVoice(const AudioGroup& group, double sampleRate,
|
||||||
|
@ -35,8 +36,6 @@ class Engine
|
||||||
Submix* _allocateSubmix(Submix* smx);
|
Submix* _allocateSubmix(Submix* smx);
|
||||||
std::list<Voice>::iterator _destroyVoice(Voice* voice);
|
std::list<Voice>::iterator _destroyVoice(Voice* voice);
|
||||||
std::list<Submix>::iterator _destroySubmix(Submix* smx);
|
std::list<Submix>::iterator _destroySubmix(Submix* smx);
|
||||||
AudioGroup* _findGroupFromSfxId(int sfxId, const AudioGroupSampleDirectory::Entry*& entOut) const;
|
|
||||||
AudioGroup* _findGroupFromSongId(int songId) const;
|
|
||||||
public:
|
public:
|
||||||
Engine(IBackendVoiceAllocator& backend);
|
Engine(IBackendVoiceAllocator& backend);
|
||||||
|
|
||||||
|
@ -64,7 +63,7 @@ public:
|
||||||
Submix* smx=nullptr);
|
Submix* smx=nullptr);
|
||||||
|
|
||||||
/** Start song playing from loaded audio groups */
|
/** Start song playing from loaded audio groups */
|
||||||
Sequencer* seqPlay(int songId, const unsigned char* arrData);
|
Sequencer* seqPlay(int groupId, int songId, const unsigned char* arrData);
|
||||||
|
|
||||||
/** Find voice from VoiceId */
|
/** Find voice from VoiceId */
|
||||||
Voice* findVoice(int vid);
|
Voice* findVoice(int vid);
|
||||||
|
|
|
@ -21,15 +21,7 @@ enum class ObjectType : uint8_t
|
||||||
|
|
||||||
/** Common ID structure statically tagging
|
/** Common ID structure statically tagging
|
||||||
* SoundMacros, Tables, Keymaps, Layers */
|
* SoundMacros, Tables, Keymaps, Layers */
|
||||||
struct ObjectId
|
using ObjectId = uint16_t;
|
||||||
{
|
|
||||||
ObjectType type = ObjectType::Invalid;
|
|
||||||
uint8_t id = 0xff;
|
|
||||||
bool operator ==(const ObjectId& other) const
|
|
||||||
{return *reinterpret_cast<const uint16_t*>(this) == reinterpret_cast<const uint16_t&>(other);}
|
|
||||||
bool operator !=(const ObjectId& other) const
|
|
||||||
{return *reinterpret_cast<const uint16_t*>(this) != reinterpret_cast<const uint16_t&>(other);}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Common 'engine child' class */
|
/** Common 'engine child' class */
|
||||||
class Entity
|
class Entity
|
||||||
|
@ -65,13 +57,4 @@ using Curve = uint8_t[128];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace std
|
|
||||||
{
|
|
||||||
template <> struct hash<amuse::ObjectId>
|
|
||||||
{
|
|
||||||
size_t operator()(const amuse::ObjectId& val) const noexcept
|
|
||||||
{return std::hash<uint16_t>()(reinterpret_cast<const uint16_t&>(val));}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // __AMUSE_ENTITY_HPP__
|
#endif // __AMUSE_ENTITY_HPP__
|
||||||
|
|
|
@ -33,6 +33,9 @@ class IBackendVoice
|
||||||
public:
|
public:
|
||||||
virtual ~IBackendVoice() = default;
|
virtual ~IBackendVoice() = default;
|
||||||
|
|
||||||
|
/** Set new sample rate into platform voice (may result in artifacts while playing) */
|
||||||
|
virtual void resetSampleRate(double sampleRate)=0;
|
||||||
|
|
||||||
/** 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])=0;
|
virtual void setMatrixCoefficients(const float coefs[8])=0;
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,16 @@ class IBackendSubmix;
|
||||||
class Voice;
|
class Voice;
|
||||||
class Submix;
|
class Submix;
|
||||||
|
|
||||||
|
/** Same enum as boo for describing speaker-configuration */
|
||||||
|
enum class AudioChannelSet
|
||||||
|
{
|
||||||
|
Stereo,
|
||||||
|
Quad,
|
||||||
|
Surround51,
|
||||||
|
Surround71,
|
||||||
|
Unknown = 0xff
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Client-implemented voice allocator
|
* @brief Client-implemented voice allocator
|
||||||
*/
|
*/
|
||||||
|
@ -19,10 +29,15 @@ public:
|
||||||
virtual ~IBackendVoiceAllocator() = default;
|
virtual ~IBackendVoiceAllocator() = default;
|
||||||
|
|
||||||
/** Amuse obtains a new voice from the platform this way */
|
/** Amuse obtains a new voice from the platform this way */
|
||||||
virtual std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch)=0;
|
virtual std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox,
|
||||||
|
double sampleRate,
|
||||||
|
bool dynamicPitch)=0;
|
||||||
|
|
||||||
/** Amuse obtains a new submix from the platform this way */
|
/** Amuse obtains a new submix from the platform this way */
|
||||||
virtual std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx)=0;
|
virtual std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx)=0;
|
||||||
|
|
||||||
|
/** Amuse obtains speaker-configuration from the platform this way */
|
||||||
|
virtual AudioChannelSet getAvailableSet()=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef __AMUSE_SURROUNDPROFILES_HPP__
|
||||||
|
#define __AMUSE_SURROUNDPROFILES_HPP__
|
||||||
|
|
||||||
|
#include "IBackendVoice.hpp"
|
||||||
|
#include "IBackendVoiceAllocator.hpp"
|
||||||
|
#include "Emitter.hpp"
|
||||||
|
|
||||||
|
namespace amuse
|
||||||
|
{
|
||||||
|
struct ReferenceVector;
|
||||||
|
|
||||||
|
class SurroundProfiles
|
||||||
|
{
|
||||||
|
static void SetupRefs(float matOut[8], const ChannelMap& map,
|
||||||
|
const Vector3f& listenEmit, const ReferenceVector refs[]);
|
||||||
|
public:
|
||||||
|
static void SetupMatrix(float matOut[8], const ChannelMap& map, AudioChannelSet set,
|
||||||
|
const Vector3f& emitPos, const Vector3f& listenPos,
|
||||||
|
const Vector3f& listenDir, const Vector3f& listenUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __AMUSE_SURROUNDPROFILES_HPP__
|
|
@ -7,6 +7,8 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
#include "SoundMacroState.hpp"
|
#include "SoundMacroState.hpp"
|
||||||
#include "Entity.hpp"
|
#include "Entity.hpp"
|
||||||
|
#include "AudioGroupSampleDirectory.hpp"
|
||||||
|
#include "AudioGroup.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
|
@ -35,7 +37,20 @@ class Voice : public Entity
|
||||||
uint8_t m_lastNote = 0; /**< Last MIDI semitone played by voice */
|
uint8_t m_lastNote = 0; /**< Last MIDI semitone played by voice */
|
||||||
uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */
|
uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */
|
||||||
|
|
||||||
|
const Sample* m_curSample = nullptr; /**< Current sample entry playing */
|
||||||
|
const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */
|
||||||
|
uint32_t m_curSamplePos = 0; /**< Current sample position */
|
||||||
|
uint32_t m_lastSamplePos = 0; /**< Last sample position (or last loop sample) */
|
||||||
|
int16_t m_prev1 = 0; /**< DSPADPCM prev sample */
|
||||||
|
int16_t m_prev2 = 0; /**< DSPADPCM prev-prev sample */
|
||||||
|
|
||||||
|
VoiceState m_voxState = VoiceState::Finished; /**< Current high-level state of voice */
|
||||||
|
bool m_sustained = false; /**< Sustain pedal pressed for this voice */
|
||||||
|
bool m_sustainKeyOff = false; /**< Keyoff event occured while sustained */
|
||||||
|
|
||||||
void _destroy();
|
void _destroy();
|
||||||
|
bool _checkSamplePos();
|
||||||
|
void _doKeyOff();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Voice(Engine& engine, const AudioGroup& group, int vid, bool emitter, Submix* smx);
|
Voice(Engine& engine, const AudioGroup& group, int vid, bool emitter, Submix* smx);
|
||||||
|
@ -49,7 +64,7 @@ public:
|
||||||
Submix* getSubmix() {return m_submix;}
|
Submix* getSubmix() {return m_submix;}
|
||||||
|
|
||||||
/** Get current state of voice */
|
/** Get current state of voice */
|
||||||
VoiceState state() const;
|
VoiceState state() const {return m_voxState;}
|
||||||
|
|
||||||
/** Get VoiceId of this voice (unique to all currently-playing voices) */
|
/** Get VoiceId of this voice (unique to all currently-playing voices) */
|
||||||
int vid() const {return m_vid;}
|
int vid() const {return m_vid;}
|
||||||
|
@ -60,34 +75,63 @@ public:
|
||||||
/** Load specified SoundMacro ID of within group into voice */
|
/** Load specified SoundMacro ID of within group into voice */
|
||||||
bool loadSoundMacro(ObjectId macroId, int macroStep=0, bool pushPc=false);
|
bool loadSoundMacro(ObjectId macroId, int macroStep=0, bool pushPc=false);
|
||||||
|
|
||||||
/** Signals voice to begin fade-out, eventually reaching silence */
|
/** Signals voice to begin fade-out (or defer if sustained), eventually reaching silence */
|
||||||
void keyOff();
|
void keyOff();
|
||||||
|
|
||||||
/** Sends numeric message to voice and all siblings */
|
/** Sends numeric message to voice and all siblings */
|
||||||
void message(int32_t val);
|
void message(int32_t val);
|
||||||
|
|
||||||
|
/** Start playing specified sample from within group, optionally by sample offset */
|
||||||
void startSample(int16_t sampId, int32_t offset);
|
void startSample(int16_t sampId, int32_t offset);
|
||||||
|
|
||||||
|
/** Stop playing current sample */
|
||||||
void stopSample();
|
void stopSample();
|
||||||
|
|
||||||
|
/** Set current voice volume immediately */
|
||||||
void setVolume(float vol);
|
void setVolume(float vol);
|
||||||
|
|
||||||
|
/** Set current voice panning immediately */
|
||||||
void setPanning(float pan);
|
void setPanning(float pan);
|
||||||
|
|
||||||
|
/** Set current voice surround-panning immediately */
|
||||||
void setSurroundPanning(float span);
|
void setSurroundPanning(float span);
|
||||||
|
|
||||||
|
/** Set voice relative-pitch in cents */
|
||||||
void setPitchKey(int32_t cents);
|
void setPitchKey(int32_t cents);
|
||||||
|
|
||||||
|
/** Set voice modulation */
|
||||||
void setModulation(float mod);
|
void setModulation(float mod);
|
||||||
|
|
||||||
|
/** Set sustain status within voice; clearing will trigger a deferred keyoff */
|
||||||
void setPedal(bool pedal);
|
void setPedal(bool pedal);
|
||||||
|
|
||||||
|
/** Set doppler factor for voice */
|
||||||
void setDoppler(float doppler);
|
void setDoppler(float doppler);
|
||||||
|
|
||||||
|
/** Set reverb mix for voice */
|
||||||
void setReverbVol(float rvol);
|
void setReverbVol(float rvol);
|
||||||
|
|
||||||
|
/** Set envelope for voice */
|
||||||
void setAdsr(ObjectId adsrId);
|
void setAdsr(ObjectId adsrId);
|
||||||
|
|
||||||
|
/** Set pitch in absolute hertz */
|
||||||
void setPitchFrequency(uint32_t hz, uint16_t fine);
|
void setPitchFrequency(uint32_t hz, uint16_t fine);
|
||||||
|
|
||||||
|
/** Set pitch envelope */
|
||||||
void setPitchAdsr(ObjectId adsrId, int32_t cents);
|
void setPitchAdsr(ObjectId adsrId, int32_t cents);
|
||||||
|
|
||||||
|
/** Set effective pitch range via the pitchwheel controller */
|
||||||
void setPitchWheelRange(int8_t up, int8_t down);
|
void setPitchWheelRange(int8_t up, int8_t down);
|
||||||
|
|
||||||
|
/** Assign voice to keygroup for coordinated mass-silencing */
|
||||||
void setKeygroup(uint8_t kg) {m_keygroup = kg;}
|
void setKeygroup(uint8_t kg) {m_keygroup = kg;}
|
||||||
|
|
||||||
uint8_t getLastNote() const {return m_lastNote;}
|
uint8_t getLastNote() const {return m_lastNote;}
|
||||||
int8_t getCtrlValue(uint8_t ctrl) const;
|
int8_t getCtrlValue(uint8_t ctrl) const {}
|
||||||
void setCtrlValue(uint8_t ctrl, int8_t val);
|
void setCtrlValue(uint8_t ctrl, int8_t val) {}
|
||||||
int8_t getPitchWheel() const;
|
int8_t getPitchWheel() const {}
|
||||||
int8_t getModWheel() const;
|
int8_t getModWheel() const {}
|
||||||
int8_t getAftertouch() const;
|
int8_t getAftertouch() const {}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef _DSP_h
|
||||||
|
#define _DSP_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
static inline int16_t DSPSampClamp(int32_t val)
|
||||||
|
{
|
||||||
|
if (val < -32768) val = -32768;
|
||||||
|
else if (val > 32767) val = 32767;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned DSPDecompressFrameRanged(int16_t* out, const uint8_t* in,
|
||||||
|
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||||
|
unsigned firstSample, unsigned lastSample);
|
||||||
|
unsigned DSPDecompressFrame(int16_t* out, const uint8_t* in,
|
||||||
|
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||||
|
unsigned lastSample);
|
||||||
|
unsigned DSPDecompressFrameStereoStride(int16_t* out, const uint8_t* in,
|
||||||
|
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||||
|
unsigned lastSample);
|
||||||
|
unsigned DSPDecompressFrameStereoDupe(int16_t* out, const uint8_t* in,
|
||||||
|
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||||
|
unsigned lastSample);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // _DSP_h
|
|
@ -20,12 +20,17 @@ bool AudioGroup::songInGroup(int songId) const
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
const AudioGroupSampleDirectory::Entry* AudioGroup::getSfxEntry(int sfxId) const
|
const Sample* AudioGroup::getSample(int sfxId) const
|
||||||
{
|
{
|
||||||
for (const auto& ent : m_sdir.m_entries)
|
for (const auto& ent : m_sdir.m_entries)
|
||||||
if (ent.first.m_sfxId == sfxId)
|
if (ent.second.first.m_sfxId == sfxId)
|
||||||
return &ent.first;
|
return &ent.second;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const unsigned char* AudioGroup::getSampleData(uint32_t offset) const
|
||||||
|
{
|
||||||
|
return m_samp + offset;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,14 +49,17 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data)
|
||||||
GroupHeader header = *group;
|
GroupHeader header = *group;
|
||||||
header.swapBig();
|
header.swapBig();
|
||||||
|
|
||||||
|
AudioGroupIndex* bIdx = nullptr;
|
||||||
|
|
||||||
if (header.type == GroupType::Song)
|
if (header.type == GroupType::Song)
|
||||||
{
|
{
|
||||||
SongGroupIndex& idx = m_songGroups[header.groupId];
|
SongGroupIndex& idx = m_songGroups[header.groupId];
|
||||||
|
bIdx = &idx;
|
||||||
|
|
||||||
/* Normal pages */
|
/* Normal pages */
|
||||||
const SongGroupIndex::PageEntry* normEntries =
|
const SongGroupIndex::PageEntry* normEntries =
|
||||||
reinterpret_cast<const SongGroupIndex::PageEntry*>(data + header.pageTableOff);
|
reinterpret_cast<const SongGroupIndex::PageEntry*>(data + header.pageTableOff);
|
||||||
while (normEntries->objId.type != ObjectType::Invalid)
|
while (normEntries->objId != 0xffff)
|
||||||
{
|
{
|
||||||
idx.m_normPages[normEntries->programNo] = normEntries;
|
idx.m_normPages[normEntries->programNo] = normEntries;
|
||||||
++normEntries;
|
++normEntries;
|
||||||
|
@ -65,7 +68,7 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data)
|
||||||
/* Drum pages */
|
/* Drum pages */
|
||||||
const SongGroupIndex::PageEntry* drumEntries =
|
const SongGroupIndex::PageEntry* drumEntries =
|
||||||
reinterpret_cast<const SongGroupIndex::PageEntry*>(data + header.drumTableOff);
|
reinterpret_cast<const SongGroupIndex::PageEntry*>(data + header.drumTableOff);
|
||||||
while (drumEntries->objId.type != ObjectType::Invalid)
|
while (drumEntries->objId != 0xffff)
|
||||||
{
|
{
|
||||||
idx.m_drumPages[drumEntries->programNo] = drumEntries;
|
idx.m_drumPages[drumEntries->programNo] = drumEntries;
|
||||||
++drumEntries;
|
++drumEntries;
|
||||||
|
@ -85,17 +88,26 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data)
|
||||||
else if (header.type == GroupType::SFX)
|
else if (header.type == GroupType::SFX)
|
||||||
{
|
{
|
||||||
SFXGroupIndex& idx = m_sfxGroups[header.groupId];
|
SFXGroupIndex& idx = m_sfxGroups[header.groupId];
|
||||||
|
bIdx = &idx;
|
||||||
|
|
||||||
/* SFX entries */
|
/* SFX entries */
|
||||||
const SFXGroupIndex::SFXEntry* entries =
|
const SFXGroupIndex::SFXEntry* entries =
|
||||||
reinterpret_cast<const SFXGroupIndex::SFXEntry*>(data + header.pageTableOff);
|
reinterpret_cast<const SFXGroupIndex::SFXEntry*>(data + header.pageTableOff);
|
||||||
while (entries->objId.type != ObjectType::Invalid)
|
while (entries->objId != 0xffff)
|
||||||
{
|
{
|
||||||
idx.m_sfxEntries[SBig(entries->defineId)] = entries;
|
idx.m_sfxEntries[SBig(entries->defineId)] = entries;
|
||||||
++entries;
|
++entries;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bIdx)
|
||||||
|
{
|
||||||
|
bIdx->m_soundMacroIndex = reinterpret_cast<const uint16_t*>(data + header.soundMacroIdsOff);
|
||||||
|
bIdx->m_tablesIndex = reinterpret_cast<const uint16_t*>(data + header.tableIdsOff);
|
||||||
|
bIdx->m_keymapsIndex = reinterpret_cast<const uint16_t*>(data + header.keymapIdsOff);
|
||||||
|
bIdx->m_layersIndex = reinterpret_cast<const uint16_t*>(data + header.layerIdsOff);
|
||||||
|
}
|
||||||
|
|
||||||
group = reinterpret_cast<const GroupHeader*>(data + header.groupEndOff);
|
group = reinterpret_cast<const GroupHeader*>(data + header.groupEndOff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,11 @@ void AudioGroupSampleDirectory::ADPCMParms::swapBig()
|
||||||
m_bytesPerFrame = SBig(m_bytesPerFrame);
|
m_bytesPerFrame = SBig(m_bytesPerFrame);
|
||||||
m_hist1 = SBig(m_hist1);
|
m_hist1 = SBig(m_hist1);
|
||||||
m_hist2 = SBig(m_hist2);
|
m_hist2 = SBig(m_hist2);
|
||||||
for (int i=0 ; i<16 ; ++i)
|
for (int i=0 ; i<8 ; ++i)
|
||||||
m_coefs[i] = SBig(m_coefs[i]);
|
{
|
||||||
|
m_coefs[i][0] = SBig(m_coefs[i][0]);
|
||||||
|
m_coefs[i][1] = SBig(m_coefs[i][1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data)
|
AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data)
|
||||||
|
|
|
@ -23,6 +23,11 @@ BooBackendVoice::BooBackendVoice(boo::IAudioSubmix& submix, Voice& clientVox,
|
||||||
m_booVoice(submix.allocateNewMonoVoice(sampleRate, &m_cb, dynamicPitch))
|
m_booVoice(submix.allocateNewMonoVoice(sampleRate, &m_cb, dynamicPitch))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
void BooBackendVoice::resetSampleRate(double sampleRate)
|
||||||
|
{
|
||||||
|
m_booVoice->resetSampleRate(sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
void BooBackendVoice::setMatrixCoefficients(const float coefs[8])
|
void BooBackendVoice::setMatrixCoefficients(const float coefs[8])
|
||||||
{
|
{
|
||||||
m_booVoice->setMonoMatrixCoefficients(coefs);
|
m_booVoice->setMonoMatrixCoefficients(coefs);
|
||||||
|
@ -100,4 +105,9 @@ std::unique_ptr<IBackendSubmix> BooBackendVoiceAllocator::allocateSubmix(Submix&
|
||||||
return std::make_unique<BooBackendSubmix>(m_booEngine, clientSmx);
|
return std::make_unique<BooBackendSubmix>(m_booEngine, clientSmx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AudioChannelSet BooBackendVoiceAllocator::getAvailableSet()
|
||||||
|
{
|
||||||
|
return AudioChannelSet(m_booEngine.getAvailableSet());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "amuse/IBackendVoiceAllocator.hpp"
|
#include "amuse/IBackendVoiceAllocator.hpp"
|
||||||
#include "amuse/AudioGroupData.hpp"
|
#include "amuse/AudioGroupData.hpp"
|
||||||
#include "amuse/AudioGroup.hpp"
|
#include "amuse/AudioGroup.hpp"
|
||||||
|
#include "amuse/Common.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
|
@ -50,25 +51,6 @@ std::list<Submix>::iterator Engine::_destroySubmix(Submix* smx)
|
||||||
return m_activeSubmixes.erase(smx->m_engineIt);
|
return m_activeSubmixes.erase(smx->m_engineIt);
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioGroup* Engine::_findGroupFromSfxId(int sfxId, const AudioGroupSampleDirectory::Entry*& entOut) const
|
|
||||||
{
|
|
||||||
for (const auto& grp : m_audioGroups)
|
|
||||||
{
|
|
||||||
entOut = grp.second->getSfxEntry(sfxId);
|
|
||||||
if (entOut)
|
|
||||||
return grp.second.get();
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioGroup* Engine::_findGroupFromSongId(int songId) const
|
|
||||||
{
|
|
||||||
for (const auto& grp : m_audioGroups)
|
|
||||||
if (grp.second->songInGroup(songId))
|
|
||||||
return grp.second.get();
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Update all active audio entities and fill OS audio buffers as needed */
|
/** Update all active audio entities and fill OS audio buffers as needed */
|
||||||
void Engine::pumpEngine()
|
void Engine::pumpEngine()
|
||||||
{
|
{
|
||||||
|
@ -88,12 +70,28 @@ const AudioGroup* Engine::addAudioGroup(int groupId, const AudioGroupData& data)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
AudioGroup* ret = grp.get();
|
AudioGroup* ret = grp.get();
|
||||||
m_audioGroups.emplace(std::make_pair(groupId, std::move(grp)));
|
m_audioGroups.emplace(std::make_pair(groupId, std::move(grp)));
|
||||||
|
|
||||||
|
/* setup SFX index for contained objects */
|
||||||
|
for (const auto& pair : ret->getProj().sfxGroups())
|
||||||
|
{
|
||||||
|
const SFXGroupIndex& sfxGroup = pair.second;
|
||||||
|
m_sfxLookup.reserve(m_sfxLookup.size() + sfxGroup.m_sfxEntries.size());
|
||||||
|
for (const auto& pair : sfxGroup.m_sfxEntries)
|
||||||
|
m_sfxLookup[pair.first] = std::make_pair(ret, pair.second->objId);
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Remove audio group from engine */
|
/** Remove audio group from engine */
|
||||||
void Engine::removeAudioGroup(int groupId)
|
void Engine::removeAudioGroup(int groupId)
|
||||||
{
|
{
|
||||||
|
auto search = m_audioGroups.find(groupId);
|
||||||
|
if (search == m_audioGroups.end())
|
||||||
|
return;
|
||||||
|
AudioGroup* grp = search->second.get();
|
||||||
|
|
||||||
|
/* Destroy runtime entities within group */
|
||||||
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ;)
|
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ;)
|
||||||
{
|
{
|
||||||
if (it->getAudioGroup().groupId() == groupId)
|
if (it->getAudioGroup().groupId() == groupId)
|
||||||
|
@ -127,6 +125,14 @@ void Engine::removeAudioGroup(int groupId)
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* teardown SFX index for contained objects */
|
||||||
|
for (const auto& pair : grp->getProj().sfxGroups())
|
||||||
|
{
|
||||||
|
const SFXGroupIndex& sfxGroup = pair.second;
|
||||||
|
for (const auto& pair : sfxGroup.m_sfxEntries)
|
||||||
|
m_sfxLookup.erase(pair.first);
|
||||||
|
}
|
||||||
|
|
||||||
m_audioGroups.erase(groupId);
|
m_audioGroups.erase(groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,12 +178,15 @@ void Engine::removeSubmix(Submix* smx)
|
||||||
/** Start soundFX playing from loaded audio groups */
|
/** Start soundFX playing from loaded audio groups */
|
||||||
Voice* Engine::fxStart(int sfxId, float vol, float pan, Submix* smx)
|
Voice* Engine::fxStart(int sfxId, float vol, float pan, Submix* smx)
|
||||||
{
|
{
|
||||||
const AudioGroupSampleDirectory::Entry* entry;
|
auto search = m_sfxLookup.find(sfxId);
|
||||||
AudioGroup* grp = _findGroupFromSfxId(sfxId, entry);
|
if (search == m_sfxLookup.end())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
AudioGroup* grp = search->second.first;
|
||||||
if (!grp)
|
if (!grp)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
Voice* ret = _allocateVoice(*grp, entry->m_sampleRate, true, false, smx);
|
Voice* ret = _allocateVoice(*grp, 32000.0, true, false, smx);
|
||||||
ret->setVolume(vol);
|
ret->setVolume(vol);
|
||||||
ret->setPanning(pan);
|
ret->setPanning(pan);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -187,12 +196,15 @@ Voice* Engine::fxStart(int sfxId, float vol, float pan, Submix* smx)
|
||||||
Emitter* Engine::addEmitter(const Vector3f& pos, const Vector3f& dir, float maxDist,
|
Emitter* Engine::addEmitter(const Vector3f& pos, const Vector3f& dir, float maxDist,
|
||||||
float falloff, int sfxId, float minVol, float maxVol, Submix* smx)
|
float falloff, int sfxId, float minVol, float maxVol, Submix* smx)
|
||||||
{
|
{
|
||||||
const AudioGroupSampleDirectory::Entry* entry;
|
auto search = m_sfxLookup.find(sfxId);
|
||||||
AudioGroup* grp = _findGroupFromSfxId(sfxId, entry);
|
if (search == m_sfxLookup.end())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
AudioGroup* grp = search->second.first;
|
||||||
if (!grp)
|
if (!grp)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
Voice* vox = _allocateVoice(*grp, entry->m_sampleRate, true, true, smx);
|
Voice* vox = _allocateVoice(*grp, 32000.0, true, true, smx);
|
||||||
m_activeEmitters.emplace_back(*this, *grp, *vox);
|
m_activeEmitters.emplace_back(*this, *grp, *vox);
|
||||||
Emitter& ret = m_activeEmitters.back();
|
Emitter& ret = m_activeEmitters.back();
|
||||||
ret.setPos(pos);
|
ret.setPos(pos);
|
||||||
|
@ -206,7 +218,7 @@ Emitter* Engine::addEmitter(const Vector3f& pos, const Vector3f& dir, float maxD
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start song playing from loaded audio groups */
|
/** Start song playing from loaded audio groups */
|
||||||
Sequencer* Engine::seqPlay(int songId, const unsigned char* arrData)
|
Sequencer* Engine::seqPlay(int groupId, int songId, const unsigned char* arrData)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -511,7 +511,7 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
int32_t eval = int32_t(orgVel ? m_initVel : m_curVel) * scale / 127 + add;
|
int32_t eval = int32_t(orgVel ? m_initVel : m_curVel) * scale / 127 + add;
|
||||||
eval = std::max(0, std::min(127, eval));
|
eval = std::max(0, std::min(127, eval));
|
||||||
|
|
||||||
if (curve.id != 0)
|
if (curve != 0)
|
||||||
{
|
{
|
||||||
const Curve* curveData = vox.getAudioGroup().getPool().tableAsCurves(curve);
|
const Curve* curveData = vox.getAudioGroup().getPool().tableAsCurves(curve);
|
||||||
if (curveData)
|
if (curveData)
|
||||||
|
@ -558,7 +558,7 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
m_envelopeStart = m_curVel;
|
m_envelopeStart = m_curVel;
|
||||||
m_envelopeEnd = eval;
|
m_envelopeEnd = eval;
|
||||||
|
|
||||||
if (curve.id != 0)
|
if (curve != 0)
|
||||||
m_envelopeCurve = vox.getAudioGroup().getPool().tableAsCurves(curve);
|
m_envelopeCurve = vox.getAudioGroup().getPool().tableAsCurves(curve);
|
||||||
else
|
else
|
||||||
m_envelopeCurve = nullptr;
|
m_envelopeCurve = nullptr;
|
||||||
|
@ -632,7 +632,7 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
m_envelopeStart = 0.f;
|
m_envelopeStart = 0.f;
|
||||||
m_envelopeEnd = eval;
|
m_envelopeEnd = eval;
|
||||||
|
|
||||||
if (curve.id != 0)
|
if (curve != 0)
|
||||||
m_envelopeCurve = vox.getAudioGroup().getPool().tableAsCurves(curve);
|
m_envelopeCurve = vox.getAudioGroup().getPool().tableAsCurves(curve);
|
||||||
else
|
else
|
||||||
m_envelopeCurve = nullptr;
|
m_envelopeCurve = nullptr;
|
||||||
|
@ -1333,7 +1333,7 @@ void SoundMacroState::keyoffNotify(Voice& vox)
|
||||||
if (m_inWait && m_keyoffWait)
|
if (m_inWait && m_keyoffWait)
|
||||||
m_inWait = false;
|
m_inWait = false;
|
||||||
|
|
||||||
if (m_keyoffTrap.macroId.id != 0xff)
|
if (m_keyoffTrap.macroId != 0xffff)
|
||||||
{
|
{
|
||||||
if (m_keyoffTrap.macroId == m_header.m_macroId)
|
if (m_keyoffTrap.macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = m_keyoffTrap.macroStep;
|
m_pc.back().second = m_keyoffTrap.macroStep;
|
||||||
|
@ -1348,7 +1348,7 @@ void SoundMacroState::sampleEndNotify(Voice& vox)
|
||||||
if (m_inWait && m_sampleEndWait)
|
if (m_inWait && m_sampleEndWait)
|
||||||
m_inWait = false;
|
m_inWait = false;
|
||||||
|
|
||||||
if (m_sampleEndTrap.macroId.id != 0xff)
|
if (m_sampleEndTrap.macroId != 0xffff)
|
||||||
{
|
{
|
||||||
if (m_sampleEndTrap.macroId == m_header.m_macroId)
|
if (m_sampleEndTrap.macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = m_sampleEndTrap.macroStep;
|
m_pc.back().second = m_sampleEndTrap.macroStep;
|
||||||
|
@ -1361,7 +1361,7 @@ void SoundMacroState::messageNotify(Voice& vox, int32_t val)
|
||||||
{
|
{
|
||||||
m_messageQueue.push_back(val);
|
m_messageQueue.push_back(val);
|
||||||
|
|
||||||
if (m_messageTrap.macroId.id != 0xff)
|
if (m_messageTrap.macroId != 0xffff)
|
||||||
{
|
{
|
||||||
if (m_messageTrap.macroId == m_header.m_macroId)
|
if (m_messageTrap.macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = m_messageTrap.macroStep;
|
m_pc.back().second = m_messageTrap.macroStep;
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
#include "amuse/SurroundProfiles.hpp"
|
||||||
|
#include <cmath>
|
||||||
|
#include <cfloat>
|
||||||
|
|
||||||
|
namespace amuse
|
||||||
|
{
|
||||||
|
|
||||||
|
static float Dot(const Vector3f& a, const Vector3f& b)
|
||||||
|
{
|
||||||
|
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
static float Length(const Vector3f& a)
|
||||||
|
{
|
||||||
|
if (a[0] <= FLT_EPSILON && a[1] <= FLT_EPSILON && a[2] <= FLT_EPSILON)
|
||||||
|
return 0.f;
|
||||||
|
return std::sqrt(Dot(a, a));
|
||||||
|
}
|
||||||
|
|
||||||
|
static float Normalize(Vector3f& out, const Vector3f& in)
|
||||||
|
{
|
||||||
|
out[0] = in[0];
|
||||||
|
out[1] = in[1];
|
||||||
|
out[2] = in[2];
|
||||||
|
float dist = Length(out);
|
||||||
|
out[0] /= dist;
|
||||||
|
out[1] /= dist;
|
||||||
|
out[2] /= dist;
|
||||||
|
return dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Cross(Vector3f& out, const Vector3f& a, const Vector3f& b)
|
||||||
|
{
|
||||||
|
out[0] = a[1] * b[2] - a[2] * b[1];
|
||||||
|
out[1] = a[2] * b[0] - a[0] * b[2];
|
||||||
|
out[2] = a[0] * b[1] - a[1] * b[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
class SimpleMatrix
|
||||||
|
{
|
||||||
|
Vector3f m_mat[3];
|
||||||
|
public:
|
||||||
|
SimpleMatrix(const Vector3f& dir, const Vector3f& up)
|
||||||
|
{
|
||||||
|
Vector3f temp;
|
||||||
|
Normalize(temp, dir);
|
||||||
|
m_mat[0][1] = temp[0];
|
||||||
|
m_mat[1][1] = temp[1];
|
||||||
|
m_mat[2][1] = temp[2];
|
||||||
|
|
||||||
|
Normalize(temp, up);
|
||||||
|
m_mat[0][2] = temp[0];
|
||||||
|
m_mat[1][2] = temp[1];
|
||||||
|
m_mat[2][2] = temp[2];
|
||||||
|
|
||||||
|
Cross(temp, dir, up);
|
||||||
|
m_mat[0][0] = temp[0];
|
||||||
|
m_mat[1][0] = temp[1];
|
||||||
|
m_mat[2][0] = temp[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
void vecMult(Vector3f& out, const Vector3f& in)
|
||||||
|
{
|
||||||
|
out[0] = Dot(m_mat[0], in);
|
||||||
|
out[1] = Dot(m_mat[1], in);
|
||||||
|
out[2] = Dot(m_mat[2], in);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ReferenceVector
|
||||||
|
{
|
||||||
|
Vector3f vec;
|
||||||
|
float bias;
|
||||||
|
bool valid = false;
|
||||||
|
ReferenceVector() = default;
|
||||||
|
ReferenceVector(float x, float y, float z, float thres)
|
||||||
|
{
|
||||||
|
vec[0] = x;
|
||||||
|
vec[1] = y;
|
||||||
|
vec[2] = z;
|
||||||
|
bias = thres;
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const ReferenceVector StereoVectors[8] =
|
||||||
|
{
|
||||||
|
{-0.80901f, 0.58778f, 0.f, 0.3f},
|
||||||
|
{ 0.80901f, 0.58778f, 0.f, 0.3f},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const ReferenceVector QuadVectors[8] =
|
||||||
|
{
|
||||||
|
{-0.70710f, 0.70710f, 0.f, 0.1f},
|
||||||
|
{ 0.70710f, 0.70710f, 0.f, 0.1f},
|
||||||
|
{-0.70710f, -0.70710f, 0.f, 0.1f},
|
||||||
|
{ 0.70710f, -0.70710f, 0.f, 0.1f},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const ReferenceVector Sur51Vectors[8] =
|
||||||
|
{
|
||||||
|
{-0.70710f, 0.70710f, 0.f, 0.1f},
|
||||||
|
{ 0.70710f, 0.70710f, 0.f, 0.1f},
|
||||||
|
{-0.70710f, -0.70710f, 0.f, 0.1f},
|
||||||
|
{ 0.70710f, -0.70710f, 0.f, 0.1f},
|
||||||
|
{ 0.0f, 1.0f, 0.f, 0.1f},
|
||||||
|
{ 0.0f, 1.0f, 0.f, 1.0f},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const ReferenceVector Sur71Vectors[8] =
|
||||||
|
{
|
||||||
|
{-0.70710f, 0.70710f, 0.f, 0.1f},
|
||||||
|
{ 0.70710f, 0.70710f, 0.f, 0.1f},
|
||||||
|
{-0.70710f, -0.70710f, 0.f, 0.1f},
|
||||||
|
{ 0.70710f, -0.70710f, 0.f, 0.1f},
|
||||||
|
{ 0.0f, 1.0f, 0.f, 0.1f},
|
||||||
|
{ 0.0f, 1.0f, 0.f, 1.0f},
|
||||||
|
{-1.f, 0.0f, 0.f, 0.1f},
|
||||||
|
{ 1.f, 0.0f, 0.f, 0.1f},
|
||||||
|
};
|
||||||
|
|
||||||
|
void SurroundProfiles::SetupRefs(float matOut[8], const ChannelMap& map,
|
||||||
|
const Vector3f& listenEmit, const ReferenceVector refs[])
|
||||||
|
{
|
||||||
|
for (unsigned i=0 ; i<map.m_channelCount && i<8 ; ++i)
|
||||||
|
{
|
||||||
|
matOut[i] = 0.f;
|
||||||
|
if (map.m_channels[i] == AudioChannel::Unknown)
|
||||||
|
continue;
|
||||||
|
const ReferenceVector& refVec = refs[int(map.m_channels[i])];
|
||||||
|
if (!refVec.valid)
|
||||||
|
continue;
|
||||||
|
matOut[i] = std::max(1.f, Dot(listenEmit, refVec.vec) + refVec.bias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SurroundProfiles::SetupMatrix(float matOut[8], const ChannelMap& map, AudioChannelSet set,
|
||||||
|
const Vector3f& emitPos, const Vector3f& listenPos,
|
||||||
|
const Vector3f& listenHeading, const Vector3f& listenUp)
|
||||||
|
{
|
||||||
|
Vector3f listenDelta;
|
||||||
|
listenDelta[0] = emitPos[0] - listenPos[0];
|
||||||
|
listenDelta[1] = emitPos[1] - listenPos[1];
|
||||||
|
listenDelta[2] = emitPos[2] - listenPos[2];
|
||||||
|
|
||||||
|
Vector3f listenNorm;
|
||||||
|
float dist = Normalize(listenNorm, listenDelta);
|
||||||
|
|
||||||
|
SimpleMatrix listenerMat(listenHeading, listenUp);
|
||||||
|
Vector3f listenEmit;
|
||||||
|
listenerMat.vecMult(listenEmit, listenNorm);
|
||||||
|
|
||||||
|
/* Factor for each channel in set */
|
||||||
|
switch (set)
|
||||||
|
{
|
||||||
|
case AudioChannelSet::Stereo:
|
||||||
|
default:
|
||||||
|
SetupRefs(matOut, map, listenEmit, StereoVectors);
|
||||||
|
break;
|
||||||
|
case AudioChannelSet::Quad:
|
||||||
|
SetupRefs(matOut, map, listenEmit, QuadVectors);
|
||||||
|
break;
|
||||||
|
case AudioChannelSet::Surround51:
|
||||||
|
SetupRefs(matOut, map, listenEmit, Sur51Vectors);
|
||||||
|
break;
|
||||||
|
case AudioChannelSet::Surround71:
|
||||||
|
SetupRefs(matOut, map, listenEmit, Sur71Vectors);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
154
lib/Voice.cpp
154
lib/Voice.cpp
|
@ -1,6 +1,9 @@
|
||||||
#include "amuse/Voice.hpp"
|
#include "amuse/Voice.hpp"
|
||||||
#include "amuse/Submix.hpp"
|
#include "amuse/Submix.hpp"
|
||||||
#include "amuse/IBackendVoice.hpp"
|
#include "amuse/IBackendVoice.hpp"
|
||||||
|
#include "amuse/AudioGroup.hpp"
|
||||||
|
#include "amuse/dsp.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
|
@ -26,8 +29,83 @@ Voice::Voice(Engine& engine, const AudioGroup& group, ObjectId oid, int vid, boo
|
||||||
m_submix->m_activeVoices.insert(this);
|
m_submix->m_activeVoices.insert(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Voice::supplyAudio(size_t frames, int16_t* data)
|
bool Voice::_checkSamplePos()
|
||||||
{
|
{
|
||||||
|
if (m_curSamplePos >= m_lastSamplePos)
|
||||||
|
{
|
||||||
|
if (m_curSample->first.m_loopLengthSamples)
|
||||||
|
{
|
||||||
|
/* Turn over looped sample */
|
||||||
|
m_curSamplePos = m_curSample->first.m_loopStartSample;
|
||||||
|
m_prev1 = m_curSample->second.m_hist1;
|
||||||
|
m_prev2 = m_curSample->second.m_hist2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Notify sample end */
|
||||||
|
m_state.sampleEndNotify(*this);
|
||||||
|
m_curSample = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::_doKeyOff()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
|
{
|
||||||
|
uint32_t samplesRem = samples;
|
||||||
|
if (m_curSample)
|
||||||
|
{
|
||||||
|
uint32_t block = m_curSamplePos / 14;
|
||||||
|
uint32_t rem = m_curSamplePos % 14;
|
||||||
|
|
||||||
|
if (rem)
|
||||||
|
{
|
||||||
|
uint32_t decSamples = DSPDecompressFrameRanged(data, m_curSampleData + 8 * block,
|
||||||
|
m_curSample->second.m_coefs,
|
||||||
|
&m_prev1, &m_prev2, rem,
|
||||||
|
std::min(samplesRem,
|
||||||
|
m_lastSamplePos - block * 14));
|
||||||
|
m_curSamplePos += decSamples;
|
||||||
|
samplesRem -= decSamples;
|
||||||
|
data += decSamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_checkSamplePos())
|
||||||
|
{
|
||||||
|
if (samplesRem)
|
||||||
|
memset(data, 0, sizeof(int16_t) * samplesRem);
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (samplesRem)
|
||||||
|
{
|
||||||
|
block = m_curSamplePos / 14;
|
||||||
|
uint32_t decSamples = DSPDecompressFrame(data, m_curSampleData + 8 * block,
|
||||||
|
m_curSample->second.m_coefs,
|
||||||
|
&m_prev1, &m_prev2,
|
||||||
|
std::min(samplesRem,
|
||||||
|
m_lastSamplePos - block * 14));
|
||||||
|
m_curSamplePos += decSamples;
|
||||||
|
samplesRem -= decSamples;
|
||||||
|
data += decSamples;
|
||||||
|
|
||||||
|
if (_checkSamplePos())
|
||||||
|
{
|
||||||
|
if (samplesRem)
|
||||||
|
memset(data, 0, sizeof(int16_t) * samplesRem);
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
memset(data, 0, sizeof(int16_t) * samples);
|
||||||
|
|
||||||
|
return samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
Voice* Voice::startChildMacro(int8_t addNote, ObjectId macroId, int macroStep)
|
Voice* Voice::startChildMacro(int8_t addNote, ObjectId macroId, int macroStep)
|
||||||
|
@ -40,12 +118,40 @@ bool Voice::loadSoundMacro(ObjectId macroId, int macroStep, bool pushPc)
|
||||||
|
|
||||||
void Voice::keyOff()
|
void Voice::keyOff()
|
||||||
{
|
{
|
||||||
|
if (m_sustained)
|
||||||
|
m_sustainKeyOff = true;
|
||||||
|
else
|
||||||
|
_doKeyOff();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::message(int32_t val)
|
void Voice::message(int32_t val)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Voice::startSample(int16_t sampId, int32_t offset)
|
||||||
|
{
|
||||||
|
m_curSample = m_audioGroup.getSample(sampId);
|
||||||
|
if (m_curSample)
|
||||||
|
{
|
||||||
|
m_backendVoice->stop();
|
||||||
|
m_backendVoice->resetSampleRate(m_curSample->first.m_sampleRate);
|
||||||
|
m_backendVoice->start();
|
||||||
|
m_curSamplePos = offset;
|
||||||
|
m_curSampleData = m_audioGroup.getSampleData(m_curSample->first.m_sampleOff);
|
||||||
|
m_prev1 = 0;
|
||||||
|
m_prev2 = 0;
|
||||||
|
m_lastSamplePos = m_curSample->first.m_loopLengthSamples ?
|
||||||
|
(m_curSample->first.m_loopStartSample + m_curSample->first.m_loopLengthSamples) :
|
||||||
|
m_curSample->first.m_numSamples;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::stopSample()
|
||||||
|
{
|
||||||
|
m_backendVoice->stop();
|
||||||
|
m_curSample = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void Voice::setVolume(float vol)
|
void Voice::setVolume(float vol)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -54,4 +160,50 @@ void Voice::setPanning(float pan)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Voice::setSurroundPanning(float span)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::setPitchKey(int32_t cents)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::setModulation(float mod)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::setPedal(bool pedal)
|
||||||
|
{
|
||||||
|
if (m_sustained && !pedal && m_sustainKeyOff)
|
||||||
|
{
|
||||||
|
m_sustainKeyOff = false;
|
||||||
|
_doKeyOff();
|
||||||
|
}
|
||||||
|
m_sustained = pedal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::setDoppler(float doppler)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::setReverbVol(float rvol)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::setAdsr(ObjectId adsrId)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::setPitchFrequency(uint32_t hz, uint16_t fine)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::setPitchAdsr(ObjectId adsrId, int32_t cents)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::setPitchWheelRange(int8_t up, int8_t down)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
#include "amuse/dsp.h"
|
||||||
|
|
||||||
|
static const int32_t NibbleToInt[16] = {0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1};
|
||||||
|
|
||||||
|
unsigned DSPDecompressFrameRanged(int16_t* out, const uint8_t* in,
|
||||||
|
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||||
|
unsigned firstSample, unsigned lastSample)
|
||||||
|
{
|
||||||
|
uint8_t cIdx = (in[0]>>4) & 0xf;
|
||||||
|
int32_t factor1 = coefs[cIdx][0];
|
||||||
|
int32_t factor2 = coefs[cIdx][1];
|
||||||
|
uint8_t exp = in[0] & 0xf;
|
||||||
|
unsigned ret = 0;
|
||||||
|
for (int s=firstSample ; s<14 && s<lastSample ; ++s)
|
||||||
|
{
|
||||||
|
int32_t sampleData = (s&1)?
|
||||||
|
NibbleToInt[(in[s/2+1])&0xf]:
|
||||||
|
NibbleToInt[(in[s/2+1]>>4)&0xf];
|
||||||
|
sampleData <<= exp;
|
||||||
|
sampleData <<= 11;
|
||||||
|
sampleData += 1024;
|
||||||
|
sampleData +=
|
||||||
|
factor1 * ((int32_t)(*prev1)) +
|
||||||
|
factor2 * ((int32_t)(*prev2));
|
||||||
|
sampleData >>= 11;
|
||||||
|
sampleData = DSPSampClamp(sampleData);
|
||||||
|
*out++ = sampleData;
|
||||||
|
*prev2 = *prev1;
|
||||||
|
*prev1 = sampleData;
|
||||||
|
++ret;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned DSPDecompressFrame(int16_t* out, const uint8_t* in,
|
||||||
|
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||||
|
unsigned lastSample)
|
||||||
|
{
|
||||||
|
uint8_t cIdx = (in[0]>>4) & 0xf;
|
||||||
|
int32_t factor1 = coefs[cIdx][0];
|
||||||
|
int32_t factor2 = coefs[cIdx][1];
|
||||||
|
uint8_t exp = in[0] & 0xf;
|
||||||
|
unsigned ret = 0;
|
||||||
|
for (int s=0 ; s<14 && s<lastSample ; ++s)
|
||||||
|
{
|
||||||
|
int32_t sampleData = (s&1)?
|
||||||
|
NibbleToInt[(in[s/2+1])&0xf]:
|
||||||
|
NibbleToInt[(in[s/2+1]>>4)&0xf];
|
||||||
|
sampleData <<= exp;
|
||||||
|
sampleData <<= 11;
|
||||||
|
sampleData += 1024;
|
||||||
|
sampleData +=
|
||||||
|
factor1 * ((int32_t)(*prev1)) +
|
||||||
|
factor2 * ((int32_t)(*prev2));
|
||||||
|
sampleData >>= 11;
|
||||||
|
sampleData = DSPSampClamp(sampleData);
|
||||||
|
out[s] = sampleData;
|
||||||
|
*prev2 = *prev1;
|
||||||
|
*prev1 = sampleData;
|
||||||
|
++ret;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned DSPDecompressFrameStereoStride(int16_t* out, const uint8_t* in,
|
||||||
|
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||||
|
unsigned lastSample)
|
||||||
|
{
|
||||||
|
uint32_t cIdx = (in[0]>>4) & 0xf;
|
||||||
|
int32_t factor1 = coefs[cIdx][0];
|
||||||
|
int32_t factor2 = coefs[cIdx][1];
|
||||||
|
uint32_t exp = in[0] & 0xf;
|
||||||
|
unsigned ret = 0;
|
||||||
|
for (int s=0 ; s<14 && s<lastSample ; ++s)
|
||||||
|
{
|
||||||
|
int sampleData = (s&1)?
|
||||||
|
NibbleToInt[(in[s/2+1])&0xf]:
|
||||||
|
NibbleToInt[(in[s/2+1]>>4)&0xf];
|
||||||
|
sampleData <<= exp;
|
||||||
|
sampleData <<= 11;
|
||||||
|
sampleData += 1024;
|
||||||
|
sampleData +=
|
||||||
|
factor1 * ((int32_t)(*prev1)) +
|
||||||
|
factor2 * ((int32_t)(*prev2));
|
||||||
|
sampleData >>= 11;
|
||||||
|
sampleData = DSPSampClamp(sampleData);
|
||||||
|
out[s*2] = sampleData;
|
||||||
|
*prev2 = *prev1;
|
||||||
|
*prev1 = sampleData;
|
||||||
|
++ret;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned DSPDecompressFrameStereoDupe(int16_t* out, const uint8_t* in,
|
||||||
|
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||||
|
unsigned lastSample)
|
||||||
|
{
|
||||||
|
uint8_t cIdx = (in[0]>>4) & 0xf;
|
||||||
|
int32_t factor1 = coefs[cIdx][0];
|
||||||
|
int32_t factor2 = coefs[cIdx][1];
|
||||||
|
uint8_t exp = in[0] & 0xf;
|
||||||
|
unsigned ret = 0;
|
||||||
|
for (int s=0 ; s<14 && s<lastSample ; ++s)
|
||||||
|
{
|
||||||
|
int sampleData = (s&1)?
|
||||||
|
NibbleToInt[(in[s/2+1])&0xf]:
|
||||||
|
NibbleToInt[(in[s/2+1]>>4)&0xf];
|
||||||
|
sampleData <<= exp;
|
||||||
|
sampleData <<= 11;
|
||||||
|
sampleData += 1024;
|
||||||
|
sampleData +=
|
||||||
|
factor1 * ((int32_t)(*prev1)) +
|
||||||
|
factor2 * ((int32_t)(*prev2));
|
||||||
|
sampleData >>= 11;
|
||||||
|
sampleData = DSPSampClamp(sampleData);
|
||||||
|
out[s*2] = sampleData;
|
||||||
|
out[s*2+1] = sampleData;
|
||||||
|
*prev2 = *prev1;
|
||||||
|
*prev1 = sampleData;
|
||||||
|
++ret;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
Loading…
Reference in New Issue