phonon: accessors; multiple sources; start reverb;

This commit is contained in:
bjorn 2021-02-22 13:24:09 -07:00 committed by Bjorn
parent 5f2cdf0c22
commit ec96a126d7
6 changed files with 344 additions and 105 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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