lovr/src/api/l_data_soundData.c

134 lines
4.6 KiB
C
Raw Normal View History

2018-07-06 03:23:46 +00:00
#include "api.h"
#include "data/soundData.h"
2020-11-26 21:14:02 +00:00
#include "data/blob.h"
#include "core/ref.h"
LOVR_USE_OCULUS_AUDIO This is a large patch which adds a new Oculus Audio spatializer. Oculus Audio is slightly different from the dummy spatializer in a few ways: - It *must* receive fixed-size input buffers, every time, always. - It can only handle a fixed number of spatialized sound sources at a time. - It has a concept of "tails"; the spatialization of a sound can continue after the sound itself ends (eg echo). Changes to audio.c were needed to support Oculus Audio's quirks: - audio.c now supports a "fixedBuffer" mode which invokes the generator/spatializer in fixed size chunks - Each source now has an intptr_t "memo" field that the spatializer may use to store whatever (Oculus spatializer uses this to handle the sound source limit). - The spatializer interface got a couple new methods: A "tail" method which returns a sound buffer after all sources are processed; and "create" and "destroy" methods that are called when a sound source is created or destroyed (Oculus spatializer uses this to populate/clear the "memo" field). Along the way some other miscellaneous changes got made: - lovr.audio.getSpatializerName() returns the current spatializer - Spatializer init now takes in "config in" and "config out" structs (Spatializer changes fields in config out to request things, currently fixed buffer mode). - lovr.conf now takes t.audio.spatializer (string name of desired spatializer) and t.audio.spatializerMaxSourcesHint (Spatializers with max sources limits like Oculus will use this as the limit). - audio.c went back to tracking position/orientation as vectors rather than a matrix - A file oculus_spatializer_math_shim.h was added containing a minimal copypaste of OVR_CAPI.h from Oculus SDK to support a ovrPoseStatef the spatializer API needs. This may have license consequences but we are probably OK via a combination of fair use and the fact that a user cannot use this header file without accepting Oculus's license through other means. Some work remains to be done, in particular there is an entire reverb feature I did not touch and LOVR_USE_OCULUS_AUDIO cannot be activated from tup. Oculus Spatializer works better when it has velocity and time information but this patch does not supply it.
2020-12-19 22:17:16 +00:00
#include "core/util.h"
#include <stdlib.h>
2021-02-05 21:06:25 +00:00
StringEntry lovrSampleFormat[] = {
[SAMPLE_F32] = ENTRY("f32"),
[SAMPLE_I16] = ENTRY("i16"),
{ 0 }
};
static int l_lovrSoundDataGetFormat(lua_State* L) {
2020-11-26 21:14:02 +00:00
SoundData* soundData = luax_checktype(L, 1, SoundData);
2021-02-05 21:06:25 +00:00
luax_pushenum(L, SampleFormat, soundData->format);
return 1;
}
static int l_lovrSoundDataGetSampleRate(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
lua_pushinteger(L, soundData->sampleRate);
return 1;
}
static int l_lovrSoundDataGetChannelCount(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
lua_pushinteger(L, soundData->channels);
2020-11-26 21:14:02 +00:00
return 1;
}
2018-07-06 03:23:46 +00:00
2021-02-04 18:25:06 +00:00
static int l_lovrSoundDataGetFrameCount(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
2021-02-04 18:25:06 +00:00
uint32_t frames = lovrSoundDataGetFrameCount(soundData);
lua_pushinteger(L, frames);
return 1;
}
2021-02-05 21:06:25 +00:00
static int l_lovrSoundDataIsCompressed(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
bool compressed = lovrSoundDataIsCompressed(soundData);
lua_pushboolean(L, compressed);
return 1;
}
static int l_lovrSoundDataIsStream(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
bool stream = lovrSoundDataIsStream(soundData);
lua_pushboolean(L, stream);
return 1;
}
static int l_lovrSoundDataGetBlob(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
Blob* blob = soundData->blob;
luax_pushtype(L, Blob, blob);
return 1;
}
// soundData:read(dest, {size}, {offset}) -> framesRead
// soundData:read({size}) -> framesRead
static int l_lovrSoundDataRead(lua_State* L) {
//struct SoundData* soundData, uint32_t offset, uint32_t count, void* data
SoundData* source = luax_checktype(L, 1, SoundData);
int index = 2;
SoundData* dest = luax_totype(L, index, SoundData);
2020-12-16 15:47:51 +00:00
if (dest) index++;
2021-02-04 18:25:06 +00:00
size_t frameCount = lua_type(L, index) == LUA_TNUMBER ? lua_tointeger(L, index++) : lovrSoundDataGetFrameCount(source);
size_t offset = dest ? luaL_optinteger(L, index, 0) : 0;
bool shouldRelease = false;
if (dest == NULL) {
dest = lovrSoundDataCreateRaw(frameCount, source->channels, source->sampleRate, source->format, NULL);
shouldRelease = true;
} else {
lovrAssert(dest->channels == source->channels, "Source (%d) and destination (%d) channel counts must match", source->channels, dest->channels);
lovrAssert(dest->sampleRate == source->sampleRate, "Source (%d) and destination (%d) sample rates must match", source->sampleRate, dest->sampleRate);
2021-02-05 21:06:25 +00:00
lovrAssert(dest->format == source->format, "Source (%s) and destination (%s) formats must match", lovrSampleFormat[source->format].string, lovrSampleFormat[dest->format].string);
lovrAssert(offset + frameCount <= dest->frames, "Tried to write samples past the end of a SoundData buffer");
lovrAssert(dest->blob, "Can't read into a stream destination");
}
size_t outFrames = source->read(source, offset, frameCount, dest->blob->data);
dest->frames = outFrames;
2021-02-05 21:06:25 +00:00
dest->blob->size = outFrames * lovrSoundDataGetStride(dest);
luax_pushtype(L, SoundData, dest);
if (shouldRelease) lovrRelease(SoundData, dest);
return 1;
}
2020-12-10 12:29:42 +00:00
static int l_lovrSoundDataAppend(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
Blob* blob = luax_totype(L, 2, Blob);
SoundData* sound = luax_totype(L, 2, SoundData);
2020-12-15 09:16:58 +00:00
size_t appendedSamples = 0;
2020-12-10 12:29:42 +00:00
if (sound) {
appendedSamples = lovrSoundDataStreamAppendSound(soundData, sound);
} else if (blob) {
appendedSamples = lovrSoundDataStreamAppendBlob(soundData, blob);
2020-12-15 09:32:00 +00:00
} else {
luaL_argerror(L, 2, "Invalid blob appended");
2020-12-10 12:29:42 +00:00
}
lua_pushinteger(L, appendedSamples);
return 1;
}
2021-02-05 21:06:25 +00:00
static int l_lovrSoundDataGetSample(lua_State* L) {
return 0;
}
2020-12-10 13:23:35 +00:00
static int l_lovrSoundDataSetSample(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
int index = luaL_checkinteger(L, 2);
float value = luax_checkfloat(L, 3);
lovrSoundDataSetSample(soundData, index, value);
return 0;
}
2018-07-06 03:23:46 +00:00
const luaL_Reg lovrSoundData[] = {
2021-02-05 21:06:25 +00:00
{ "getFormat", l_lovrSoundDataGetFormat },
{ "getSampleRate", l_lovrSoundDataGetSampleRate },
{ "getChannelCount", l_lovrSoundDataGetChannelCount },
2021-02-04 18:25:06 +00:00
{ "getFrameCount", l_lovrSoundDataGetFrameCount },
2021-02-05 21:06:25 +00:00
{ "isCompressed", l_lovrSoundDataIsCompressed },
{ "isStream", l_lovrSoundDataIsStream },
{ "getBlob", l_lovrSoundDataGetBlob },
{ "read", l_lovrSoundDataRead },
2020-12-10 12:29:42 +00:00
{ "append", l_lovrSoundDataAppend },
2021-02-05 21:06:25 +00:00
{ "getSample", l_lovrSoundDataGetSample },
2020-12-10 13:23:35 +00:00
{ "setSample", l_lovrSoundDataSetSample },
2020-12-03 16:07:55 +00:00
{ NULL, NULL }
2018-07-06 03:23:46 +00:00
};