mirror of https://github.com/bjornbytes/lovr.git
SoundData is now named Sound!;
This commit is contained in:
parent
8c714c45b0
commit
dca79f83f0
|
@ -353,13 +353,13 @@ if(LOVR_ENABLE_DATA)
|
|||
src/modules/data/modelData_gltf.c
|
||||
src/modules/data/modelData_obj.c
|
||||
src/modules/data/rasterizer.c
|
||||
src/modules/data/soundData.c
|
||||
src/modules/data/sound.c
|
||||
src/modules/data/textureData.c
|
||||
src/api/l_data.c
|
||||
src/api/l_data_blob.c
|
||||
src/api/l_data_modelData.c
|
||||
src/api/l_data_rasterizer.c
|
||||
src/api/l_data_soundData.c
|
||||
src/api/l_data_sound.c
|
||||
src/api/l_data_textureData.c
|
||||
src/lib/minimp3/minimp3.c
|
||||
src/lib/stb/stb_image.c
|
||||
|
|
|
@ -53,7 +53,7 @@ extern const luaL_Reg lovrRasterizer[];
|
|||
extern const luaL_Reg lovrShader[];
|
||||
extern const luaL_Reg lovrShaderBlock[];
|
||||
extern const luaL_Reg lovrSliderJoint[];
|
||||
extern const luaL_Reg lovrSoundData[];
|
||||
extern const luaL_Reg lovrSound[];
|
||||
extern const luaL_Reg lovrSource[];
|
||||
extern const luaL_Reg lovrSphereShape[];
|
||||
extern const luaL_Reg lovrMeshShape[];
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "api.h"
|
||||
#include "audio/audio.h"
|
||||
#include "data/blob.h"
|
||||
#include "data/soundData.h"
|
||||
#include "data/sound.h"
|
||||
#include "core/maf.h"
|
||||
#include "core/util.h"
|
||||
#include <stdlib.h>
|
||||
|
@ -111,20 +111,20 @@ static int l_lovrAudioGetSpatializer(lua_State *L) {
|
|||
}
|
||||
|
||||
static int l_lovrAudioGetCaptureStream(lua_State* L) {
|
||||
SoundData* soundData = lovrAudioGetCaptureStream();
|
||||
luax_pushtype(L, SoundData, soundData);
|
||||
Sound* sound = lovrAudioGetCaptureStream();
|
||||
luax_pushtype(L, Sound, sound);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrAudioNewSource(lua_State* L) {
|
||||
SoundData* soundData = luax_totype(L, 1, SoundData);
|
||||
Sound* sound = luax_totype(L, 1, Sound);
|
||||
|
||||
if (!soundData) {
|
||||
if (!sound) {
|
||||
Blob* blob = luax_readblob(L, 1, "Source");
|
||||
soundData = lovrSoundDataCreateFromFile(blob, false);
|
||||
sound = lovrSoundCreateFromFile(blob, false);
|
||||
lovrRelease(blob, lovrBlobDestroy);
|
||||
} else {
|
||||
lovrRetain(soundData);
|
||||
lovrRetain(sound);
|
||||
}
|
||||
|
||||
bool spatial = true;
|
||||
|
@ -134,9 +134,9 @@ static int l_lovrAudioNewSource(lua_State* L) {
|
|||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
Source* source = lovrSourceCreate(soundData, spatial);
|
||||
Source* source = lovrSourceCreate(sound, spatial);
|
||||
luax_pushtype(L, Source, source);
|
||||
lovrRelease(soundData, lovrSoundDataDestroy);
|
||||
lovrRelease(sound, lovrSoundDestroy);
|
||||
lovrRelease(source, lovrSourceDestroy);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include "api.h"
|
||||
#include "audio/audio.h"
|
||||
#include "data/soundData.h"
|
||||
#include "core/maf.h"
|
||||
#include "core/util.h"
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include "data/blob.h"
|
||||
#include "data/modelData.h"
|
||||
#include "data/rasterizer.h"
|
||||
#include "data/soundData.h"
|
||||
#include "data/sound.h"
|
||||
#include "data/textureData.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -64,7 +64,7 @@ static int l_lovrDataNewRasterizer(lua_State* L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrDataNewSoundData(lua_State* L) {
|
||||
static int l_lovrDataNewSound(lua_State* L) {
|
||||
if (lua_type(L, 1) == LUA_TNUMBER) {
|
||||
uint64_t frames = luaL_checkinteger(L, 1);
|
||||
SampleFormat format = luax_checkenum(L, 2, SampleFormat, "f32");
|
||||
|
@ -73,20 +73,20 @@ static int l_lovrDataNewSoundData(lua_State* L) {
|
|||
Blob* blob = luax_totype(L, 5, Blob);
|
||||
const char* other = lua_tostring(L, 5);
|
||||
bool stream = other && !strcmp(other, "stream");
|
||||
SoundData* soundData = stream ?
|
||||
lovrSoundDataCreateStream(frames, format, channels, sampleRate) :
|
||||
lovrSoundDataCreateRaw(frames, format, channels, sampleRate, blob);
|
||||
luax_pushtype(L, SoundData, soundData);
|
||||
lovrRelease(soundData, lovrSoundDataDestroy);
|
||||
Sound* sound = stream ?
|
||||
lovrSoundCreateStream(frames, format, channels, sampleRate) :
|
||||
lovrSoundCreateRaw(frames, format, channels, sampleRate, blob);
|
||||
luax_pushtype(L, Sound, sound);
|
||||
lovrRelease(sound, lovrSoundDestroy);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Blob* blob = luax_readblob(L, 1, "SoundData");
|
||||
Blob* blob = luax_readblob(L, 1, "Sound");
|
||||
bool decode = lua_toboolean(L, 2);
|
||||
SoundData* soundData = lovrSoundDataCreateFromFile(blob, decode);
|
||||
luax_pushtype(L, SoundData, soundData);
|
||||
Sound* sound = lovrSoundCreateFromFile(blob, decode);
|
||||
luax_pushtype(L, Sound, sound);
|
||||
lovrRelease(blob, lovrBlobDestroy);
|
||||
lovrRelease(soundData, lovrSoundDataDestroy);
|
||||
lovrRelease(sound, lovrSoundDestroy);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -119,7 +119,7 @@ static const luaL_Reg lovrData[] = {
|
|||
{ "newBlob", l_lovrDataNewBlob },
|
||||
{ "newModelData", l_lovrDataNewModelData },
|
||||
{ "newRasterizer", l_lovrDataNewRasterizer },
|
||||
{ "newSoundData", l_lovrDataNewSoundData },
|
||||
{ "newSound", l_lovrDataNewSound },
|
||||
{ "newTextureData", l_lovrDataNewTextureData },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
@ -130,7 +130,7 @@ int luaopen_lovr_data(lua_State* L) {
|
|||
luax_registertype(L, Blob);
|
||||
luax_registertype(L, ModelData);
|
||||
luax_registertype(L, Rasterizer);
|
||||
luax_registertype(L, SoundData);
|
||||
luax_registertype(L, Sound);
|
||||
luax_registertype(L, TextureData);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "api.h"
|
||||
#include "data/soundData.h"
|
||||
#include "data/sound.h"
|
||||
#include "data/blob.h"
|
||||
#include "core/util.h"
|
||||
|
||||
|
@ -9,74 +9,74 @@ StringEntry lovrSampleFormat[] = {
|
|||
{ 0 }
|
||||
};
|
||||
|
||||
static int l_lovrSoundDataGetBlob(lua_State* L) {
|
||||
SoundData* soundData = luax_checktype(L, 1, SoundData);
|
||||
Blob* blob = lovrSoundDataGetBlob(soundData);
|
||||
static int l_lovrSoundGetBlob(lua_State* L) {
|
||||
Sound* sound = luax_checktype(L, 1, Sound);
|
||||
Blob* blob = lovrSoundGetBlob(sound);
|
||||
luax_pushtype(L, Blob, blob);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrSoundDataGetFormat(lua_State* L) {
|
||||
SoundData* soundData = luax_checktype(L, 1, SoundData);
|
||||
luax_pushenum(L, SampleFormat, lovrSoundDataGetFormat(soundData));
|
||||
static int l_lovrSoundGetFormat(lua_State* L) {
|
||||
Sound* sound = luax_checktype(L, 1, Sound);
|
||||
luax_pushenum(L, SampleFormat, lovrSoundGetFormat(sound));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrSoundDataGetChannelCount(lua_State* L) {
|
||||
SoundData* soundData = luax_checktype(L, 1, SoundData);
|
||||
lua_pushinteger(L, lovrSoundDataGetChannelCount(soundData));
|
||||
static int l_lovrSoundGetChannelCount(lua_State* L) {
|
||||
Sound* sound = luax_checktype(L, 1, Sound);
|
||||
lua_pushinteger(L, lovrSoundGetChannelCount(sound));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrSoundDataGetSampleRate(lua_State* L) {
|
||||
SoundData* soundData = luax_checktype(L, 1, SoundData);
|
||||
lua_pushinteger(L, lovrSoundDataGetSampleRate(soundData));
|
||||
static int l_lovrSoundGetSampleRate(lua_State* L) {
|
||||
Sound* sound = luax_checktype(L, 1, Sound);
|
||||
lua_pushinteger(L, lovrSoundGetSampleRate(sound));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrSoundDataGetFrameCount(lua_State* L) {
|
||||
SoundData* soundData = luax_checktype(L, 1, SoundData);
|
||||
uint32_t frames = lovrSoundDataGetFrameCount(soundData);
|
||||
static int l_lovrSoundGetFrameCount(lua_State* L) {
|
||||
Sound* sound = luax_checktype(L, 1, Sound);
|
||||
uint32_t frames = lovrSoundGetFrameCount(sound);
|
||||
lua_pushinteger(L, frames);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrSoundDataGetSampleCount(lua_State* L) {
|
||||
SoundData* soundData = luax_checktype(L, 1, SoundData);
|
||||
uint32_t frames = lovrSoundDataGetFrameCount(soundData);
|
||||
uint32_t channels = lovrSoundDataGetChannelCount(soundData);
|
||||
static int l_lovrSoundGetSampleCount(lua_State* L) {
|
||||
Sound* sound = luax_checktype(L, 1, Sound);
|
||||
uint32_t frames = lovrSoundGetFrameCount(sound);
|
||||
uint32_t channels = lovrSoundGetChannelCount(sound);
|
||||
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);
|
||||
static int l_lovrSoundGetDuration(lua_State* L) {
|
||||
Sound* sound = luax_checktype(L, 1, Sound);
|
||||
uint32_t frames = lovrSoundGetFrameCount(sound);
|
||||
uint32_t rate = lovrSoundGetSampleRate(sound);
|
||||
lua_pushnumber(L, (double) frames / rate);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrSoundDataIsCompressed(lua_State* L) {
|
||||
SoundData* soundData = luax_checktype(L, 1, SoundData);
|
||||
bool compressed = lovrSoundDataIsCompressed(soundData);
|
||||
static int l_lovrSoundIsCompressed(lua_State* L) {
|
||||
Sound* sound = luax_checktype(L, 1, Sound);
|
||||
bool compressed = lovrSoundIsCompressed(sound);
|
||||
lua_pushboolean(L, compressed);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrSoundDataIsStream(lua_State* L) {
|
||||
SoundData* soundData = luax_checktype(L, 1, SoundData);
|
||||
bool stream = lovrSoundDataIsStream(soundData);
|
||||
static int l_lovrSoundIsStream(lua_State* L) {
|
||||
Sound* sound = luax_checktype(L, 1, Sound);
|
||||
bool stream = lovrSoundIsStream(sound);
|
||||
lua_pushboolean(L, stream);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrSoundDataGetFrames(lua_State* L) {
|
||||
SoundData* soundData = luax_checktype(L, 1, SoundData);
|
||||
size_t stride = lovrSoundDataGetStride(soundData);
|
||||
SampleFormat format = lovrSoundDataGetFormat(soundData);
|
||||
uint32_t channels = lovrSoundDataGetChannelCount(soundData);
|
||||
uint32_t frameCount = lovrSoundDataGetFrameCount(soundData);
|
||||
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);
|
||||
uint32_t count = frameCount;
|
||||
uint32_t offset = 0;
|
||||
uint32_t dstOffset = 0;
|
||||
|
@ -91,13 +91,13 @@ static int l_lovrSoundDataGetFrames(lua_State* L) {
|
|||
}
|
||||
}
|
||||
|
||||
lovrAssert(offset + count <= frameCount, "Tried to read samples past the end of the SoundData");
|
||||
lovrAssert(offset + count <= frameCount, "Tried to read samples past the end of the Sound");
|
||||
|
||||
switch (lua_type(L, index)) {
|
||||
case LUA_TNIL:
|
||||
case LUA_TNONE:
|
||||
lua_settop(L, index - 1);
|
||||
lua_createtable(L, count * lovrSoundDataGetChannelCount(soundData), 0);
|
||||
lua_createtable(L, count * lovrSoundGetChannelCount(sound), 0);
|
||||
// fallthrough;
|
||||
case LUA_TTABLE:
|
||||
dstOffset = luaL_optinteger(L, index + 1, 1);
|
||||
|
@ -106,7 +106,7 @@ static int l_lovrSoundDataGetFrames(lua_State* L) {
|
|||
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 read = lovrSoundRead(sound, offset + frames, chunk, buffer);
|
||||
uint32_t samples = read * channels;
|
||||
if (read == 0) break;
|
||||
|
||||
|
@ -131,36 +131,36 @@ static int l_lovrSoundDataGetFrames(lua_State* L) {
|
|||
case LUA_TUSERDATA:
|
||||
dstOffset = luaL_optinteger(L, index + 1, 0);
|
||||
lua_settop(L, index);
|
||||
SoundData* other = luax_totype(L, index, SoundData);
|
||||
Sound* other = luax_totype(L, index, Sound);
|
||||
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);
|
||||
uint32_t read = lovrSoundRead(sound, 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);
|
||||
uint32_t frames = lovrSoundCopy(sound, other, count, offset, dstOffset);
|
||||
lua_pushinteger(L, frames);
|
||||
return 2;
|
||||
}
|
||||
// fallthrough;
|
||||
default:
|
||||
return luax_typeerror(L, index, "nil, table, Blob, or SoundData");
|
||||
return luax_typeerror(L, index, "nil, table, Blob, or Sound");
|
||||
}
|
||||
}
|
||||
|
||||
static int l_lovrSoundDataSetFrames(lua_State* L) {
|
||||
SoundData* soundData = luax_checktype(L, 1, SoundData);
|
||||
size_t stride = lovrSoundDataGetStride(soundData);
|
||||
SampleFormat format = lovrSoundDataGetFormat(soundData);
|
||||
uint32_t frameCount = lovrSoundDataGetFrameCount(soundData);
|
||||
uint32_t channels = lovrSoundDataGetChannelCount(soundData);
|
||||
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);
|
||||
|
||||
if (lua_isuserdata(L, 2)) {
|
||||
Blob* blob = luax_totype(L, 2, Blob);
|
||||
|
@ -169,25 +169,25 @@ static int l_lovrSoundDataSetFrames(lua_State* L) {
|
|||
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);
|
||||
uint32_t frames = lovrSoundWrite(sound, dstOffset, count, (char*) blob->data + srcOffset);
|
||||
lua_pushinteger(L, frames);
|
||||
return 1;
|
||||
}
|
||||
|
||||
SoundData* other = luax_totype(L, 2, SoundData);
|
||||
Sound* other = luax_totype(L, 2, Sound);
|
||||
|
||||
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);
|
||||
uint32_t count = luaL_optinteger(L, 3, lovrSoundGetFrameCount(other) - srcOffset);
|
||||
uint32_t frames = lovrSoundCopy(other, sound, count, srcOffset, dstOffset);
|
||||
lua_pushinteger(L, frames);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!lua_istable(L, 2)) {
|
||||
return luax_typeerror(L, 2, "table, Blob, or SoundData");
|
||||
return luax_typeerror(L, 2, "table, Blob, or Sound");
|
||||
}
|
||||
|
||||
int length = luax_len(L, 2);
|
||||
|
@ -219,7 +219,7 @@ static int l_lovrSoundDataSetFrames(lua_State* L) {
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t written = lovrSoundDataWrite(soundData, srcOffset + frames, chunk, buffer);
|
||||
uint32_t written = lovrSoundWrite(sound, srcOffset + frames, chunk, buffer);
|
||||
if (written == 0) break;
|
||||
frames += written;
|
||||
}
|
||||
|
@ -227,17 +227,17 @@ static int l_lovrSoundDataSetFrames(lua_State* L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
const luaL_Reg lovrSoundData[] = {
|
||||
{ "getBlob", l_lovrSoundDataGetBlob },
|
||||
{ "getFormat", l_lovrSoundDataGetFormat },
|
||||
{ "getChannelCount", l_lovrSoundDataGetChannelCount },
|
||||
{ "getSampleRate", l_lovrSoundDataGetSampleRate },
|
||||
{ "getFrameCount", l_lovrSoundDataGetFrameCount },
|
||||
{ "getSampleCount", l_lovrSoundDataGetSampleCount },
|
||||
{ "getDuration", l_lovrSoundDataGetDuration },
|
||||
{ "isCompressed", l_lovrSoundDataIsCompressed },
|
||||
{ "isStream", l_lovrSoundDataIsStream },
|
||||
{ "getFrames", l_lovrSoundDataGetFrames },
|
||||
{ "setFrames", l_lovrSoundDataSetFrames },
|
||||
const luaL_Reg lovrSound[] = {
|
||||
{ "getBlob", l_lovrSoundGetBlob },
|
||||
{ "getFormat", l_lovrSoundGetFormat },
|
||||
{ "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 },
|
||||
{ NULL, NULL }
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
#include "audio/audio.h"
|
||||
#include "audio/spatializer.h"
|
||||
#include "data/soundData.h"
|
||||
#include "data/sound.h"
|
||||
#include "core/os.h"
|
||||
#include "core/util.h"
|
||||
#include "lib/miniaudio/miniaudio.h"
|
||||
|
@ -21,7 +21,7 @@ static const ma_format miniaudioFormats[] = {
|
|||
struct Source {
|
||||
ref_t ref;
|
||||
Source* next;
|
||||
SoundData* sound;
|
||||
Sound* sound;
|
||||
ma_data_converter* converter;
|
||||
intptr_t spatializerMemo;
|
||||
uint32_t offset;
|
||||
|
@ -40,7 +40,7 @@ static struct {
|
|||
ma_device devices[2];
|
||||
ma_mutex lock;
|
||||
Source* sources;
|
||||
SoundData* captureStream;
|
||||
Sound* captureStream;
|
||||
arr_t(ma_data_converter*) converters;
|
||||
float position[4];
|
||||
float orientation[4];
|
||||
|
@ -92,13 +92,13 @@ static void onPlayback(ma_device* device, void* out, const void* in, uint32_t co
|
|||
|
||||
// Read and convert raw frames until there's BUFFER_SIZE converted frames
|
||||
uint32_t channels = source->spatial ? 1 : 2;
|
||||
uint64_t frameLimit = sizeof(raw) / lovrSoundDataGetChannelCount(source->sound) / sizeof(float);
|
||||
uint64_t frameLimit = sizeof(raw) / lovrSoundGetChannelCount(source->sound) / sizeof(float);
|
||||
uint32_t framesToConvert = BUFFER_SIZE;
|
||||
uint32_t framesConverted = 0;
|
||||
while (framesToConvert > 0) {
|
||||
src = raw;
|
||||
uint64_t framesToRead = source->converter ? MIN(ma_data_converter_get_required_input_frame_count(source->converter, framesToConvert), frameLimit) : framesToConvert;
|
||||
uint64_t framesRead = lovrSoundDataRead(source->sound, source->offset, framesToRead, src);
|
||||
uint64_t framesRead = lovrSoundRead(source->sound, source->offset, framesToRead, src);
|
||||
ma_uint64 framesIn = framesRead;
|
||||
ma_uint64 framesOut = framesToConvert;
|
||||
|
||||
|
@ -160,7 +160,7 @@ static void onPlayback(ma_device* device, void* out, const void* in, uint32_t co
|
|||
}
|
||||
|
||||
static void onCapture(ma_device* device, void* output, const void* input, uint32_t count) {
|
||||
lovrSoundDataWrite(state.captureStream, 0, count, input);
|
||||
lovrSoundWrite(state.captureStream, 0, count, input);
|
||||
}
|
||||
|
||||
static const ma_device_callback_proc callbacks[] = { onPlayback, onCapture };
|
||||
|
@ -213,7 +213,7 @@ void lovrAudioDestroy() {
|
|||
}
|
||||
ma_mutex_uninit(&state.lock);
|
||||
ma_context_uninit(&state.context);
|
||||
lovrRelease(state.captureStream, lovrSoundDataDestroy);
|
||||
lovrRelease(state.captureStream, lovrSoundDestroy);
|
||||
if (state.spatializer) state.spatializer->destroy();
|
||||
for (size_t i = 0; i < state.converters.length; i++) {
|
||||
ma_data_converter_uninit(state.converters.data[i]);
|
||||
|
@ -268,8 +268,8 @@ bool lovrAudioSetDevice(AudioType type, void* id, size_t size, uint32_t sampleRa
|
|||
config.capture.format = miniaudioFormats[format];
|
||||
config.capture.channels = CAPTURE_CHANNELS;
|
||||
config.capture.shareMode = exclusive ? ma_share_mode_exclusive : ma_share_mode_shared;
|
||||
lovrRelease(state.captureStream, lovrSoundDataDestroy);
|
||||
state.captureStream = lovrSoundDataCreateStream(sampleRate * 1., format, CAPTURE_CHANNELS, sampleRate);
|
||||
lovrRelease(state.captureStream, lovrSoundDestroy);
|
||||
state.captureStream = lovrSoundCreateStream(sampleRate * 1., format, CAPTURE_CHANNELS, sampleRate);
|
||||
}
|
||||
|
||||
config.sampleRate = sampleRate;
|
||||
|
@ -316,13 +316,13 @@ const char* lovrAudioGetSpatializer() {
|
|||
return state.spatializer->name;
|
||||
}
|
||||
|
||||
struct SoundData* lovrAudioGetCaptureStream() {
|
||||
Sound* lovrAudioGetCaptureStream() {
|
||||
return state.captureStream;
|
||||
}
|
||||
|
||||
// Source
|
||||
|
||||
Source* lovrSourceCreate(SoundData* sound, bool spatial) {
|
||||
Source* lovrSourceCreate(Sound* sound, bool spatial) {
|
||||
Source* source = calloc(1, sizeof(Source));
|
||||
lovrAssert(source, "Out of memory");
|
||||
source->ref = 1;
|
||||
|
@ -333,11 +333,11 @@ Source* lovrSourceCreate(SoundData* sound, bool spatial) {
|
|||
source->spatial = spatial;
|
||||
|
||||
ma_data_converter_config config = ma_data_converter_config_init_default();
|
||||
config.formatIn = miniaudioFormats[lovrSoundDataGetFormat(sound)];
|
||||
config.formatIn = miniaudioFormats[lovrSoundGetFormat(sound)];
|
||||
config.formatOut = miniaudioFormats[OUTPUT_FORMAT];
|
||||
config.channelsIn = lovrSoundDataGetChannelCount(sound);
|
||||
config.channelsIn = lovrSoundGetChannelCount(sound);
|
||||
config.channelsOut = spatial ? 1 : 2;
|
||||
config.sampleRateIn = lovrSoundDataGetSampleRate(sound);
|
||||
config.sampleRateIn = lovrSoundGetSampleRate(sound);
|
||||
config.sampleRateOut = PLAYBACK_SAMPLE_RATE;
|
||||
|
||||
if (config.formatIn != config.formatOut || config.channelsIn == config.channelsOut || config.sampleRateIn != config.sampleRateOut) {
|
||||
|
@ -368,7 +368,7 @@ Source* lovrSourceCreate(SoundData* sound, bool spatial) {
|
|||
void lovrSourceDestroy(void* ref) {
|
||||
Source* source = ref;
|
||||
state.spatializer->sourceDestroy(source);
|
||||
lovrRelease(source->sound, lovrSoundDataDestroy);
|
||||
lovrRelease(source->sound, lovrSoundDestroy);
|
||||
free(source);
|
||||
}
|
||||
|
||||
|
@ -406,7 +406,7 @@ bool lovrSourceIsLooping(Source* source) {
|
|||
}
|
||||
|
||||
void lovrSourceSetLooping(Source* source, bool loop) {
|
||||
lovrAssert(loop == false || lovrSoundDataIsStream(source->sound) == false, "Can't loop streams");
|
||||
lovrAssert(loop == false || lovrSoundIsStream(source->sound) == false, "Can't loop streams");
|
||||
source->looping = loop;
|
||||
}
|
||||
|
||||
|
@ -437,17 +437,17 @@ void lovrSourceSetPose(Source *source, float position[4], float orientation[4])
|
|||
}
|
||||
|
||||
double lovrSourceGetDuration(Source* source, TimeUnit units) {
|
||||
uint32_t frames = lovrSoundDataGetFrameCount(source->sound);
|
||||
return units == UNIT_SECONDS ? (double) frames / lovrSoundDataGetSampleRate(source->sound) : frames;
|
||||
uint32_t frames = lovrSoundGetFrameCount(source->sound);
|
||||
return units == UNIT_SECONDS ? (double) frames / lovrSoundGetSampleRate(source->sound) : frames;
|
||||
}
|
||||
|
||||
double lovrSourceGetTime(Source* source, TimeUnit units) {
|
||||
return units == UNIT_SECONDS ? (double) source->offset / lovrSoundDataGetSampleRate(source->sound) : source->offset;
|
||||
return units == UNIT_SECONDS ? (double) source->offset / lovrSoundGetSampleRate(source->sound) : source->offset;
|
||||
}
|
||||
|
||||
void lovrSourceSetTime(Source* source, double time, TimeUnit units) {
|
||||
ma_mutex_lock(&state.lock);
|
||||
source->offset = units == UNIT_SECONDS ? (uint32_t) (time * lovrSoundDataGetSampleRate(source->sound) + .5) : (uint32_t) time;
|
||||
source->offset = units == UNIT_SECONDS ? (uint32_t) (time * lovrSoundGetSampleRate(source->sound) + .5) : (uint32_t) time;
|
||||
ma_mutex_unlock(&state.lock);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#define PLAYBACK_SAMPLE_RATE 48000
|
||||
|
||||
struct SoundData;
|
||||
struct Sound;
|
||||
|
||||
typedef struct Source Source;
|
||||
|
||||
|
@ -34,11 +34,11 @@ void lovrAudioSetVolume(float volume);
|
|||
void lovrAudioGetPose(float position[4], float orientation[4]);
|
||||
void lovrAudioSetPose(float position[4], float orientation[4]);
|
||||
const char* lovrAudioGetSpatializer(void);
|
||||
struct SoundData* lovrAudioGetCaptureStream(void);
|
||||
struct Sound* lovrAudioGetCaptureStream(void);
|
||||
|
||||
// Source
|
||||
|
||||
Source* lovrSourceCreate(struct SoundData* soundData, bool spatial);
|
||||
Source* lovrSourceCreate(struct Sound* sound, bool spatial);
|
||||
void lovrSourceDestroy(void* ref);
|
||||
bool lovrSourcePlay(Source* source);
|
||||
void lovrSourcePause(Source* source);
|
||||
|
|
|
@ -0,0 +1,348 @@
|
|||
#include "data/sound.h"
|
||||
#include "data/blob.h"
|
||||
#include "core/util.h"
|
||||
#include "lib/stb/stb_vorbis.h"
|
||||
#include "lib/miniaudio/miniaudio.h"
|
||||
#define MINIMP3_FLOAT_OUTPUT
|
||||
#define MINIMP3_NO_STDIO
|
||||
#include "lib/minimp3/minimp3_ex.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static const ma_format miniaudioFormats[] = {
|
||||
[SAMPLE_I16] = ma_format_s16,
|
||||
[SAMPLE_F32] = ma_format_f32
|
||||
};
|
||||
|
||||
struct Sound {
|
||||
ref_t ref;
|
||||
uint32_t (*read)(Sound* sound, uint32_t offset, uint32_t count, void* data);
|
||||
struct Blob* blob;
|
||||
void* decoder;
|
||||
void* stream;
|
||||
SampleFormat format;
|
||||
uint32_t sampleRate;
|
||||
uint32_t channels;
|
||||
uint32_t frames;
|
||||
uint32_t cursor;
|
||||
};
|
||||
|
||||
// Readers
|
||||
|
||||
static uint32_t lovrSoundReadRaw(Sound* sound, uint32_t offset, uint32_t count, void* data) {
|
||||
uint8_t* p = sound->blob->data;
|
||||
uint32_t n = MIN(count, sound->frames - offset);
|
||||
size_t stride = lovrSoundGetStride(sound);
|
||||
memcpy(data, p + offset * stride, n * stride);
|
||||
return n;
|
||||
}
|
||||
|
||||
static uint32_t lovrSoundReadStream(Sound* sound, uint32_t offset, uint32_t count, void* data) {
|
||||
void* p = NULL;
|
||||
uint32_t frames = count;
|
||||
ma_pcm_rb_acquire_read(sound->stream, &frames, &p);
|
||||
memcpy(data, p, frames * lovrSoundGetStride(sound));
|
||||
ma_pcm_rb_commit_read(sound->stream, frames, p);
|
||||
return frames;
|
||||
}
|
||||
|
||||
static uint32_t lovrSoundReadOgg(Sound* sound, uint32_t offset, uint32_t count, void* data) {
|
||||
if (sound->cursor != offset) {
|
||||
stb_vorbis_seek(sound->decoder, (int) offset);
|
||||
sound->cursor = offset;
|
||||
}
|
||||
|
||||
uint32_t channels = sound->channels;
|
||||
uint32_t n = stb_vorbis_get_samples_float_interleaved(sound->decoder, channels, data, count * channels);
|
||||
sound->cursor += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
static uint32_t lovrSoundReadMp3(Sound* sound, uint32_t offset, uint32_t count, void* data) {
|
||||
if (sound->cursor != offset) {
|
||||
mp3dec_ex_seek(sound->decoder, offset);
|
||||
sound->cursor = offset;
|
||||
}
|
||||
|
||||
size_t samples = mp3dec_ex_read(sound->decoder, data, count * sound->channels);
|
||||
uint32_t frames = samples / sound->channels;
|
||||
sound->cursor += frames;
|
||||
return frames;
|
||||
}
|
||||
|
||||
// Sound
|
||||
|
||||
Sound* lovrSoundCreateRaw(uint32_t frames, SampleFormat format, uint32_t channels, uint32_t sampleRate, struct Blob* blob) {
|
||||
Sound* sound = calloc(1, sizeof(Sound));
|
||||
lovrAssert(sound, "Out of memory");
|
||||
sound->ref = 1;
|
||||
sound->frames = frames;
|
||||
sound->format = format;
|
||||
sound->channels = channels;
|
||||
sound->sampleRate = sampleRate;
|
||||
sound->read = lovrSoundReadRaw;
|
||||
size_t size = frames * lovrSoundGetStride(sound);
|
||||
void* data = calloc(1, size);
|
||||
lovrAssert(data, "Out of memory");
|
||||
sound->blob = lovrBlobCreate(data, size, "Sound");
|
||||
|
||||
if (blob) {
|
||||
memcpy(sound->blob->data, blob->data, MIN(size, blob->size));
|
||||
}
|
||||
|
||||
return sound;
|
||||
}
|
||||
|
||||
Sound* lovrSoundCreateStream(uint32_t frames, SampleFormat format, uint32_t channels, uint32_t sampleRate) {
|
||||
Sound* sound = calloc(1, sizeof(Sound));
|
||||
lovrAssert(sound, "Out of memory");
|
||||
sound->ref = 1;
|
||||
sound->frames = frames;
|
||||
sound->format = format;
|
||||
sound->channels = channels;
|
||||
sound->sampleRate = sampleRate;
|
||||
sound->read = lovrSoundReadStream;
|
||||
sound->stream = malloc(sizeof(ma_pcm_rb));
|
||||
lovrAssert(sound->stream, "Out of memory");
|
||||
size_t size = frames * lovrSoundGetStride(sound);
|
||||
void* data = malloc(size);
|
||||
lovrAssert(data, "Out of memory");
|
||||
sound->blob = lovrBlobCreate(data, size, NULL);
|
||||
ma_result status = ma_pcm_rb_init(miniaudioFormats[format], channels, frames, data, NULL, sound->stream);
|
||||
lovrAssert(status == MA_SUCCESS, "Failed to create ring buffer for streamed Sound: %s (%d)", ma_result_description(status), status);
|
||||
return sound;
|
||||
}
|
||||
|
||||
Sound* lovrSoundCreateFromFile(struct Blob* blob, bool decode) {
|
||||
Sound* sound = calloc(1, sizeof(Sound));
|
||||
lovrAssert(sound, "Out of memory");
|
||||
sound->ref = 1;
|
||||
|
||||
if (blob->size >= 4 && !memcmp(blob->data, "OggS", 4)) {
|
||||
sound->decoder = stb_vorbis_open_memory(blob->data, (int) blob->size, NULL, NULL);
|
||||
lovrAssert(sound->decoder, "Could not load sound from '%s'", blob->name);
|
||||
|
||||
stb_vorbis_info info = stb_vorbis_get_info(sound->decoder);
|
||||
sound->format = SAMPLE_F32;
|
||||
sound->channels = info.channels;
|
||||
sound->sampleRate = info.sample_rate;
|
||||
sound->frames = stb_vorbis_stream_length_in_samples(sound->decoder);
|
||||
|
||||
if (decode) {
|
||||
sound->read = lovrSoundReadRaw;
|
||||
size_t size = sound->frames * lovrSoundGetStride(sound);
|
||||
void* data = calloc(1, size);
|
||||
lovrAssert(data, "Out of memory");
|
||||
sound->blob = lovrBlobCreate(data, size, "Sound");
|
||||
if (stb_vorbis_get_samples_float_interleaved(sound->decoder, info.channels, data, size / 4) < (int) sound->frames) {
|
||||
lovrThrow("Could not decode vorbis from '%s'", blob->name);
|
||||
}
|
||||
stb_vorbis_close(sound->decoder);
|
||||
sound->decoder = NULL;
|
||||
} else {
|
||||
sound->read = lovrSoundReadOgg;
|
||||
sound->blob = blob;
|
||||
lovrRetain(blob);
|
||||
}
|
||||
|
||||
return sound;
|
||||
} else if (blob->size >= 64 && !memcmp(blob->data, "RIFF", 4)) {
|
||||
typedef struct {
|
||||
uint32_t id;
|
||||
uint32_t size;
|
||||
uint32_t fileFormat;
|
||||
uint32_t fmtId;
|
||||
uint32_t fmtSize;
|
||||
uint16_t format;
|
||||
uint16_t channels;
|
||||
uint32_t sampleRate;
|
||||
uint32_t byteRate;
|
||||
uint16_t frameSize;
|
||||
uint16_t sampleSize;
|
||||
uint16_t extSize;
|
||||
uint16_t validBitsPerSample;
|
||||
uint32_t channelMask;
|
||||
char guid[16];
|
||||
} wavHeader;
|
||||
|
||||
char guidi16[16] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 };
|
||||
char guidf32[16] = { 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 };
|
||||
|
||||
wavHeader* wav = blob->data;
|
||||
lovrAssert(wav->size == blob->size - 8, "Invalid WAV");
|
||||
lovrAssert(!memcmp(&wav->fileFormat, "WAVE", 4), "Invalid WAV");
|
||||
lovrAssert(!memcmp(&wav->fmtId, "fmt ", 4), "Invalid WAV");
|
||||
if (wav->fmtSize == 16 && wav->format == 1 && wav->sampleSize == 16) {
|
||||
sound->format = SAMPLE_I16;
|
||||
} else if (wav->fmtSize == 16 && wav->format == 3 && wav->sampleSize == 32) {
|
||||
sound->format = SAMPLE_F32;
|
||||
} else if (wav->fmtSize == 40 && wav->format == 65534 && wav->extSize == 22 && wav->validBitsPerSample == 16 && !memcmp(wav->guid, guidi16, 16)) {
|
||||
sound->format = SAMPLE_I16;
|
||||
} else if (wav->fmtSize == 40 && wav->format == 65534 && wav->extSize == 22 && wav->validBitsPerSample == 32 && !memcmp(wav->guid, guidf32, 16)) {
|
||||
sound->format = SAMPLE_F32;
|
||||
} else {
|
||||
lovrThrow("Unsupported WAV format");
|
||||
}
|
||||
sound->channels = wav->channels;
|
||||
sound->sampleRate = wav->sampleRate;
|
||||
lovrAssert(wav->frameSize == lovrSoundGetStride(sound), "Invalid WAV");
|
||||
|
||||
size_t offset = 12 + 8 + wav->fmtSize;
|
||||
char* data = (char*) blob->data + offset;
|
||||
while (offset < blob->size - 8) {
|
||||
uint32_t chunkSize = *((uint32_t*) data + 1);
|
||||
if (!memcmp(data, "data", 4)) {
|
||||
offset += 8;
|
||||
data += 8;
|
||||
lovrAssert(chunkSize == blob->size - offset, "Invalid WAV");
|
||||
size_t size = blob->size - offset;
|
||||
void* samples = malloc(size); // TODO Blob views
|
||||
lovrAssert(samples, "Out of memory");
|
||||
memcpy(samples, data, size);
|
||||
sound->blob = lovrBlobCreate(samples, size, blob->name);
|
||||
sound->read = lovrSoundReadRaw;
|
||||
sound->frames = size / wav->frameSize;
|
||||
return sound;
|
||||
} else {
|
||||
offset += chunkSize + 8;
|
||||
data += chunkSize + 8;
|
||||
}
|
||||
}
|
||||
} else if (!mp3dec_detect_buf(blob->data, blob->size)) {
|
||||
if (decode) {
|
||||
mp3dec_t decoder;
|
||||
mp3dec_file_info_t info;
|
||||
int status = mp3dec_load_buf(&decoder, blob->data, blob->size, &info, NULL, NULL);
|
||||
lovrAssert(!status, "Could not decode mp3 from '%s'", blob->name);
|
||||
sound->blob = lovrBlobCreate(info.buffer, info.samples * sizeof(float), blob->name);
|
||||
sound->format = SAMPLE_F32;
|
||||
sound->sampleRate = info.hz;
|
||||
sound->channels = info.channels;
|
||||
sound->frames = info.samples / info.channels;
|
||||
sound->read = lovrSoundReadRaw;
|
||||
return sound;
|
||||
} else {
|
||||
mp3dec_ex_t* decoder = sound->decoder = malloc(sizeof(mp3dec_ex_t));
|
||||
lovrAssert(decoder, "Out of memory");
|
||||
if (mp3dec_ex_open_buf(sound->decoder, blob->data, blob->size, MP3D_SEEK_TO_SAMPLE)) {
|
||||
free(sound->decoder);
|
||||
lovrThrow("Could not load mp3 from '%s'", blob->name);
|
||||
}
|
||||
sound->format = SAMPLE_F32;
|
||||
sound->sampleRate = decoder->info.hz;
|
||||
sound->channels = decoder->info.channels;
|
||||
sound->frames = decoder->samples / sound->channels;
|
||||
sound->read = lovrSoundReadMp3;
|
||||
sound->blob = blob;
|
||||
lovrRetain(blob);
|
||||
return sound;
|
||||
}
|
||||
}
|
||||
|
||||
lovrThrow("Could not load sound from '%s': Audio format not recognized", blob->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void lovrSoundDestroy(void* ref) {
|
||||
Sound* sound = (Sound*) ref;
|
||||
lovrRelease(sound->blob, lovrBlobDestroy);
|
||||
if (sound->read == lovrSoundReadOgg) stb_vorbis_close(sound->decoder);
|
||||
if (sound->read == lovrSoundReadMp3) mp3dec_ex_close(sound->decoder), free(sound->decoder);
|
||||
ma_pcm_rb_uninit(sound->stream);
|
||||
free(sound->stream);
|
||||
free(sound);
|
||||
}
|
||||
|
||||
Blob* lovrSoundGetBlob(Sound* sound) {
|
||||
return sound->blob;
|
||||
}
|
||||
|
||||
SampleFormat lovrSoundGetFormat(Sound* sound) {
|
||||
return sound->format;
|
||||
}
|
||||
|
||||
uint32_t lovrSoundGetChannelCount(Sound* sound) {
|
||||
return sound->channels;
|
||||
}
|
||||
|
||||
uint32_t lovrSoundGetSampleRate(Sound* sound) {
|
||||
return sound->sampleRate;
|
||||
}
|
||||
|
||||
uint32_t lovrSoundGetFrameCount(Sound* sound) {
|
||||
return sound->stream ? ma_pcm_rb_available_read(sound->stream) : sound->frames;
|
||||
}
|
||||
|
||||
size_t lovrSoundGetStride(Sound* sound) {
|
||||
return sound->channels * (sound->format == SAMPLE_I16 ? sizeof(short) : sizeof(float));
|
||||
}
|
||||
|
||||
bool lovrSoundIsCompressed(Sound* sound) {
|
||||
return sound->decoder;
|
||||
}
|
||||
|
||||
bool lovrSoundIsStream(Sound* sound) {
|
||||
return sound->stream;
|
||||
}
|
||||
|
||||
uint32_t lovrSoundRead(Sound* sound, uint32_t offset, uint32_t count, void* data) {
|
||||
return sound->read(sound, offset, count, data);
|
||||
}
|
||||
|
||||
uint32_t lovrSoundWrite(Sound* sound, uint32_t offset, uint32_t count, const void* data) {
|
||||
lovrAssert(!sound->decoder, "Compressed Sound can not be written to");
|
||||
size_t stride = lovrSoundGetStride(sound);
|
||||
uint32_t frames = 0;
|
||||
|
||||
if (sound->stream) {
|
||||
const char* bytes = data;
|
||||
while (frames < count) {
|
||||
void* pointer;
|
||||
uint32_t chunk = count - frames;
|
||||
ma_pcm_rb_acquire_write(sound->stream, &chunk, &pointer);
|
||||
memcpy(pointer, bytes, chunk * stride);
|
||||
ma_pcm_rb_commit_write(sound->stream, chunk, pointer);
|
||||
if (chunk == 0) break;
|
||||
bytes += chunk * stride;
|
||||
frames += chunk;
|
||||
}
|
||||
} else {
|
||||
count = MIN(count, sound->frames - offset);
|
||||
memcpy((char*) sound->blob->data + offset * stride, data, count * stride);
|
||||
frames = count;
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
uint32_t lovrSoundCopy(Sound* src, Sound* dst, uint32_t count, uint32_t srcOffset, uint32_t dstOffset) {
|
||||
lovrAssert(!dst->decoder, "Compressed Sound can not be written to");
|
||||
lovrAssert(src != dst, "Can not copy a Sound to itself");
|
||||
lovrAssert(src->format == dst->format, "Sound formats need to match");
|
||||
lovrAssert(src->channels == dst->channels, "Sound channel layouts need to match");
|
||||
uint32_t frames = 0;
|
||||
|
||||
if (dst->stream) {
|
||||
while (frames < count) {
|
||||
void* data;
|
||||
uint32_t available = count - frames;
|
||||
ma_pcm_rb_acquire_write(dst->stream, &available, &data);
|
||||
uint32_t read = src->read(src, srcOffset + frames, available, data);
|
||||
ma_pcm_rb_commit_write(dst->stream, read, data);
|
||||
if (read == 0) break;
|
||||
frames += read;
|
||||
}
|
||||
} else {
|
||||
count = MIN(count, dst->frames - dstOffset);
|
||||
size_t stride = lovrSoundGetStride(src);
|
||||
char* data = (char*) dst->blob->data + dstOffset * stride;
|
||||
while (frames < count) {
|
||||
uint32_t read = src->read(src, srcOffset + frames, count - frames, data);
|
||||
if (read == 0) break;
|
||||
data += read * stride;
|
||||
frames += read;
|
||||
}
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
struct Blob;
|
||||
|
||||
typedef enum {
|
||||
SAMPLE_F32,
|
||||
SAMPLE_I16
|
||||
} SampleFormat;
|
||||
|
||||
typedef struct Sound Sound;
|
||||
Sound* lovrSoundCreateRaw(uint32_t frames, SampleFormat format, uint32_t channels, uint32_t sampleRate, struct Blob* data);
|
||||
Sound* lovrSoundCreateStream(uint32_t frames, SampleFormat format, uint32_t channels, uint32_t sampleRate);
|
||||
Sound* lovrSoundCreateFromFile(struct Blob* blob, bool decode);
|
||||
void lovrSoundDestroy(void* ref);
|
||||
struct Blob* lovrSoundGetBlob(Sound* sound);
|
||||
SampleFormat lovrSoundGetFormat(Sound* sound);
|
||||
uint32_t lovrSoundGetChannelCount(Sound* sound);
|
||||
uint32_t lovrSoundGetSampleRate(Sound* sound);
|
||||
uint32_t lovrSoundGetFrameCount(Sound* sound);
|
||||
size_t lovrSoundGetStride(Sound* sound);
|
||||
bool lovrSoundIsCompressed(Sound* sound);
|
||||
bool lovrSoundIsStream(Sound* sound);
|
||||
uint32_t lovrSoundRead(Sound* sound, uint32_t offset, uint32_t count, void* data);
|
||||
uint32_t lovrSoundWrite(Sound* sound, uint32_t offset, uint32_t count, const void* data);
|
||||
uint32_t lovrSoundCopy(Sound* src, Sound* dst, uint32_t frames, uint32_t srcOffset, uint32_t dstOffset);
|
|
@ -1,348 +0,0 @@
|
|||
#include "data/soundData.h"
|
||||
#include "data/blob.h"
|
||||
#include "core/util.h"
|
||||
#include "lib/stb/stb_vorbis.h"
|
||||
#include "lib/miniaudio/miniaudio.h"
|
||||
#define MINIMP3_FLOAT_OUTPUT
|
||||
#define MINIMP3_NO_STDIO
|
||||
#include "lib/minimp3/minimp3_ex.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static const ma_format miniaudioFormats[] = {
|
||||
[SAMPLE_I16] = ma_format_s16,
|
||||
[SAMPLE_F32] = ma_format_f32
|
||||
};
|
||||
|
||||
struct SoundData {
|
||||
ref_t ref;
|
||||
uint32_t (*read)(SoundData* soundData, uint32_t offset, uint32_t count, void* data);
|
||||
struct Blob* blob;
|
||||
void* decoder;
|
||||
void* stream;
|
||||
SampleFormat format;
|
||||
uint32_t sampleRate;
|
||||
uint32_t channels;
|
||||
uint32_t frames;
|
||||
uint32_t cursor;
|
||||
};
|
||||
|
||||
// Readers
|
||||
|
||||
static uint32_t lovrSoundDataReadRaw(SoundData* soundData, uint32_t offset, uint32_t count, void* data) {
|
||||
uint8_t* p = soundData->blob->data;
|
||||
uint32_t n = MIN(count, soundData->frames - offset);
|
||||
size_t stride = lovrSoundDataGetStride(soundData);
|
||||
memcpy(data, p + offset * stride, n * stride);
|
||||
return n;
|
||||
}
|
||||
|
||||
static uint32_t lovrSoundDataReadStream(SoundData* soundData, uint32_t offset, uint32_t count, void* data) {
|
||||
void* p = NULL;
|
||||
uint32_t frames = count;
|
||||
ma_pcm_rb_acquire_read(soundData->stream, &frames, &p);
|
||||
memcpy(data, p, frames * lovrSoundDataGetStride(soundData));
|
||||
ma_pcm_rb_commit_read(soundData->stream, frames, p);
|
||||
return frames;
|
||||
}
|
||||
|
||||
static uint32_t lovrSoundDataReadOgg(SoundData* soundData, uint32_t offset, uint32_t count, void* data) {
|
||||
if (soundData->cursor != offset) {
|
||||
stb_vorbis_seek(soundData->decoder, (int) offset);
|
||||
soundData->cursor = offset;
|
||||
}
|
||||
|
||||
uint32_t channels = soundData->channels;
|
||||
uint32_t n = stb_vorbis_get_samples_float_interleaved(soundData->decoder, channels, data, count * channels);
|
||||
soundData->cursor += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
static uint32_t lovrSoundDataReadMp3(SoundData* soundData, uint32_t offset, uint32_t count, void* data) {
|
||||
if (soundData->cursor != offset) {
|
||||
mp3dec_ex_seek(soundData->decoder, offset);
|
||||
soundData->cursor = offset;
|
||||
}
|
||||
|
||||
size_t samples = mp3dec_ex_read(soundData->decoder, data, count * soundData->channels);
|
||||
uint32_t frames = samples / soundData->channels;
|
||||
soundData->cursor += frames;
|
||||
return frames;
|
||||
}
|
||||
|
||||
// SoundData
|
||||
|
||||
SoundData* lovrSoundDataCreateRaw(uint32_t frames, SampleFormat format, uint32_t channels, uint32_t sampleRate, struct Blob* blob) {
|
||||
SoundData* soundData = calloc(1, sizeof(SoundData));
|
||||
lovrAssert(soundData, "Out of memory");
|
||||
soundData->ref = 1;
|
||||
soundData->frames = frames;
|
||||
soundData->format = format;
|
||||
soundData->channels = channels;
|
||||
soundData->sampleRate = sampleRate;
|
||||
soundData->read = lovrSoundDataReadRaw;
|
||||
size_t size = frames * lovrSoundDataGetStride(soundData);
|
||||
void* data = calloc(1, size);
|
||||
lovrAssert(data, "Out of memory");
|
||||
soundData->blob = lovrBlobCreate(data, size, "SoundData");
|
||||
|
||||
if (blob) {
|
||||
memcpy(soundData->blob->data, blob->data, MIN(size, blob->size));
|
||||
}
|
||||
|
||||
return soundData;
|
||||
}
|
||||
|
||||
SoundData* lovrSoundDataCreateStream(uint32_t frames, SampleFormat format, uint32_t channels, uint32_t sampleRate) {
|
||||
SoundData* soundData = calloc(1, sizeof(SoundData));
|
||||
lovrAssert(soundData, "Out of memory");
|
||||
soundData->ref = 1;
|
||||
soundData->frames = frames;
|
||||
soundData->format = format;
|
||||
soundData->channels = channels;
|
||||
soundData->sampleRate = sampleRate;
|
||||
soundData->read = lovrSoundDataReadStream;
|
||||
soundData->stream = malloc(sizeof(ma_pcm_rb));
|
||||
lovrAssert(soundData->stream, "Out of memory");
|
||||
size_t size = frames * lovrSoundDataGetStride(soundData);
|
||||
void* data = malloc(size);
|
||||
lovrAssert(data, "Out of memory");
|
||||
soundData->blob = lovrBlobCreate(data, size, NULL);
|
||||
ma_result status = ma_pcm_rb_init(miniaudioFormats[format], channels, frames, data, NULL, soundData->stream);
|
||||
lovrAssert(status == MA_SUCCESS, "Failed to create ring buffer for streamed SoundData: %s (%d)", ma_result_description(status), status);
|
||||
return soundData;
|
||||
}
|
||||
|
||||
SoundData* lovrSoundDataCreateFromFile(struct Blob* blob, bool decode) {
|
||||
SoundData* soundData = calloc(1, sizeof(SoundData));
|
||||
lovrAssert(soundData, "Out of memory");
|
||||
soundData->ref = 1;
|
||||
|
||||
if (blob->size >= 4 && !memcmp(blob->data, "OggS", 4)) {
|
||||
soundData->decoder = stb_vorbis_open_memory(blob->data, (int) blob->size, NULL, NULL);
|
||||
lovrAssert(soundData->decoder, "Could not load sound from '%s'", blob->name);
|
||||
|
||||
stb_vorbis_info info = stb_vorbis_get_info(soundData->decoder);
|
||||
soundData->format = SAMPLE_F32;
|
||||
soundData->channels = info.channels;
|
||||
soundData->sampleRate = info.sample_rate;
|
||||
soundData->frames = stb_vorbis_stream_length_in_samples(soundData->decoder);
|
||||
|
||||
if (decode) {
|
||||
soundData->read = lovrSoundDataReadRaw;
|
||||
size_t size = soundData->frames * lovrSoundDataGetStride(soundData);
|
||||
void* data = calloc(1, size);
|
||||
lovrAssert(data, "Out of memory");
|
||||
soundData->blob = lovrBlobCreate(data, size, "SoundData");
|
||||
if (stb_vorbis_get_samples_float_interleaved(soundData->decoder, info.channels, data, size / 4) < (int) soundData->frames) {
|
||||
lovrThrow("Could not decode vorbis from '%s'", blob->name);
|
||||
}
|
||||
stb_vorbis_close(soundData->decoder);
|
||||
soundData->decoder = NULL;
|
||||
} else {
|
||||
soundData->read = lovrSoundDataReadOgg;
|
||||
soundData->blob = blob;
|
||||
lovrRetain(blob);
|
||||
}
|
||||
|
||||
return soundData;
|
||||
} else if (blob->size >= 64 && !memcmp(blob->data, "RIFF", 4)) {
|
||||
typedef struct {
|
||||
uint32_t id;
|
||||
uint32_t size;
|
||||
uint32_t fileFormat;
|
||||
uint32_t fmtId;
|
||||
uint32_t fmtSize;
|
||||
uint16_t format;
|
||||
uint16_t channels;
|
||||
uint32_t sampleRate;
|
||||
uint32_t byteRate;
|
||||
uint16_t frameSize;
|
||||
uint16_t sampleSize;
|
||||
uint16_t extSize;
|
||||
uint16_t validBitsPerSample;
|
||||
uint32_t channelMask;
|
||||
char guid[16];
|
||||
} wavHeader;
|
||||
|
||||
char guidi16[16] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 };
|
||||
char guidf32[16] = { 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 };
|
||||
|
||||
wavHeader* wav = blob->data;
|
||||
lovrAssert(wav->size == blob->size - 8, "Invalid WAV");
|
||||
lovrAssert(!memcmp(&wav->fileFormat, "WAVE", 4), "Invalid WAV");
|
||||
lovrAssert(!memcmp(&wav->fmtId, "fmt ", 4), "Invalid WAV");
|
||||
if (wav->fmtSize == 16 && wav->format == 1 && wav->sampleSize == 16) {
|
||||
soundData->format = SAMPLE_I16;
|
||||
} else if (wav->fmtSize == 16 && wav->format == 3 && wav->sampleSize == 32) {
|
||||
soundData->format = SAMPLE_F32;
|
||||
} else if (wav->fmtSize == 40 && wav->format == 65534 && wav->extSize == 22 && wav->validBitsPerSample == 16 && !memcmp(wav->guid, guidi16, 16)) {
|
||||
soundData->format = SAMPLE_I16;
|
||||
} else if (wav->fmtSize == 40 && wav->format == 65534 && wav->extSize == 22 && wav->validBitsPerSample == 32 && !memcmp(wav->guid, guidf32, 16)) {
|
||||
soundData->format = SAMPLE_F32;
|
||||
} else {
|
||||
lovrThrow("Unsupported WAV format");
|
||||
}
|
||||
soundData->channels = wav->channels;
|
||||
soundData->sampleRate = wav->sampleRate;
|
||||
lovrAssert(wav->frameSize == lovrSoundDataGetStride(soundData), "Invalid WAV");
|
||||
|
||||
size_t offset = 12 + 8 + wav->fmtSize;
|
||||
char* data = (char*) blob->data + offset;
|
||||
while (offset < blob->size - 8) {
|
||||
uint32_t chunkSize = *((uint32_t*) data + 1);
|
||||
if (!memcmp(data, "data", 4)) {
|
||||
offset += 8;
|
||||
data += 8;
|
||||
lovrAssert(chunkSize == blob->size - offset, "Invalid WAV");
|
||||
size_t size = blob->size - offset;
|
||||
void* samples = malloc(size); // TODO Blob views
|
||||
lovrAssert(samples, "Out of memory");
|
||||
memcpy(samples, data, size);
|
||||
soundData->blob = lovrBlobCreate(samples, size, blob->name);
|
||||
soundData->read = lovrSoundDataReadRaw;
|
||||
soundData->frames = size / wav->frameSize;
|
||||
return soundData;
|
||||
} else {
|
||||
offset += chunkSize + 8;
|
||||
data += chunkSize + 8;
|
||||
}
|
||||
}
|
||||
} else if (!mp3dec_detect_buf(blob->data, blob->size)) {
|
||||
if (decode) {
|
||||
mp3dec_t decoder;
|
||||
mp3dec_file_info_t info;
|
||||
int status = mp3dec_load_buf(&decoder, blob->data, blob->size, &info, NULL, NULL);
|
||||
lovrAssert(!status, "Could not decode mp3 from '%s'", blob->name);
|
||||
soundData->blob = lovrBlobCreate(info.buffer, info.samples * sizeof(float), blob->name);
|
||||
soundData->format = SAMPLE_F32;
|
||||
soundData->sampleRate = info.hz;
|
||||
soundData->channels = info.channels;
|
||||
soundData->frames = info.samples / info.channels;
|
||||
soundData->read = lovrSoundDataReadRaw;
|
||||
return soundData;
|
||||
} else {
|
||||
mp3dec_ex_t* decoder = soundData->decoder = malloc(sizeof(mp3dec_ex_t));
|
||||
lovrAssert(decoder, "Out of memory");
|
||||
if (mp3dec_ex_open_buf(soundData->decoder, blob->data, blob->size, MP3D_SEEK_TO_SAMPLE)) {
|
||||
free(soundData->decoder);
|
||||
lovrThrow("Could not load mp3 from '%s'", blob->name);
|
||||
}
|
||||
soundData->format = SAMPLE_F32;
|
||||
soundData->sampleRate = decoder->info.hz;
|
||||
soundData->channels = decoder->info.channels;
|
||||
soundData->frames = decoder->samples / soundData->channels;
|
||||
soundData->read = lovrSoundDataReadMp3;
|
||||
soundData->blob = blob;
|
||||
lovrRetain(blob);
|
||||
return soundData;
|
||||
}
|
||||
}
|
||||
|
||||
lovrThrow("Could not load sound from '%s': Audio format not recognized", blob->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void lovrSoundDataDestroy(void* ref) {
|
||||
SoundData* soundData = (SoundData*) ref;
|
||||
lovrRelease(soundData->blob, lovrBlobDestroy);
|
||||
if (soundData->read == lovrSoundDataReadOgg) stb_vorbis_close(soundData->decoder);
|
||||
if (soundData->read == lovrSoundDataReadMp3) mp3dec_ex_close(soundData->decoder), free(soundData->decoder);
|
||||
ma_pcm_rb_uninit(soundData->stream);
|
||||
free(soundData->stream);
|
||||
free(soundData);
|
||||
}
|
||||
|
||||
Blob* lovrSoundDataGetBlob(SoundData* soundData) {
|
||||
return soundData->blob;
|
||||
}
|
||||
|
||||
SampleFormat lovrSoundDataGetFormat(SoundData* soundData) {
|
||||
return soundData->format;
|
||||
}
|
||||
|
||||
uint32_t lovrSoundDataGetChannelCount(SoundData* soundData) {
|
||||
return soundData->channels;
|
||||
}
|
||||
|
||||
uint32_t lovrSoundDataGetSampleRate(SoundData* soundData) {
|
||||
return soundData->sampleRate;
|
||||
}
|
||||
|
||||
uint32_t lovrSoundDataGetFrameCount(SoundData* soundData) {
|
||||
return soundData->stream ? ma_pcm_rb_available_read(soundData->stream) : soundData->frames;
|
||||
}
|
||||
|
||||
size_t lovrSoundDataGetStride(SoundData* soundData) {
|
||||
return soundData->channels * (soundData->format == SAMPLE_I16 ? sizeof(short) : sizeof(float));
|
||||
}
|
||||
|
||||
bool lovrSoundDataIsCompressed(SoundData* soundData) {
|
||||
return soundData->decoder;
|
||||
}
|
||||
|
||||
bool lovrSoundDataIsStream(SoundData* soundData) {
|
||||
return soundData->stream;
|
||||
}
|
||||
|
||||
uint32_t lovrSoundDataRead(SoundData* soundData, uint32_t offset, uint32_t count, void* data) {
|
||||
return soundData->read(soundData, offset, count, data);
|
||||
}
|
||||
|
||||
uint32_t lovrSoundDataWrite(SoundData* soundData, uint32_t offset, uint32_t count, const void* data) {
|
||||
lovrAssert(!soundData->decoder, "Compressed SoundData can not be written to");
|
||||
size_t stride = lovrSoundDataGetStride(soundData);
|
||||
uint32_t frames = 0;
|
||||
|
||||
if (soundData->stream) {
|
||||
const char* bytes = data;
|
||||
while (frames < count) {
|
||||
void* pointer;
|
||||
uint32_t chunk = count - frames;
|
||||
ma_pcm_rb_acquire_write(soundData->stream, &chunk, &pointer);
|
||||
memcpy(pointer, bytes, chunk * stride);
|
||||
ma_pcm_rb_commit_write(soundData->stream, chunk, pointer);
|
||||
if (chunk == 0) break;
|
||||
bytes += chunk * stride;
|
||||
frames += chunk;
|
||||
}
|
||||
} else {
|
||||
count = MIN(count, soundData->frames - offset);
|
||||
memcpy((char*) soundData->blob->data + offset * stride, data, count * stride);
|
||||
frames = count;
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
uint32_t lovrSoundDataCopy(SoundData* src, SoundData* dst, uint32_t count, uint32_t srcOffset, uint32_t dstOffset) {
|
||||
lovrAssert(!dst->decoder, "Compressed SoundData can not be written to");
|
||||
lovrAssert(src != dst, "Can not copy a SoundData to itself");
|
||||
lovrAssert(src->format == dst->format, "SoundData formats need to match");
|
||||
lovrAssert(src->channels == dst->channels, "SoundData channel layouts need to match");
|
||||
uint32_t frames = 0;
|
||||
|
||||
if (dst->stream) {
|
||||
while (frames < count) {
|
||||
void* data;
|
||||
uint32_t available = count - frames;
|
||||
ma_pcm_rb_acquire_write(dst->stream, &available, &data);
|
||||
uint32_t read = src->read(src, srcOffset + frames, available, data);
|
||||
ma_pcm_rb_commit_write(dst->stream, read, data);
|
||||
if (read == 0) break;
|
||||
frames += read;
|
||||
}
|
||||
} else {
|
||||
count = MIN(count, dst->frames - dstOffset);
|
||||
size_t stride = lovrSoundDataGetStride(src);
|
||||
char* data = (char*) dst->blob->data + dstOffset * stride;
|
||||
while (frames < count) {
|
||||
uint32_t read = src->read(src, srcOffset + frames, count - frames, data);
|
||||
if (read == 0) break;
|
||||
data += read * stride;
|
||||
frames += read;
|
||||
}
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
struct Blob;
|
||||
|
||||
typedef enum {
|
||||
SAMPLE_F32,
|
||||
SAMPLE_I16
|
||||
} SampleFormat;
|
||||
|
||||
typedef struct SoundData SoundData;
|
||||
SoundData* lovrSoundDataCreateRaw(uint32_t frames, SampleFormat format, uint32_t channels, uint32_t sampleRate, struct Blob* data);
|
||||
SoundData* lovrSoundDataCreateStream(uint32_t frames, SampleFormat format, uint32_t channels, uint32_t sampleRate);
|
||||
SoundData* lovrSoundDataCreateFromFile(struct Blob* blob, bool decode);
|
||||
void lovrSoundDataDestroy(void* ref);
|
||||
struct Blob* lovrSoundDataGetBlob(SoundData* soundData);
|
||||
SampleFormat lovrSoundDataGetFormat(SoundData* soundData);
|
||||
uint32_t lovrSoundDataGetChannelCount(SoundData* soundData);
|
||||
uint32_t lovrSoundDataGetSampleRate(SoundData* soundData);
|
||||
uint32_t lovrSoundDataGetFrameCount(SoundData* soundData);
|
||||
size_t lovrSoundDataGetStride(SoundData* soundData);
|
||||
bool lovrSoundDataIsCompressed(SoundData* soundData);
|
||||
bool lovrSoundDataIsStream(SoundData* soundData);
|
||||
uint32_t lovrSoundDataRead(SoundData* soundData, uint32_t offset, uint32_t count, void* data);
|
||||
uint32_t lovrSoundDataWrite(SoundData* soundData, uint32_t offset, uint32_t count, const void* data);
|
||||
uint32_t lovrSoundDataCopy(SoundData* src, SoundData* dst, uint32_t frames, uint32_t srcOffset, uint32_t dstOffset);
|
Loading…
Reference in New Issue