Work on Voice state and SurroundProfiles

This commit is contained in:
Jack Andersen 2016-05-10 18:48:08 -10:00
parent 60f873e76e
commit 1102d50f8f
22 changed files with 1099 additions and 176 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

34
include/amuse/dsp.h Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

172
lib/SurroundProfiles.cpp Normal file
View File

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

View File

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

124
lib/dsp.c Normal file
View File

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