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
2022-02-21 22:00:36 +00:00
static int l_lovrSoundGetByteStride ( lua_State * L ) {
Sound * sound = luax_checktype ( L , 1 , Sound ) ;
uint32_t stride = lovrSoundGetStride ( sound ) ;
lua_pushinteger ( L , stride ) ;
return 1 ;
}
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-04-15 16:40:54 +00:00
static int l_lovrSoundGetCapacity ( lua_State * L ) {
Sound * sound = luax_checktype ( L , 1 , Sound ) ;
uint32_t frames = lovrSoundGetCapacity ( sound ) ;
lua_pushinteger ( L , frames ) ;
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 ) ;
2020-12-16 15:07:29 +00:00
2021-04-05 18:52:16 +00:00
int index = lua_type ( L , 2 ) = = LUA_TNUMBER ? 2 : 3 ;
2022-02-08 20:40:10 +00:00
uint32_t dstOffset = luax_optu32 ( L , index + 2 , 0 ) ;
uint32_t srcOffset = luax_optu32 ( L , index + 1 , 0 ) ;
uint32_t count = luax_optu32 ( L , index , frameCount - srcOffset ) ;
2021-04-05 18:52:16 +00:00
lovrAssert ( srcOffset + count < = frameCount , " Tried to read samples past the end of the Sound " ) ;
lua_settop ( L , 2 ) ;
2021-02-07 13:07:50 +00:00
2021-04-05 18:52:16 +00:00
switch ( lua_type ( L , 2 ) ) {
2021-02-07 13:07:50 +00:00
case LUA_TNIL :
case LUA_TNONE :
2021-04-05 18:52:16 +00:00
case LUA_TNUMBER :
lua_pop ( L , 1 ) ;
lua_createtable ( L , dstOffset + count * channels , 0 ) ;
2021-02-20 06:10:24 +00:00
// fallthrough
2021-04-05 18:52:16 +00:00
case LUA_TTABLE : {
2021-02-07 13:07:50 +00:00
uint32_t frames = 0 ;
while ( frames < count ) {
char buffer [ 4096 ] ;
uint32_t chunk = MIN ( sizeof ( buffer ) / stride , count - frames ) ;
2021-04-05 18:52:16 +00:00
uint32_t read = lovrSoundRead ( sound , srcOffset + 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 + + ) ;
2021-04-05 18:52:16 +00:00
lua_rawseti ( L , 2 , dstOffset + ( frames * channels ) + i + 1 ) ;
2021-02-07 13:07:50 +00:00
}
} else {
float * floats = ( float * ) buffer ;
for ( uint32_t i = 0 ; i < samples ; i + + ) {
lua_pushnumber ( L , * floats + + ) ;
2021-04-05 18:52:16 +00:00
lua_rawseti ( L , 2 , dstOffset + ( frames * channels ) + i + 1 ) ;
2021-02-07 13:07:50 +00:00
}
}
frames + = read ;
}
lua_pushinteger ( L , frames ) ;
return 2 ;
2021-04-05 18:52:16 +00:00
}
case LUA_TUSERDATA : {
Sound * other = luax_totype ( L , 2 , Sound ) ;
Blob * blob = luax_totype ( L , 2 , Blob ) ;
2021-02-07 13:07:50 +00:00
if ( blob ) {
2022-02-21 21:58:13 +00:00
lovrAssert ( dstOffset + count * stride < = blob - > size , " This Blob can hold %d bytes, which is not enough space to hold %d bytes of audio data at the requested offset (%d) " , blob - > size , count * stride , dstOffset ) ;
2021-02-07 13:07:50 +00:00
char * data = ( char * ) blob - > data + dstOffset ;
uint32_t frames = 0 ;
while ( frames < count ) {
2021-04-05 18:52:16 +00:00
uint32_t read = lovrSoundRead ( sound , srcOffset + frames , count - frames , data ) ;
2021-02-07 13:07:50 +00:00
data + = read * stride ;
2021-04-05 18:52:16 +00:00
frames + = read ;
2021-02-07 13:07:50 +00:00
if ( read = = 0 ) break ;
}
lua_pushinteger ( L , frames ) ;
2021-04-05 18:52:16 +00:00
return 1 ;
2021-02-07 13:07:50 +00:00
} else if ( other ) {
2021-04-05 18:52:16 +00:00
uint32_t frames = lovrSoundCopy ( sound , other , count , srcOffset , dstOffset ) ;
2021-02-07 13:07:50 +00:00
lua_pushinteger ( L , frames ) ;
2021-04-05 18:52:16 +00:00
return 1 ;
2021-02-07 13:07:50 +00:00
}
2021-04-05 18:52:16 +00:00
}
2021-02-20 06:10:24 +00:00
// fallthrough
2021-02-07 13:07:50 +00:00
default :
2021-04-05 18:52:16 +00:00
return luax_typeerror ( L , 2 , " nil, number, 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 ) ;
2021-04-15 16:22:47 +00:00
uint32_t frameCount = lovrSoundGetCapacity ( sound ) ;
2021-02-09 02:52:56 +00:00
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 ) {
2022-02-08 20:40:10 +00:00
uint32_t srcOffset = luax_optu32 ( L , 5 , 0 ) ;
uint32_t dstOffset = luax_optu32 ( L , 4 , 0 ) ;
uint32_t count = luax_optu32 ( 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 ) {
2022-02-08 20:40:10 +00:00
uint32_t srcOffset = luax_optu32 ( L , 5 , 0 ) ;
uint32_t dstOffset = luax_optu32 ( L , 4 , 0 ) ;
uint32_t count = luax_optu32 ( L , 3 , lovrSoundGetCapacity ( other ) - srcOffset ) ;
2021-02-09 02:52:56 +00:00
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 ) ;
2022-02-08 20:40:10 +00:00
uint32_t srcOffset = luax_optu32 ( L , 5 , 1 ) ;
uint32_t dstOffset = luax_optu32 ( L , 4 , 0 ) ;
2021-04-03 16:01:37 +00:00
uint32_t limit = MIN ( frameCount - dstOffset , ( length - srcOffset ) / channels + 1 ) ;
2022-02-08 20:40:10 +00:00
uint32_t count = luax_optu32 ( L , 3 , limit ) ;
2021-04-15 16:22:47 +00:00
lovrAssert ( count < = limit , " Tried to write too many frames (%d is over limit %d) " , count , limit ) ;
2021-02-07 13:07:50 +00:00
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 + + ) {
2021-04-03 16:01:37 +00:00
lua_rawgeti ( L , 2 , srcOffset + ( frames * channels ) + i ) ;
2021-02-07 13:07:50 +00:00
* 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 + + ) {
2021-04-03 16:01:37 +00:00
lua_rawgeti ( L , 2 , srcOffset + ( frames * channels ) + i ) ;
2021-02-07 13:07:50 +00:00
* floats + + = lua_tonumber ( L , - 1 ) ;
lua_pop ( L , 1 ) ;
}
}
2021-04-03 04:28:38 +00:00
uint32_t written = lovrSoundWrite ( sound , dstOffset + 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 } ,
2022-02-21 22:00:36 +00:00
{ " getByteStride " , l_lovrSoundGetByteStride } ,
2021-02-09 02:52:56 +00:00
{ " getFrameCount " , l_lovrSoundGetFrameCount } ,
2021-04-15 16:40:54 +00:00
{ " getCapacity " , l_lovrSoundGetCapacity } ,
2021-02-09 02:52:56 +00:00
{ " 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
} ;