2018-07-06 03:23:46 +00:00
|
|
|
#include "api.h"
|
2021-02-09 02:52:56 +00:00
|
|
|
#include "data/sound.h"
|
2020-11-26 21:14:02 +00:00
|
|
|
#include "data/blob.h"
|
2020-12-19 22:17:16 +00:00
|
|
|
#include "core/util.h"
|
2021-03-16 00:54:27 +00:00
|
|
|
#include <lua.h>
|
|
|
|
#include <lauxlib.h>
|
2020-12-16 15:07:29 +00:00
|
|
|
|
2021-02-05 21:06:25 +00:00
|
|
|
StringEntry lovrSampleFormat[] = {
|
|
|
|
[SAMPLE_F32] = ENTRY("f32"),
|
|
|
|
[SAMPLE_I16] = ENTRY("i16"),
|
|
|
|
{ 0 }
|
|
|
|
};
|
|
|
|
|
2021-02-19 04:17:25 +00:00
|
|
|
StringEntry lovrChannelLayout[] = {
|
|
|
|
[CHANNEL_MONO] = ENTRY("mono"),
|
|
|
|
[CHANNEL_STEREO] = ENTRY("stereo"),
|
|
|
|
[CHANNEL_AMBISONIC] = ENTRY("ambisonic"),
|
|
|
|
{ 0 }
|
|
|
|
};
|
|
|
|
|
2021-02-09 02:52:56 +00:00
|
|
|
static int l_lovrSoundGetBlob(lua_State* L) {
|
|
|
|
Sound* sound = luax_checktype(L, 1, Sound);
|
|
|
|
Blob* blob = lovrSoundGetBlob(sound);
|
2021-02-07 13:07:50 +00:00
|
|
|
luax_pushtype(L, Blob, blob);
|
2021-02-05 21:06:25 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-02-09 02:52:56 +00:00
|
|
|
static int l_lovrSoundGetFormat(lua_State* L) {
|
|
|
|
Sound* sound = luax_checktype(L, 1, Sound);
|
|
|
|
luax_pushenum(L, SampleFormat, lovrSoundGetFormat(sound));
|
2021-02-05 21:06:25 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-02-19 04:17:25 +00:00
|
|
|
static int l_lovrSoundGetChannelLayout(lua_State* L) {
|
|
|
|
Sound* sound = luax_checktype(L, 1, Sound);
|
|
|
|
luax_pushenum(L, ChannelLayout, lovrSoundGetChannelLayout(sound));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-02-09 02:52:56 +00:00
|
|
|
static int l_lovrSoundGetChannelCount(lua_State* L) {
|
|
|
|
Sound* sound = luax_checktype(L, 1, Sound);
|
|
|
|
lua_pushinteger(L, lovrSoundGetChannelCount(sound));
|
2021-02-07 13:07:50 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-02-09 02:52:56 +00:00
|
|
|
static int l_lovrSoundGetSampleRate(lua_State* L) {
|
|
|
|
Sound* sound = luax_checktype(L, 1, Sound);
|
|
|
|
lua_pushinteger(L, lovrSoundGetSampleRate(sound));
|
2020-11-26 21:14:02 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2018-07-06 03:23:46 +00:00
|
|
|
|
2021-02-09 02:52:56 +00:00
|
|
|
static int l_lovrSoundGetFrameCount(lua_State* L) {
|
|
|
|
Sound* sound = luax_checktype(L, 1, Sound);
|
|
|
|
uint32_t frames = lovrSoundGetFrameCount(sound);
|
2021-02-04 18:25:06 +00:00
|
|
|
lua_pushinteger(L, frames);
|
2020-12-16 15:07:29 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-02-09 02:52:56 +00:00
|
|
|
static int l_lovrSoundGetSampleCount(lua_State* L) {
|
|
|
|
Sound* sound = luax_checktype(L, 1, Sound);
|
|
|
|
uint32_t frames = lovrSoundGetFrameCount(sound);
|
|
|
|
uint32_t channels = lovrSoundGetChannelCount(sound);
|
2021-02-07 13:07:50 +00:00
|
|
|
lua_pushinteger(L, frames * channels);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-02-09 02:52:56 +00:00
|
|
|
static int l_lovrSoundGetDuration(lua_State* L) {
|
|
|
|
Sound* sound = luax_checktype(L, 1, Sound);
|
|
|
|
uint32_t frames = lovrSoundGetFrameCount(sound);
|
|
|
|
uint32_t rate = lovrSoundGetSampleRate(sound);
|
2021-02-07 13:07:50 +00:00
|
|
|
lua_pushnumber(L, (double) frames / rate);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-02-09 02:52:56 +00:00
|
|
|
static int l_lovrSoundIsCompressed(lua_State* L) {
|
|
|
|
Sound* sound = luax_checktype(L, 1, Sound);
|
|
|
|
bool compressed = lovrSoundIsCompressed(sound);
|
2021-02-05 21:06:25 +00:00
|
|
|
lua_pushboolean(L, compressed);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-02-09 02:52:56 +00:00
|
|
|
static int l_lovrSoundIsStream(lua_State* L) {
|
|
|
|
Sound* sound = luax_checktype(L, 1, Sound);
|
|
|
|
bool stream = lovrSoundIsStream(sound);
|
2021-02-05 21:06:25 +00:00
|
|
|
lua_pushboolean(L, stream);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-02-09 02:52:56 +00:00
|
|
|
static int l_lovrSoundGetFrames(lua_State* L) {
|
|
|
|
Sound* sound = luax_checktype(L, 1, Sound);
|
|
|
|
size_t stride = lovrSoundGetStride(sound);
|
|
|
|
SampleFormat format = lovrSoundGetFormat(sound);
|
|
|
|
uint32_t channels = lovrSoundGetChannelCount(sound);
|
|
|
|
uint32_t frameCount = lovrSoundGetFrameCount(sound);
|
2021-02-07 13:07:50 +00:00
|
|
|
uint32_t count = frameCount;
|
|
|
|
uint32_t offset = 0;
|
|
|
|
uint32_t dstOffset = 0;
|
2020-12-16 15:07:29 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2020-12-16 15:07:29 +00:00
|
|
|
}
|
|
|
|
|
2021-02-09 02:52:56 +00:00
|
|
|
lovrAssert(offset + count <= frameCount, "Tried to read samples past the end of the Sound");
|
2021-02-07 13:07:50 +00:00
|
|
|
|
|
|
|
switch (lua_type(L, index)) {
|
|
|
|
case LUA_TNIL:
|
|
|
|
case LUA_TNONE:
|
|
|
|
lua_settop(L, index - 1);
|
2021-02-09 02:52:56 +00:00
|
|
|
lua_createtable(L, count * lovrSoundGetChannelCount(sound), 0);
|
2021-02-20 06:10:24 +00:00
|
|
|
// fallthrough
|
2021-02-07 13:07:50 +00:00
|
|
|
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);
|
2021-02-09 02:52:56 +00:00
|
|
|
uint32_t read = lovrSoundRead(sound, offset + frames, chunk, buffer);
|
2021-02-07 13:07:50 +00:00
|
|
|
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);
|
2021-02-09 02:52:56 +00:00
|
|
|
Sound* other = luax_totype(L, index, Sound);
|
2021-02-07 13:07:50 +00:00
|
|
|
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) {
|
2021-02-09 02:52:56 +00:00
|
|
|
uint32_t read = lovrSoundRead(sound, offset + frames, count - frames, data);
|
2021-02-07 13:07:50 +00:00
|
|
|
data += read * stride;
|
|
|
|
if (read == 0) break;
|
|
|
|
}
|
|
|
|
lua_pushinteger(L, frames);
|
|
|
|
return 2;
|
|
|
|
} else if (other) {
|
2021-02-09 02:52:56 +00:00
|
|
|
uint32_t frames = lovrSoundCopy(sound, other, count, offset, dstOffset);
|
2021-02-07 13:07:50 +00:00
|
|
|
lua_pushinteger(L, frames);
|
|
|
|
return 2;
|
|
|
|
}
|
2021-02-20 06:10:24 +00:00
|
|
|
// fallthrough
|
2021-02-07 13:07:50 +00:00
|
|
|
default:
|
2021-02-09 02:52:56 +00:00
|
|
|
return luax_typeerror(L, index, "nil, table, Blob, or Sound");
|
2021-02-07 13:07:50 +00:00
|
|
|
}
|
2020-12-16 15:07:29 +00:00
|
|
|
}
|
|
|
|
|
2021-02-09 02:52:56 +00:00
|
|
|
static int l_lovrSoundSetFrames(lua_State* L) {
|
|
|
|
Sound* sound = luax_checktype(L, 1, Sound);
|
|
|
|
size_t stride = lovrSoundGetStride(sound);
|
|
|
|
SampleFormat format = lovrSoundGetFormat(sound);
|
|
|
|
uint32_t frameCount = lovrSoundGetFrameCount(sound);
|
|
|
|
uint32_t channels = lovrSoundGetChannelCount(sound);
|
2021-02-07 13:07:50 +00:00
|
|
|
|
|
|
|
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);
|
2021-02-09 02:52:56 +00:00
|
|
|
uint32_t frames = lovrSoundWrite(sound, dstOffset, count, (char*) blob->data + srcOffset);
|
2021-02-07 13:07:50 +00:00
|
|
|
lua_pushinteger(L, frames);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-02-09 02:52:56 +00:00
|
|
|
Sound* other = luax_totype(L, 2, Sound);
|
2021-02-07 13:07:50 +00:00
|
|
|
|
|
|
|
if (other) {
|
|
|
|
uint32_t srcOffset = luaL_optinteger(L, 5, 0);
|
|
|
|
uint32_t dstOffset = luaL_optinteger(L, 4, 0);
|
2021-02-09 02:52:56 +00:00
|
|
|
uint32_t count = luaL_optinteger(L, 3, lovrSoundGetFrameCount(other) - srcOffset);
|
|
|
|
uint32_t frames = lovrSoundCopy(other, sound, count, srcOffset, dstOffset);
|
2021-02-07 13:07:50 +00:00
|
|
|
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)) {
|
2021-02-09 02:52:56 +00:00
|
|
|
return luax_typeerror(L, 2, "table, Blob, or Sound");
|
2021-02-07 13:07:50 +00:00
|
|
|
}
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-09 02:52:56 +00:00
|
|
|
uint32_t written = lovrSoundWrite(sound, srcOffset + frames, chunk, buffer);
|
2021-02-07 13:07:50 +00:00
|
|
|
if (written == 0) break;
|
|
|
|
frames += written;
|
|
|
|
}
|
|
|
|
lua_pushinteger(L, frames);
|
|
|
|
return 1;
|
2020-12-10 13:23:35 +00:00
|
|
|
}
|
|
|
|
|
2021-02-09 02:52:56 +00:00
|
|
|
const luaL_Reg lovrSound[] = {
|
|
|
|
{ "getBlob", l_lovrSoundGetBlob },
|
|
|
|
{ "getFormat", l_lovrSoundGetFormat },
|
2021-02-19 04:17:25 +00:00
|
|
|
{ "getChannelLayout", l_lovrSoundGetChannelLayout },
|
2021-02-09 02:52:56 +00:00
|
|
|
{ "getChannelCount", l_lovrSoundGetChannelCount },
|
|
|
|
{ "getSampleRate", l_lovrSoundGetSampleRate },
|
|
|
|
{ "getFrameCount", l_lovrSoundGetFrameCount },
|
|
|
|
{ "getSampleCount", l_lovrSoundGetSampleCount },
|
|
|
|
{ "getDuration", l_lovrSoundGetDuration },
|
|
|
|
{ "isCompressed", l_lovrSoundIsCompressed },
|
|
|
|
{ "isStream", l_lovrSoundIsStream },
|
|
|
|
{ "getFrames", l_lovrSoundGetFrames },
|
|
|
|
{ "setFrames", l_lovrSoundSetFrames },
|
2020-12-03 16:07:55 +00:00
|
|
|
{ NULL, NULL }
|
2018-07-06 03:23:46 +00:00
|
|
|
};
|