lovr/src/api/l_data_soundData.c

244 lines
8.0 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"
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"
2021-02-05 21:06:25 +00:00
StringEntry lovrSampleFormat[] = {
[SAMPLE_F32] = ENTRY("f32"),
[SAMPLE_I16] = ENTRY("i16"),
{ 0 }
};
2021-02-07 13:07:50 +00:00
static int l_lovrSoundDataGetBlob(lua_State* L) {
2020-11-26 21:14:02 +00:00
SoundData* soundData = luax_checktype(L, 1, SoundData);
2021-02-07 13:07:50 +00:00
Blob* blob = lovrSoundDataGetBlob(soundData);
luax_pushtype(L, Blob, blob);
2021-02-05 21:06:25 +00:00
return 1;
}
2021-02-07 13:07:50 +00:00
static int l_lovrSoundDataGetFormat(lua_State* L) {
2021-02-05 21:06:25 +00:00
SoundData* soundData = luax_checktype(L, 1, SoundData);
2021-02-07 13:07:50 +00:00
luax_pushenum(L, SampleFormat, lovrSoundDataGetFormat(soundData));
2021-02-05 21:06:25 +00:00
return 1;
}
static int l_lovrSoundDataGetChannelCount(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
2021-02-07 13:07:50 +00:00
lua_pushinteger(L, lovrSoundDataGetChannelCount(soundData));
return 1;
}
static int l_lovrSoundDataGetSampleRate(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
lua_pushinteger(L, lovrSoundDataGetSampleRate(soundData));
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-07 13:07:50 +00:00
static int l_lovrSoundDataGetSampleCount(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
uint32_t frames = lovrSoundDataGetFrameCount(soundData);
uint32_t channels = lovrSoundDataGetChannelCount(soundData);
lua_pushinteger(L, frames * channels);
return 1;
}
static int l_lovrSoundDataGetDuration(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
uint32_t frames = lovrSoundDataGetFrameCount(soundData);
uint32_t rate = lovrSoundDataGetSampleRate(soundData);
lua_pushnumber(L, (double) frames / rate);
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;
}
2021-02-07 13:07:50 +00:00
static int l_lovrSoundDataGetFrames(lua_State* L) {
2021-02-05 21:06:25 +00:00
SoundData* soundData = luax_checktype(L, 1, SoundData);
2021-02-07 13:07:50 +00:00
size_t stride = lovrSoundDataGetStride(soundData);
SampleFormat format = lovrSoundDataGetFormat(soundData);
uint32_t channels = lovrSoundDataGetChannelCount(soundData);
uint32_t frameCount = lovrSoundDataGetFrameCount(soundData);
uint32_t count = frameCount;
uint32_t offset = 0;
uint32_t dstOffset = 0;
int index = 2;
2021-02-07 13:07:50 +00:00
if (lua_type(L, 2) == LUA_TNUMBER) {
count = lua_tointeger(L, 2);
index = 3;
if (lua_type(L, 3) == LUA_TNUMBER) {
offset = lua_tointeger(L, 3);
index = 4;
}
}
2021-02-07 13:07:50 +00:00
lovrAssert(offset + count <= frameCount, "Tried to read samples past the end of the SoundData");
switch (lua_type(L, index)) {
case LUA_TNIL:
case LUA_TNONE:
lua_settop(L, index - 1);
lua_createtable(L, count * lovrSoundDataGetChannelCount(soundData), 0);
// fallthrough;
case LUA_TTABLE:
dstOffset = luaL_optinteger(L, index + 1, 1);
lua_settop(L, index);
uint32_t frames = 0;
while (frames < count) {
char buffer[4096];
uint32_t chunk = MIN(sizeof(buffer) / stride, count - frames);
uint32_t read = lovrSoundDataRead(soundData, offset + frames, chunk, buffer);
uint32_t samples = read * channels;
if (read == 0) break;
if (format == SAMPLE_I16) { // Couldn't get compiler to hoist this branch
short* shorts = (short*) buffer;
for (uint32_t i = 0; i < samples; i++) {
lua_pushnumber(L, *shorts++);
lua_rawseti(L, index, dstOffset + frames + i);
}
} else {
float* floats = (float*) buffer;
for (uint32_t i = 0; i < samples; i++) {
lua_pushnumber(L, *floats++);
lua_rawseti(L, index, dstOffset + frames + i);
}
}
frames += read;
}
lua_pushinteger(L, frames);
return 2;
case LUA_TUSERDATA:
dstOffset = luaL_optinteger(L, index + 1, 0);
lua_settop(L, index);
SoundData* other = luax_totype(L, index, SoundData);
Blob* blob = luax_totype(L, index, Blob);
if (blob) {
lovrAssert(dstOffset + count * stride <= blob->size, "Tried to write samples past the end of the Blob");
char* data = (char*) blob->data + dstOffset;
uint32_t frames = 0;
while (frames < count) {
uint32_t read = lovrSoundDataRead(soundData, offset + frames, count - frames, data);
data += read * stride;
if (read == 0) break;
}
lua_pushinteger(L, frames);
return 2;
} else if (other) {
uint32_t frames = lovrSoundDataCopy(soundData, other, count, offset, dstOffset);
lua_pushinteger(L, frames);
return 2;
}
// fallthrough;
default:
return luax_typeerror(L, index, "nil, table, Blob, or SoundData");
}
}
2021-02-07 13:07:50 +00:00
static int l_lovrSoundDataSetFrames(lua_State* L) {
2020-12-10 12:29:42 +00:00
SoundData* soundData = luax_checktype(L, 1, SoundData);
2021-02-07 13:07:50 +00:00
size_t stride = lovrSoundDataGetStride(soundData);
SampleFormat format = lovrSoundDataGetFormat(soundData);
uint32_t frameCount = lovrSoundDataGetFrameCount(soundData);
uint32_t channels = lovrSoundDataGetChannelCount(soundData);
if (lua_isuserdata(L, 2)) {
Blob* blob = luax_totype(L, 2, Blob);
if (blob) {
uint32_t srcOffset = luaL_optinteger(L, 5, 0);
uint32_t dstOffset = luaL_optinteger(L, 4, 0);
uint32_t count = luaL_optinteger(L, 3, (blob->size - srcOffset) / stride);
uint32_t frames = lovrSoundDataWrite(soundData, dstOffset, count, (char*) blob->data + srcOffset);
lua_pushinteger(L, frames);
return 1;
}
SoundData* other = luax_totype(L, 2, SoundData);
if (other) {
uint32_t srcOffset = luaL_optinteger(L, 5, 0);
uint32_t dstOffset = luaL_optinteger(L, 4, 0);
uint32_t count = luaL_optinteger(L, 3, lovrSoundDataGetFrameCount(other) - srcOffset);
uint32_t frames = lovrSoundDataCopy(other, soundData, count, srcOffset, dstOffset);
lua_pushinteger(L, frames);
return 1;
}
2020-12-10 12:29:42 +00:00
}
2021-02-07 13:07:50 +00:00
if (!lua_istable(L, 2)) {
return luax_typeerror(L, 2, "table, Blob, or SoundData");
}
2021-02-05 21:06:25 +00:00
2021-02-07 13:07:50 +00:00
int length = luax_len(L, 2);
uint32_t srcOffset = luaL_optinteger(L, 5, 1);
uint32_t dstOffset = luaL_optinteger(L, 4, 0);
uint32_t limit = MIN(frameCount - dstOffset, length - srcOffset + 1);
uint32_t count = luaL_optinteger(L, 3, limit);
lovrAssert(count <= limit, "Tried to write too many frames");
uint32_t frames = 0;
while (frames < count) {
char buffer[4096];
uint32_t chunk = MIN(sizeof(buffer) / stride, count - frames);
uint32_t samples = chunk * channels;
if (format == SAMPLE_I16) {
short* shorts = (short*) buffer;
for (uint32_t i = 0; i < samples; i++) {
lua_rawgeti(L, 2, dstOffset + frames + i);
*shorts++ = lua_tointeger(L, -1);
lua_pop(L, 1);
}
} else if (format == SAMPLE_F32) {
float* floats = (float*) buffer;
for (uint32_t i = 0; i < samples; i++) {
lua_rawgeti(L, 2, dstOffset + frames + i);
*floats++ = lua_tonumber(L, -1);
lua_pop(L, 1);
}
}
uint32_t written = lovrSoundDataWrite(soundData, srcOffset + frames, chunk, buffer);
if (written == 0) break;
frames += written;
}
lua_pushinteger(L, frames);
return 1;
2020-12-10 13:23:35 +00:00
}
2018-07-06 03:23:46 +00:00
const luaL_Reg lovrSoundData[] = {
2021-02-07 13:07:50 +00:00
{ "getBlob", l_lovrSoundDataGetBlob },
2021-02-05 21:06:25 +00:00
{ "getFormat", l_lovrSoundDataGetFormat },
{ "getChannelCount", l_lovrSoundDataGetChannelCount },
2021-02-07 13:07:50 +00:00
{ "getSampleRate", l_lovrSoundDataGetSampleRate },
2021-02-04 18:25:06 +00:00
{ "getFrameCount", l_lovrSoundDataGetFrameCount },
2021-02-07 13:07:50 +00:00
{ "getSampleCount", l_lovrSoundDataGetSampleCount },
{ "getDuration", l_lovrSoundDataGetDuration },
2021-02-05 21:06:25 +00:00
{ "isCompressed", l_lovrSoundDataIsCompressed },
{ "isStream", l_lovrSoundDataIsStream },
2021-02-07 13:07:50 +00:00
{ "getFrames", l_lovrSoundDataGetFrames },
{ "setFrames", l_lovrSoundDataSetFrames },
2020-12-03 16:07:55 +00:00
{ NULL, NULL }
2018-07-06 03:23:46 +00:00
};