mirror of https://github.com/bjornbytes/lovr.git
phonon: accessors; multiple sources; start reverb;
This commit is contained in:
parent
5f2cdf0c22
commit
ec96a126d7
|
@ -100,6 +100,7 @@ extern StringEntry lovrPermission[];
|
|||
extern StringEntry lovrSampleFormat[];
|
||||
extern StringEntry lovrShaderType[];
|
||||
extern StringEntry lovrShapeType[];
|
||||
extern StringEntry lovrSourceInterpolation[];
|
||||
extern StringEntry lovrStencilAction[];
|
||||
extern StringEntry lovrTextureFormat[];
|
||||
extern StringEntry lovrTextureType[];
|
||||
|
|
|
@ -18,6 +18,12 @@ StringEntry lovrTimeUnit[] = {
|
|||
{ 0 }
|
||||
};
|
||||
|
||||
StringEntry lovrSourceInterpolation[] = {
|
||||
[SOURCE_NEAREST] = ENTRY("nearest"),
|
||||
[SOURCE_BILINEAR] = ENTRY("bilinear"),
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static void onDevice(const void* id, size_t size, const char* name, bool isDefault, void* userdata) {
|
||||
lua_State* L = userdata;
|
||||
lua_createtable(L, 0, 3);
|
||||
|
@ -134,22 +140,32 @@ static int l_lovrAudioGetCaptureStream(lua_State* L) {
|
|||
static int l_lovrAudioNewSource(lua_State* L) {
|
||||
Sound* sound = luax_totype(L, 1, Sound);
|
||||
|
||||
bool spatial = true;
|
||||
bool shared = false;
|
||||
bool decode = false;
|
||||
if (lua_istable(L, 2)) {
|
||||
lua_getfield(L, 2, "spatial");
|
||||
spatial = lua_isnil(L, -1) || lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 2, "shared");
|
||||
spatial = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 2, "decode");
|
||||
decode = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
if (!sound) {
|
||||
Blob* blob = luax_readblob(L, 1, "Source");
|
||||
sound = lovrSoundCreateFromFile(blob, false);
|
||||
sound = lovrSoundCreateFromFile(blob, decode);
|
||||
lovrRelease(blob, lovrBlobDestroy);
|
||||
} else {
|
||||
lovrRetain(sound);
|
||||
}
|
||||
|
||||
bool spatial = true;
|
||||
if (lua_istable(L, 2)) {
|
||||
lua_getfield(L, 2, "spatial");
|
||||
spatial = lua_isnil(L, -1) || lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
Source* source = lovrSourceCreate(sound, spatial);
|
||||
Source* source = lovrSourceCreate(sound, spatial, shared);
|
||||
luax_pushtype(L, Source, source);
|
||||
lovrRelease(sound, lovrSoundDestroy);
|
||||
lovrRelease(source, lovrSourceDestroy);
|
||||
|
|
|
@ -90,10 +90,46 @@ static int l_lovrSourceSetTime(lua_State* L) {
|
|||
|
||||
static int l_lovrSourceIsSpatial(lua_State* L) {
|
||||
Source* source = luax_checktype(L, 1, Source);
|
||||
lua_pushboolean(L, lovrSourceIsSpatial(source));
|
||||
bool spatial = lovrSourceIsSpatial(source);
|
||||
lua_pushboolean(L, spatial);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrSourceIsShared(lua_State* L) {
|
||||
Source* source = luax_checktype(L, 1, Source);
|
||||
bool shared = lovrSourceIsShared(source);
|
||||
lua_pushboolean(L, shared);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrSourceGetSpatialBlend(lua_State* L) {
|
||||
Source* source = luax_checktype(L, 1, Source);
|
||||
float blend = lovrSourceGetSpatialBlend(source);
|
||||
lua_pushnumber(L, blend);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrSourceSetSpatialBlend(lua_State* L) {
|
||||
Source* source = luax_checktype(L, 1, Source);
|
||||
float blend = luax_checkfloat(L, 2);
|
||||
lovrSourceSetSpatialBlend(source, blend);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_lovrSourceGetInterpolation(lua_State* L) {
|
||||
Source* source = luax_checktype(L, 1, Source);
|
||||
SourceInterpolation interpolation = lovrSourceGetInterpolation(source);
|
||||
luax_pushenum(L, SourceInterpolation, interpolation);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrSourceSetInterpolation(lua_State* L) {
|
||||
Source* source = luax_checktype(L, 1, Source);
|
||||
SourceInterpolation interpolation = luax_checkenum(L, 2, SourceInterpolation, NULL);
|
||||
lovrSourceSetInterpolation(source, interpolation);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_lovrSourceGetPose(lua_State* L) {
|
||||
Source* source = luax_checktype(L, 1, Source);
|
||||
float position[4], orientation[4], angle, ax, ay, az;
|
||||
|
@ -136,6 +172,20 @@ static int l_lovrSourceSetDirectivity(lua_State* L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int l_lovrSourceGetRadius(lua_State* L) {
|
||||
Source* source = luax_checktype(L, 1, Source);
|
||||
float radius = lovrSourceGetRadius(source);
|
||||
lua_pushnumber(L, radius);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrSourceSetRadius(lua_State* L) {
|
||||
Source* source = luax_checktype(L, 1, Source);
|
||||
float radius = luax_checkfloat(L, 2);
|
||||
lovrSourceSetRadius(source, radius);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_lovrSourceIsAbsorptionEnabled(lua_State* L) {
|
||||
Source* source = luax_checktype(L, 1, Source);
|
||||
bool enabled = lovrSourceIsAbsorptionEnabled(source);
|
||||
|
@ -164,6 +214,48 @@ static int l_lovrSourceSetFalloffEnabled(lua_State* L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int l_lovrSourceIsOcclusionEnabled(lua_State* L) {
|
||||
Source* source = luax_checktype(L, 1, Source);
|
||||
bool enabled = lovrSourceIsOcclusionEnabled(source);
|
||||
lua_pushboolean(L, enabled);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrSourceSetOcclusionEnabled(lua_State* L) {
|
||||
Source* source = luax_checktype(L, 1, Source);
|
||||
bool enabled = lua_toboolean(L, 2);
|
||||
lovrSourceSetOcclusionEnabled(source, enabled);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_lovrSourceIsReverbEnabled(lua_State* L) {
|
||||
Source* source = luax_checktype(L, 1, Source);
|
||||
bool enabled = lovrSourceIsReverbEnabled(source);
|
||||
lua_pushboolean(L, enabled);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrSourceSetReverbEnabled(lua_State* L) {
|
||||
Source* source = luax_checktype(L, 1, Source);
|
||||
bool enabled = lua_toboolean(L, 2);
|
||||
lovrSourceSetReverbEnabled(source, enabled);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_lovrSourceIsTransmissionEnabled(lua_State* L) {
|
||||
Source* source = luax_checktype(L, 1, Source);
|
||||
bool enabled = lovrSourceIsTransmissionEnabled(source);
|
||||
lua_pushboolean(L, enabled);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrSourceSetTransmissionEnabled(lua_State* L) {
|
||||
Source* source = luax_checktype(L, 1, Source);
|
||||
bool enabled = lua_toboolean(L, 2);
|
||||
lovrSourceSetTransmissionEnabled(source, enabled);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const luaL_Reg lovrSource[] = {
|
||||
{ "clone", l_lovrSourceClone },
|
||||
{ "play", l_lovrSourcePlay },
|
||||
|
@ -178,13 +270,26 @@ const luaL_Reg lovrSource[] = {
|
|||
{ "getTime", l_lovrSourceGetTime },
|
||||
{ "setTime", l_lovrSourceSetTime },
|
||||
{ "isSpatial", l_lovrSourceIsSpatial },
|
||||
{ "isShared", l_lovrSourceIsShared },
|
||||
{ "getSpatialBlend", l_lovrSourceGetSpatialBlend },
|
||||
{ "setSpatialBlend", l_lovrSourceSetSpatialBlend },
|
||||
{ "getInterpolation", l_lovrSourceGetInterpolation },
|
||||
{ "setInterpolation", l_lovrSourceSetInterpolation },
|
||||
{ "getPose", l_lovrSourceGetPose },
|
||||
{ "setPose", l_lovrSourceSetPose },
|
||||
{ "getRadius", l_lovrSourceGetRadius },
|
||||
{ "setRadius", l_lovrSourceSetRadius },
|
||||
{ "getDirectivity", l_lovrSourceGetDirectivity },
|
||||
{ "setDirectivity", l_lovrSourceSetDirectivity },
|
||||
{ "isAbsorptionEnabled", l_lovrSourceIsAbsorptionEnabled },
|
||||
{ "setAbsorptionEnabled", l_lovrSourceSetAbsorptionEnabled },
|
||||
{ "isFalloffEnabled", l_lovrSourceIsFalloffEnabled },
|
||||
{ "setFalloffEnabled", l_lovrSourceSetFalloffEnabled },
|
||||
{ "isOcclusionEnabled", l_lovrSourceIsOcclusionEnabled },
|
||||
{ "setOcclusionEnabled", l_lovrSourceSetOcclusionEnabled },
|
||||
{ "isReverbEnabled", l_lovrSourceIsReverbEnabled },
|
||||
{ "setReverbEnabled", l_lovrSourceSetReverbEnabled },
|
||||
{ "isTransmissionEnabled", l_lovrSourceIsTransmissionEnabled },
|
||||
{ "setTransmissionEnabled", l_lovrSourceSetTransmissionEnabled },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
|
|
@ -32,15 +32,22 @@ struct Source {
|
|||
uint32_t converter;
|
||||
uint32_t offset;
|
||||
float volume;
|
||||
float blend;
|
||||
float position[4];
|
||||
float orientation[4];
|
||||
float radius;
|
||||
float dipoleWeight;
|
||||
float dipolePower;
|
||||
bool absorption;
|
||||
bool falloff;
|
||||
bool occlusion;
|
||||
bool reverb;
|
||||
bool transmission;
|
||||
bool playing;
|
||||
bool looping;
|
||||
bool spatial;
|
||||
bool shared;
|
||||
bool bilinear;
|
||||
};
|
||||
|
||||
static struct {
|
||||
|
@ -97,8 +104,8 @@ static void onPlayback(ma_device* device, void* out, const void* in, uint32_t co
|
|||
if (!source->playing) {
|
||||
state.sources[source->index] = NULL;
|
||||
state.sourceMask &= ~(1ull << source->index);
|
||||
source->index = ~0u;
|
||||
state.spatializer->sourceDestroy(source);
|
||||
source->index = ~0u;
|
||||
lovrRelease(source, lovrSourceDestroy);
|
||||
continue;
|
||||
}
|
||||
|
@ -342,7 +349,7 @@ Sound* lovrAudioGetCaptureStream() {
|
|||
|
||||
// Source
|
||||
|
||||
Source* lovrSourceCreate(Sound* sound, bool spatial) {
|
||||
Source* lovrSourceCreate(Sound* sound, bool spatial, bool shared) {
|
||||
lovrAssert(lovrSoundGetChannelLayout(sound) != CHANNEL_AMBISONIC, "Ambisonic Sources are not currently supported");
|
||||
Source* source = calloc(1, sizeof(Source));
|
||||
lovrAssert(source, "Out of memory");
|
||||
|
@ -353,6 +360,8 @@ Source* lovrSourceCreate(Sound* sound, bool spatial) {
|
|||
|
||||
source->volume = 1.f;
|
||||
source->spatial = spatial;
|
||||
source->shared = shared;
|
||||
source->converter = ~0u;
|
||||
|
||||
ma_data_converter_config config = ma_data_converter_config_init_default();
|
||||
config.formatIn = miniaudioFormats[lovrSoundGetFormat(sound)];
|
||||
|
@ -362,8 +371,6 @@ Source* lovrSourceCreate(Sound* sound, bool spatial) {
|
|||
config.sampleRateIn = lovrSoundGetSampleRate(sound);
|
||||
config.sampleRateOut = PLAYBACK_SAMPLE_RATE;
|
||||
|
||||
source->converter = ~0u;
|
||||
|
||||
if (config.formatIn != config.formatOut || config.channelsIn != config.channelsOut || config.sampleRateIn != config.sampleRateOut) {
|
||||
for (size_t i = 0; i < state.converters.length; i++) {
|
||||
ma_data_converter* converter = &state.converters.data[i];
|
||||
|
@ -479,6 +486,26 @@ bool lovrSourceIsSpatial(Source *source) {
|
|||
return source->spatial;
|
||||
}
|
||||
|
||||
bool lovrSourceIsShared(Source* source) {
|
||||
return source->shared;
|
||||
}
|
||||
|
||||
float lovrSourceGetSpatialBlend(Source* source) {
|
||||
return source->blend;
|
||||
}
|
||||
|
||||
void lovrSourceSetSpatialBlend(Source* source, float blend) {
|
||||
source->blend = blend;
|
||||
}
|
||||
|
||||
SourceInterpolation lovrSourceGetInterpolation(Source* source) {
|
||||
return source->bilinear ? SOURCE_BILINEAR : SOURCE_NEAREST;
|
||||
}
|
||||
|
||||
void lovrSourceSetInterpolation(Source* source, SourceInterpolation interpolation) {
|
||||
source->bilinear = interpolation == SOURCE_BILINEAR;
|
||||
}
|
||||
|
||||
void lovrSourceGetPose(Source *source, float position[4], float orientation[4]) {
|
||||
memcpy(position, source->position, sizeof(source->position));
|
||||
memcpy(orientation, source->orientation, sizeof(source->orientation));
|
||||
|
@ -491,6 +518,14 @@ void lovrSourceSetPose(Source *source, float position[4], float orientation[4])
|
|||
ma_mutex_unlock(&state.lock);
|
||||
}
|
||||
|
||||
float lovrSourceGetRadius(Source* source) {
|
||||
return source->radius;
|
||||
}
|
||||
|
||||
void lovrSourceSetRadius(Source* source, float radius) {
|
||||
source->radius = radius;
|
||||
}
|
||||
|
||||
void lovrSourceGetDirectivity(Source* source, float* weight, float* power) {
|
||||
*weight = source->dipoleWeight;
|
||||
*power = source->dipolePower;
|
||||
|
@ -513,10 +548,38 @@ bool lovrSourceIsFalloffEnabled(Source* source) {
|
|||
return source->falloff;
|
||||
}
|
||||
|
||||
void lovrSourceSetFalloffEnabled(Source* source, bool falloff) {
|
||||
source->falloff = falloff;
|
||||
void lovrSourceSetFalloffEnabled(Source* source, bool enabled) {
|
||||
source->falloff = enabled;
|
||||
}
|
||||
|
||||
bool lovrSourceIsOcclusionEnabled(Source* source) {
|
||||
return source->occlusion;
|
||||
}
|
||||
|
||||
void lovrSourceSetOcclusionEnabled(Source* source, bool enabled) {
|
||||
source->occlusion = enabled;
|
||||
}
|
||||
|
||||
bool lovrSourceIsReverbEnabled(Source* source) {
|
||||
return source->reverb;
|
||||
}
|
||||
|
||||
void lovrSourceSetReverbEnabled(Source* source, bool enabled) {
|
||||
source->reverb = enabled;
|
||||
}
|
||||
|
||||
bool lovrSourceIsTransmissionEnabled(Source* source) {
|
||||
return source->transmission;
|
||||
}
|
||||
|
||||
void lovrSourceSetTransmissionEnabled(Source* source, bool enabled) {
|
||||
source->transmission = enabled;
|
||||
}
|
||||
|
||||
intptr_t* lovrSourceGetSpatializerMemoField(Source* source) {
|
||||
return &source->spatializerMemo;
|
||||
}
|
||||
|
||||
uint32_t lovrSourceGetIndex(Source* source) {
|
||||
return source->index;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,11 @@ typedef enum {
|
|||
UNIT_FRAMES
|
||||
} TimeUnit;
|
||||
|
||||
typedef enum {
|
||||
SOURCE_NEAREST,
|
||||
SOURCE_BILINEAR
|
||||
} SourceInterpolation;
|
||||
|
||||
typedef void AudioDeviceCallback(const void* id, size_t size, const char* name, bool isDefault, void* userdata);
|
||||
|
||||
bool lovrAudioInit(const char* spatializer);
|
||||
|
@ -40,7 +45,7 @@ struct Sound* lovrAudioGetCaptureStream(void);
|
|||
|
||||
// Source
|
||||
|
||||
Source* lovrSourceCreate(struct Sound* sound, bool spatial);
|
||||
Source* lovrSourceCreate(struct Sound* sound, bool spatial, bool shared);
|
||||
Source* lovrSourceClone(Source* source);
|
||||
void lovrSourceDestroy(void* ref);
|
||||
bool lovrSourcePlay(Source* source);
|
||||
|
@ -55,12 +60,26 @@ double lovrSourceGetDuration(Source* source, TimeUnit units);
|
|||
double lovrSourceGetTime(Source* source, TimeUnit units);
|
||||
void lovrSourceSetTime(Source* source, double time, TimeUnit units);
|
||||
bool lovrSourceIsSpatial(Source* source);
|
||||
bool lovrSourceIsShared(Source* source);
|
||||
float lovrSourceGetSpatialBlend(Source* source);
|
||||
void lovrSourceSetSpatialBlend(Source* source, float blend);
|
||||
SourceInterpolation lovrSourceGetInterpolation(Source* source);
|
||||
void lovrSourceSetInterpolation(Source* source, SourceInterpolation interpolation);
|
||||
void lovrSourceGetPose(Source* source, float position[4], float orientation[4]);
|
||||
void lovrSourceSetPose(Source* source, float position[4], float orientation[4]);
|
||||
float lovrSourceGetRadius(Source* source);
|
||||
void lovrSourceSetRadius(Source* source, float radius);
|
||||
void lovrSourceGetDirectivity(Source* source, float* weight, float* power);
|
||||
void lovrSourceSetDirectivity(Source* source, float weight, float power);
|
||||
bool lovrSourceIsAbsorptionEnabled(Source* source);
|
||||
void lovrSourceSetAbsorptionEnabled(Source* source, bool enabled);
|
||||
bool lovrSourceIsFalloffEnabled(Source* source);
|
||||
void lovrSourceSetFalloffEnabled(Source* source, bool enabled);
|
||||
bool lovrSourceIsOcclusionEnabled(Source* source);
|
||||
void lovrSourceSetOcclusionEnabled(Source* source, bool enabled);
|
||||
bool lovrSourceIsReverbEnabled(Source* source);
|
||||
void lovrSourceSetReverbEnabled(Source* source, bool enabled);
|
||||
bool lovrSourceIsTransmissionEnabled(Source* source);
|
||||
void lovrSourceSetTransmissionEnabled(Source* source, bool enabled);
|
||||
intptr_t* lovrSourceGetSpatializerMemoField(Source* source);
|
||||
uint32_t lovrSourceGetIndex(Source* source);
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MONO (IPLAudioFormat) { IPL_CHANNELLAYOUTTYPE_SPEAKERS, IPL_CHANNELLAYOUT_MONO, .channelOrder = IPL_CHANNELORDER_INTERLEAVED }
|
||||
#define STEREO (IPLAudioFormat) { IPL_CHANNELLAYOUTTYPE_SPEAKERS, IPL_CHANNELLAYOUT_STEREO, .channelOrder = IPL_CHANNELORDER_INTERLEAVED }
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#define PHONON_LIBRARY "phonon.dll"
|
||||
|
@ -32,15 +35,24 @@ typedef IPLerror fn_iplCreateStaticMesh(IPLhandle scene, IPLint32 numVertices, I
|
|||
typedef IPLerror fn_iplDestroyStaticMesh(IPLhandle* staticMesh);
|
||||
typedef IPLerror fn_iplCreateEnvironment(IPLhandle context, IPLhandle computeDevice, IPLSimulationSettings simulationSettings, IPLhandle scene, IPLhandle probeManager, IPLhandle* environment);
|
||||
typedef IPLvoid fn_iplDestroyEnvironment(IPLhandle* environment);
|
||||
typedef IPLerror fn_iplCreateEnvironmentalRenderer(IPLhandle context, IPLhandle environment, IPLRenderingSettings renderingSettings, IPLAudioFormat outputFormat, IPLSimulationThreadCreateCallback threadCreateCallback, IPLSimulationThreadDestroyCallback threadDestroyCallback, IPLhandle* renderer);
|
||||
typedef IPLvoid fn_iplDestroyEnvironmentalRenderer(IPLhandle* renderer);
|
||||
typedef IPLerror fn_iplCreateDirectSoundEffect(IPLAudioFormat inputFormat, IPLAudioFormat outputFormat, IPLRenderingSettings renderingSettings, IPLhandle* effect);
|
||||
typedef IPLvoid fn_iplDestroyDirectSoundEffect(IPLhandle* effect);
|
||||
typedef IPLvoid fn_iplApplyDirectSoundEffect(IPLhandle effect, IPLAudioBuffer inputAudio, IPLDirectSoundPath directSoundPath, IPLDirectSoundEffectOptions options, IPLAudioBuffer outputAudio);
|
||||
typedef IPLvoid fn_iplFlushDirectSoundEffect(IPLhandle effect);
|
||||
typedef IPLDirectSoundPath fn_iplGetDirectSoundPath(IPLhandle environment, IPLVector3 listenerPosition, IPLVector3 listenerAhead, IPLVector3 listenerUp, IPLSource source, IPLfloat32 sourceRadius, IPLint32 numSamples, IPLDirectOcclusionMode occlusionMode, IPLDirectOcclusionMethod occlusionMethod);
|
||||
typedef IPLerror fn_iplCreateBinauralRenderer(IPLhandle context, IPLRenderingSettings renderingSettings, IPLHrtfParams params, IPLhandle* renderer);
|
||||
typedef IPLvoid fn_iplDestroyBinauralRenderer(IPLhandle* renderer);
|
||||
typedef IPLerror fn_iplCreateBinauralEffect(IPLhandle renderer, IPLAudioFormat inputFormat, IPLAudioFormat outputFormat, IPLhandle* effect);
|
||||
typedef IPLvoid fn_iplDestroyBinauralEffect(IPLhandle* effect);
|
||||
typedef IPLvoid fn_iplApplyBinauralEffect(IPLhandle effect, IPLhandle binauralRenderer, IPLAudioBuffer inputAudio, IPLVector3 direction, IPLHrtfInterpolation interpolation, IPLfloat32 spatialBlend, IPLAudioBuffer outputAudio);
|
||||
typedef IPLvoid fn_iplFlushBinauralEffect(IPLhandle effect);
|
||||
typedef IPLerror fn_iplCreateConvolutionEffect(IPLhandle renderer, IPLBakedDataIdentifier identifier, IPLSimulationType simulationType, IPLAudioFormat inputFormat, IPLAudioFormat outputFormat, IPLhandle* effect);
|
||||
typedef IPLvoid fn_iplDestroyConvolutionEffect(IPLhandle* effect);
|
||||
typedef IPLvoid fn_iplSetDryAudioForConvolutionEffect(IPLhandle effect, IPLSource source, IPLAudioBuffer dryAudio);
|
||||
typedef IPLvoid fn_iplGetMixedEnvironmentalAudio(IPLhandle renderer, IPLVector3 listenerPosition, IPLVector3 listenerAhead, IPLVector3 listenerUp, IPLAudioBuffer mixedWetAudio);
|
||||
typedef IPLvoid fn_iplFlushConvolutionEffect(IPLhandle effect);
|
||||
|
||||
#define PHONON_DECLARE(f) static fn_##f* phonon_##f;
|
||||
#define PHONON_LOAD(f) phonon_##f = (fn_##f*) phonon_dlsym(state.library, #f);
|
||||
|
@ -55,15 +67,24 @@ typedef IPLvoid fn_iplApplyBinauralEffect(IPLhandle effect, IPLhandle binauralRe
|
|||
X(iplDestroyStaticMesh)\
|
||||
X(iplCreateEnvironment)\
|
||||
X(iplDestroyEnvironment)\
|
||||
X(iplCreateEnvironmentalRenderer)\
|
||||
X(iplDestroyEnvironmentalRenderer)\
|
||||
X(iplCreateDirectSoundEffect)\
|
||||
X(iplDestroyDirectSoundEffect)\
|
||||
X(iplApplyDirectSoundEffect)\
|
||||
X(iplFlushDirectSoundEffect)\
|
||||
X(iplGetDirectSoundPath)\
|
||||
X(iplCreateBinauralRenderer)\
|
||||
X(iplDestroyBinauralRenderer)\
|
||||
X(iplCreateBinauralEffect)\
|
||||
X(iplDestroyBinauralEffect)\
|
||||
X(iplApplyBinauralEffect)
|
||||
X(iplApplyBinauralEffect)\
|
||||
X(iplFlushBinauralEffect)\
|
||||
X(iplCreateConvolutionEffect)\
|
||||
X(iplDestroyConvolutionEffect)\
|
||||
X(iplSetDryAudioForConvolutionEffect)\
|
||||
X(iplGetMixedEnvironmentalAudio)\
|
||||
X(iplFlushConvolutionEffect)
|
||||
|
||||
PHONON_FOREACH(PHONON_DECLARE)
|
||||
|
||||
|
@ -73,9 +94,12 @@ static struct {
|
|||
IPLhandle scene;
|
||||
IPLhandle mesh;
|
||||
IPLhandle environment;
|
||||
IPLhandle directSoundEffect;
|
||||
IPLhandle environmentalRenderer;
|
||||
IPLhandle binauralRenderer;
|
||||
IPLhandle binauralEffect;
|
||||
IPLhandle binauralEffect[MAX_SOURCES];
|
||||
IPLhandle directSoundEffect[MAX_SOURCES];
|
||||
IPLhandle convolutionEffect[MAX_SOURCES];
|
||||
IPLRenderingSettings renderingSettings;
|
||||
float listenerPosition[4];
|
||||
float listenerOrientation[4];
|
||||
float* scratchpad;
|
||||
|
@ -98,37 +122,18 @@ bool phonon_init(SpatializerConfig config) {
|
|||
status = phonon_iplCreateEnvironment(state.context, NULL, simulationSettings, NULL, NULL, &state.environment);
|
||||
if (status != IPL_STATUS_SUCCESS) return phonon_destroy(), false;
|
||||
|
||||
IPLAudioFormat mono = {
|
||||
.channelLayoutType = IPL_CHANNELLAYOUTTYPE_SPEAKERS,
|
||||
.channelLayout = IPL_CHANNELLAYOUT_MONO,
|
||||
.channelOrder = IPL_CHANNELORDER_INTERLEAVED
|
||||
};
|
||||
|
||||
IPLAudioFormat stereo = {
|
||||
.channelLayoutType = IPL_CHANNELLAYOUTTYPE_SPEAKERS,
|
||||
.channelLayout = IPL_CHANNELLAYOUT_STEREO,
|
||||
.channelOrder = IPL_CHANNELORDER_INTERLEAVED
|
||||
};
|
||||
|
||||
IPLRenderingSettings renderingSettings = {
|
||||
.samplingRate = config.sampleRate,
|
||||
.frameSize = config.fixedBufferSize
|
||||
};
|
||||
state.renderingSettings.samplingRate = config.sampleRate;
|
||||
state.renderingSettings.frameSize = config.fixedBufferSize;
|
||||
state.renderingSettings.convolutionType = IPL_CONVOLUTIONTYPE_PHONON;
|
||||
|
||||
state.scratchpad = malloc(config.fixedBufferSize * sizeof(float));
|
||||
if (!state.scratchpad) return phonon_destroy(), false;
|
||||
|
||||
status = phonon_iplCreateDirectSoundEffect(mono, mono, renderingSettings, &state.directSoundEffect);
|
||||
if (status != IPL_STATUS_SUCCESS) return phonon_destroy(), false;
|
||||
|
||||
IPLHrtfParams hrtfParams = {
|
||||
.type = IPL_HRTFDATABASETYPE_DEFAULT
|
||||
};
|
||||
|
||||
status = phonon_iplCreateBinauralRenderer(state.context, renderingSettings, hrtfParams, &state.binauralRenderer);
|
||||
if (status != IPL_STATUS_SUCCESS) return phonon_destroy(), false;
|
||||
|
||||
status = phonon_iplCreateBinauralEffect(state.binauralRenderer, mono, stereo, &state.binauralEffect);
|
||||
status = phonon_iplCreateBinauralRenderer(state.context, state.renderingSettings, hrtfParams, &state.binauralRenderer);
|
||||
if (status != IPL_STATUS_SUCCESS) return phonon_destroy(), false;
|
||||
|
||||
return true;
|
||||
|
@ -136,9 +141,13 @@ bool phonon_init(SpatializerConfig config) {
|
|||
|
||||
void phonon_destroy() {
|
||||
if (state.scratchpad) free(state.scratchpad);
|
||||
if (state.binauralEffect) phonon_iplDestroyBinauralEffect(&state.binauralEffect);
|
||||
for (size_t i = 0; i < MAX_SOURCES; i++) {
|
||||
if (state.binauralEffect[i]) phonon_iplDestroyBinauralEffect(&state.binauralEffect[i]);
|
||||
if (state.directSoundEffect[i]) phonon_iplDestroyDirectSoundEffect(&state.directSoundEffect[i]);
|
||||
if (state.convolutionEffect[i]) phonon_iplDestroyConvolutionEffect(&state.convolutionEffect[i]);
|
||||
}
|
||||
if (state.binauralRenderer) phonon_iplDestroyBinauralRenderer(&state.binauralRenderer);
|
||||
if (state.directSoundEffect) phonon_iplDestroyDirectSoundEffect(&state.directSoundEffect);
|
||||
if (state.environmentalRenderer) phonon_iplDestroyEnvironmentalRenderer(&state.environmentalRenderer);
|
||||
if (state.environment) phonon_iplDestroyEnvironment(&state.environment);
|
||||
if (state.mesh) phonon_iplDestroyStaticMesh(&state.mesh);
|
||||
if (state.scene) phonon_iplDestroyStaticMesh(&state.scene);
|
||||
|
@ -149,85 +158,83 @@ void phonon_destroy() {
|
|||
}
|
||||
|
||||
uint32_t phonon_apply(Source* source, const float* input, float* output, uint32_t frames, uint32_t why) {
|
||||
float forward[4] = { 0.f, 0.f, -1.f };
|
||||
float up[4] = { 0.f, 1.f, 0.f };
|
||||
IPLAudioBuffer in = { .format = MONO, .numSamples = frames, .interleavedBuffer = (float*) input };
|
||||
IPLAudioBuffer tmp = { .format = MONO, .numSamples = frames, .interleavedBuffer = state.scratchpad };
|
||||
IPLAudioBuffer out = { .format = STEREO, .numSamples = frames, .interleavedBuffer = output };
|
||||
|
||||
quat_rotate(state.listenerOrientation, forward);
|
||||
quat_rotate(state.listenerOrientation, up);
|
||||
uint32_t index = lovrSourceGetIndex(source);
|
||||
|
||||
IPLVector3 listenerPosition = { state.listenerPosition[0], state.listenerPosition[1], state.listenerPosition[2] };
|
||||
IPLVector3 listenerForward = { forward[0], forward[1], forward[2] };
|
||||
IPLVector3 listenerUp = { up[0], up[1], up[2] };
|
||||
float x[4], y[4], z[4];
|
||||
|
||||
// Using a matrix may be more effective here?
|
||||
float position[4], orientation[4], right[4];
|
||||
vec3_set(y, 0.f, 1.f, 0.f);
|
||||
vec3_set(z, 0.f, 0.f, -1.f);
|
||||
quat_rotate(state.listenerOrientation, y);
|
||||
quat_rotate(state.listenerOrientation, z);
|
||||
IPLVector3 listener = { state.listenerPosition[0], state.listenerPosition[1], state.listenerPosition[2] };
|
||||
IPLVector3 forward = { z[0], z[1], z[2] };
|
||||
IPLVector3 up = { y[0], y[1], y[2] };
|
||||
|
||||
// TODO maybe this should use a matrix
|
||||
float position[4], orientation[4];
|
||||
lovrSourceGetPose(source, position, orientation);
|
||||
vec3_set(forward, 0.f, 0.f, -1.f);
|
||||
vec3_set(up, 0.f, 1.f, 0.f);
|
||||
vec3_set(right, 1.f, 0.f, 0.f);
|
||||
quat_rotate(orientation, forward);
|
||||
quat_rotate(orientation, up);
|
||||
quat_rotate(orientation, right);
|
||||
vec3_set(x, 1.f, 0.f, 0.f);
|
||||
vec3_set(y, 0.f, 1.f, 0.f);
|
||||
vec3_set(z, 0.f, 0.f, -1.f);
|
||||
quat_rotate(orientation, x);
|
||||
quat_rotate(orientation, y);
|
||||
quat_rotate(orientation, z);
|
||||
|
||||
float weight;
|
||||
float power;
|
||||
float weight, power;
|
||||
lovrSourceGetDirectivity(source, &weight, &power);
|
||||
|
||||
IPLSource iplSource = {
|
||||
.position = (IPLVector3) { position[0], position[1], position[2] },
|
||||
.ahead = (IPLVector3) { forward[0], forward[1], forward[2] },
|
||||
.up = (IPLVector3) { up[0], up[1], up[2] },
|
||||
.right = (IPLVector3) { right[0], right[1], right[2] },
|
||||
.ahead = (IPLVector3) { z[0], z[1], z[2] },
|
||||
.up = (IPLVector3) { y[0], y[1], y[2] },
|
||||
.right = (IPLVector3) { x[0], x[1], x[2] },
|
||||
.directivity.dipoleWeight = weight,
|
||||
.directivity.dipolePower = power
|
||||
};
|
||||
|
||||
IPLDirectOcclusionMode occlusionMode = IPL_DIRECTOCCLUSION_TRANSMISSIONBYFREQUENCY;
|
||||
IPLDirectOcclusionMethod occlusionMethod = IPL_DIRECTOCCLUSION_VOLUMETRIC;
|
||||
IPLDirectSoundPath path = phonon_iplGetDirectSoundPath(state.environment, listenerPosition, listenerForward, listenerUp, iplSource, .5f, 32, occlusionMode, occlusionMethod);
|
||||
IPLDirectOcclusionMode occlusion = IPL_DIRECTOCCLUSION_NONE;
|
||||
IPLDirectOcclusionMethod volumetric = IPL_DIRECTOCCLUSION_RAYCAST;
|
||||
float radius = 0.f;
|
||||
IPLint32 rays = 0;
|
||||
|
||||
if (lovrSourceIsOcclusionEnabled(source)) {
|
||||
occlusion = lovrSourceIsTransmissionEnabled(source) ? IPL_DIRECTOCCLUSION_TRANSMISSIONBYFREQUENCY : IPL_DIRECTOCCLUSION_NOTRANSMISSION;
|
||||
radius = lovrSourceGetRadius(source);
|
||||
|
||||
if (radius > 0.f) {
|
||||
volumetric = IPL_DIRECTOCCLUSION_VOLUMETRIC;
|
||||
rays = 32;
|
||||
}
|
||||
}
|
||||
|
||||
IPLDirectSoundPath path = phonon_iplGetDirectSoundPath(state.environment, listener, forward, up, iplSource, radius, rays, occlusion, volumetric);
|
||||
|
||||
IPLDirectSoundEffectOptions options = {
|
||||
.applyDistanceAttenuation = lovrSourceIsFalloffEnabled(source) ? IPL_TRUE : IPL_FALSE,
|
||||
.applyAirAbsorption = lovrSourceIsAbsorptionEnabled(source) ? IPL_TRUE : IPL_FALSE,
|
||||
.applyDirectivity = weight > 0.f && power > 0.f ? IPL_TRUE : IPL_FALSE,
|
||||
.directOcclusionMode = occlusionMode
|
||||
.directOcclusionMode = occlusion
|
||||
};
|
||||
|
||||
IPLAudioFormat mono = {
|
||||
.channelLayoutType = IPL_CHANNELLAYOUTTYPE_SPEAKERS,
|
||||
.channelLayout = IPL_CHANNELLAYOUT_MONO,
|
||||
.channelOrder = IPL_CHANNELORDER_INTERLEAVED
|
||||
};
|
||||
phonon_iplApplyDirectSoundEffect(state.directSoundEffect[index], in, path, options, tmp);
|
||||
|
||||
IPLAudioFormat stereo = {
|
||||
.channelLayoutType = IPL_CHANNELLAYOUTTYPE_SPEAKERS,
|
||||
.channelLayout = IPL_CHANNELLAYOUT_STEREO,
|
||||
.channelOrder = IPL_CHANNELORDER_INTERLEAVED
|
||||
};
|
||||
if (lovrSourceIsShared(source)) {
|
||||
memset(output, 0, frames * 2 * sizeof(float));
|
||||
// ambisonic panning effect
|
||||
} else {
|
||||
float blend = lovrSourceGetSpatialBlend(source);
|
||||
IPLHrtfInterpolation interpolation = lovrSourceGetInterpolation(source) == SOURCE_BILINEAR ? IPL_HRTFINTERPOLATION_BILINEAR : IPL_HRTFINTERPOLATION_NEAREST;
|
||||
phonon_iplApplyBinauralEffect(state.binauralEffect[index], state.binauralRenderer, tmp, path.direction, interpolation, blend, out);
|
||||
}
|
||||
|
||||
IPLAudioBuffer inputBuffer = {
|
||||
.format = mono,
|
||||
.numSamples = frames,
|
||||
.interleavedBuffer = (float*) input
|
||||
};
|
||||
if (lovrSourceIsReverbEnabled(source)) {
|
||||
phonon_iplSetDryAudioForConvolutionEffect(state.convolutionEffect[index], iplSource, in);
|
||||
}
|
||||
|
||||
IPLAudioBuffer scratchBuffer = {
|
||||
.format = mono,
|
||||
.numSamples = frames,
|
||||
.interleavedBuffer = state.scratchpad
|
||||
};
|
||||
|
||||
IPLAudioBuffer outputBuffer = {
|
||||
.format = stereo,
|
||||
.numSamples = frames,
|
||||
.interleavedBuffer = output
|
||||
};
|
||||
|
||||
IPLHrtfInterpolation interpolation = IPL_HRTFINTERPOLATION_NEAREST;
|
||||
float spatialBlend = 1.f;
|
||||
|
||||
phonon_iplApplyDirectSoundEffect(state.directSoundEffect, inputBuffer, path, options, scratchBuffer);
|
||||
phonon_iplApplyBinauralEffect(state.binauralEffect, state.binauralRenderer, scratchBuffer, path.direction, interpolation, spatialBlend, outputBuffer);
|
||||
return frames;
|
||||
}
|
||||
|
||||
|
@ -235,7 +242,6 @@ uint32_t phonon_tail(float* scratch, float* output, uint32_t frames) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
// TODO lock/transact
|
||||
void phonon_setListenerPose(float position[4], float orientation[4]) {
|
||||
memcpy(state.listenerPosition, position, sizeof(state.listenerPosition));
|
||||
memcpy(state.listenerOrientation, orientation, sizeof(state.listenerOrientation));
|
||||
|
@ -245,6 +251,7 @@ bool phonon_setGeometry(float* vertices, uint32_t* indices, uint32_t vertexCount
|
|||
if (state.mesh) phonon_iplDestroyStaticMesh(&state.mesh);
|
||||
if (state.scene) phonon_iplDestroyScene(&state.scene);
|
||||
if (state.environment) phonon_iplDestroyEnvironment(&state.environment);
|
||||
if (state.environmentalRenderer) phonon_iplDestroyEnvironment(&state.environmentalRenderer);
|
||||
|
||||
IPLMaterial material = (IPLMaterial) {
|
||||
.lowFreqAbsorption = .1f,
|
||||
|
@ -265,7 +272,7 @@ bool phonon_setGeometry(float* vertices, uint32_t* indices, uint32_t vertexCount
|
|||
.numThreads = 1,
|
||||
.irDuration = 1.f,
|
||||
.ambisonicsOrder = 1,
|
||||
.maxConvolutionSources = 64,
|
||||
.maxConvolutionSources = MAX_SOURCES,
|
||||
.bakingBatchSize = 1,
|
||||
.irradianceMinDistance = .1f
|
||||
};
|
||||
|
@ -284,6 +291,9 @@ bool phonon_setGeometry(float* vertices, uint32_t* indices, uint32_t vertexCount
|
|||
status = phonon_iplCreateEnvironment(state.context, NULL, settings, state.scene, NULL, &state.environment);
|
||||
if (status != IPL_STATUS_SUCCESS) goto fail;
|
||||
|
||||
status = phonon_iplCreateEnvironmentalRenderer(state.context, state.environment, state.renderingSettings, STEREO, NULL, NULL, &state.environmentalRenderer);
|
||||
if (status != IPL_STATUS_SUCCESS) goto fail;
|
||||
|
||||
free(materials);
|
||||
return true;
|
||||
|
||||
|
@ -292,16 +302,41 @@ fail:
|
|||
if (state.mesh) phonon_iplDestroyStaticMesh(&state.mesh);
|
||||
if (state.scene) phonon_iplDestroyScene(&state.scene);
|
||||
if (state.environment) phonon_iplDestroyEnvironment(&state.environment);
|
||||
if (state.environmentalRenderer) phonon_iplDestroyEnvironmentalRenderer(&state.environmentalRenderer);
|
||||
phonon_iplCreateEnvironment(state.context, NULL, settings, NULL, NULL, &state.environment);
|
||||
phonon_iplCreateEnvironmentalRenderer(state.context, state.environment, state.renderingSettings, STEREO, NULL, NULL, &state.environmentalRenderer);
|
||||
return false;
|
||||
}
|
||||
|
||||
void phonon_sourceCreate(Source* source) {
|
||||
//
|
||||
uint32_t index = lovrSourceGetIndex(source);
|
||||
|
||||
if (!state.binauralEffect[index] && !lovrSourceIsShared(source)) {
|
||||
phonon_iplCreateBinauralEffect(state.binauralRenderer, MONO, STEREO, &state.binauralEffect[index]);
|
||||
}
|
||||
|
||||
if (!state.directSoundEffect[index]) {
|
||||
phonon_iplCreateDirectSoundEffect(MONO, MONO, state.renderingSettings, &state.directSoundEffect[index]);
|
||||
}
|
||||
|
||||
if (!state.convolutionEffect[index]) {
|
||||
IPLBakedDataIdentifier id = { 0 };
|
||||
IPLAudioFormat ambisonic = (IPLAudioFormat) {
|
||||
.channelLayoutType = IPL_CHANNELLAYOUTTYPE_AMBISONICS,
|
||||
.ambisonicsOrder = 1,
|
||||
.ambisonicsOrdering = IPL_AMBISONICSORDERING_FURSEMALHAM,
|
||||
.ambisonicsNormalization = IPL_AMBISONICSNORMALIZATION_FURSEMALHAM,
|
||||
.channelOrder = IPL_CHANNELORDER_INTERLEAVED
|
||||
};
|
||||
phonon_iplCreateConvolutionEffect(state.environmentalRenderer, id, IPL_SIMTYPE_REALTIME, MONO, ambisonic, &state.convolutionEffect[index]);
|
||||
}
|
||||
}
|
||||
|
||||
void phonon_sourceDestroy(Source* source) {
|
||||
//
|
||||
uint32_t index = lovrSourceGetIndex(source);
|
||||
if (state.binauralEffect[index]) phonon_iplFlushBinauralEffect(state.binauralEffect[index]);
|
||||
if (state.directSoundEffect[index]) phonon_iplFlushDirectSoundEffect(state.directSoundEffect[index]);
|
||||
if (state.convolutionEffect[index]) phonon_iplFlushConvolutionEffect(state.convolutionEffect[index]);
|
||||
}
|
||||
|
||||
Spatializer phononSpatializer = {
|
||||
|
|
Loading…
Reference in New Issue