Merge pull request #7 from bjornbytes/sound

Sound
This commit is contained in:
Bjorn Swenson 2017-01-06 19:17:39 -08:00 committed by GitHub
commit 7cce1e30a7
19 changed files with 6394 additions and 7 deletions

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ src/obj
src/Tupfile
*.lua
*.glsl
.DS_Store

View File

@ -79,6 +79,17 @@ else()
set(LOVR_ASSIMP ${ASSIMP_LIBRARIES})
endif()
# OpenAL
if (WIN32)
add_subdirectory(deps/openal-soft openal)
include_directories(deps/openal-soft/include)
set(LOVR_OPENAL OpenAL32)
else()
pkg_search_module(OPENAL REQUIRED openal)
include_directories(${OPENAL_INCLUDE_DIRS})
set(LOVR_OPENAL ${OPENAL_LIBRARIES})
endif()
# openvr
if(WIN32)
include_directories(deps/openvr/headers)
@ -109,6 +120,7 @@ set(LOVR_LIB
${LOVR_GLFW}
${LOVR_PHYSFS}
${LOVR_ASSIMP}
${LOVR_OPENAL}
${LOVR_OPENVR}
)
@ -120,4 +132,5 @@ if(WIN32)
move_dll(${LOVR_GLFW})
move_dll(${LOVR_PHYSFS})
move_dll(${LOVR_ASSIMP})
move_dll(${LOVR_OPENAL})
endif()

196
src/audio/audio.c Normal file
View File

@ -0,0 +1,196 @@
#include "audio/audio.h"
#include "loaders/source.h"
#include "util.h"
#include <stdlib.h>
#include <math.h>
static AudioState state;
static LPALCRESETDEVICESOFT alcResetDeviceSOFT;
static void cross(float ux, float uy, float uz, float vx, float vy, float vz, float* x, float* y, float* z) {
*x = uy * vz - uz * vy;
*y = ux * vz - uz * vx;
*z = ux * vy - uy * vx;
}
void lovrAudioInit() {
ALCdevice* device = alcOpenDevice(NULL);
if (!device) {
error("Unable to open default audio device");
}
ALCcontext* context = alcCreateContext(device, NULL);
if (!context || !alcMakeContextCurrent(context) || alcGetError(device) != ALC_NO_ERROR) {
error("Unable to create OpenAL context");
}
alcResetDeviceSOFT = (LPALCRESETDEVICESOFT) alcGetProcAddress(device, "alcResetDeviceSOFT");
if (alcIsExtensionPresent(device, "ALC_SOFT_HRTF")) {
ALCint attrs[3] = { ALC_HRTF_SOFT, ALC_TRUE, 0 };
alcResetDeviceSOFT(device, attrs);
}
state.device = device;
state.context = context;
vec_init(&state.sources);
}
void lovrAudioDestroy() {
alcMakeContextCurrent(NULL);
alcDestroyContext(state.context);
alcCloseDevice(state.device);
vec_deinit(&state.sources);
}
void lovrAudioUpdate() {
int i; Source* source;
vec_foreach_rev(&state.sources, source, i) {
int isStopped = lovrSourceIsStopped(source);
ALint processed;
alGetSourcei(source->id, AL_BUFFERS_PROCESSED, &processed);
if (processed) {
ALuint buffers[SOURCE_BUFFERS];
alSourceUnqueueBuffers(source->id, processed, buffers);
lovrSourceStream(source, buffers, processed);
if (isStopped) {
alSourcePlay(source->id);
}
} else if (isStopped) {
vec_splice(&state.sources, i, 1);
lovrRelease(&source->ref);
}
}
}
void lovrAudioAdd(Source* source) {
if (!lovrAudioHas(source)) {
lovrRetain(&source->ref);
vec_push(&state.sources, source);
}
}
void lovrAudioGetOrientation(float* angle, float* ax, float* ay, float* az) {
float v[6];
alGetListenerfv(AL_ORIENTATION, v);
float cx, cy, cz;
cross(v[0], v[1], v[2], v[3], v[4], v[5], &cx, &cy, &cz);
float w = 1 + v[0] * v[3] + v[1] * v[4] + v[2] * v[5];
float len = sqrt(cx * cx + cy * cy + cz * cz + w * w);
if (len != 1) {
cx /= len;
cy /= len;
cz /= len;
w /= len;
}
*angle = 2 * acos(w);
float s = sqrt(1 - w * w);
if (s < .001) {
*ax = cx;
*ay = cy;
*az = cz;
} else {
*ax = cx / s;
*ay = cy / s;
*az = cz / s;
}
}
void lovrAudioGetPosition(float* x, float* y, float* z) {
alGetListener3f(AL_POSITION, x, y, z);
}
float lovrAudioGetVolume() {
float volume;
alGetListenerf(AL_GAIN, &volume);
return volume;
}
int lovrAudioHas(Source* source) {
int index;
vec_find(&state.sources, source, index);
return index >= 0;
}
void lovrAudioPause() {
int i; Source* source;
vec_foreach(&state.sources, source, i) {
lovrSourcePause(source);
}
}
void lovrAudioResume() {
int i; Source* source;
vec_foreach(&state.sources, source, i) {
lovrSourceResume(source);
}
}
void lovrAudioRewind() {
int i; Source* source;
vec_foreach(&state.sources, source, i) {
lovrSourceRewind(source);
}
}
// Help
void lovrAudioSetOrientation(float angle, float ax, float ay, float az) {
// Quaternion
float cos2 = cos(angle / 2.f);
float sin2 = sin(angle / 2.f);
float qx = sin2 * ax;
float qy = sin2 * ay;
float qz = sin2 * az;
float s = cos2;
float vx, vy, vz, qdotv, qdotq, a, b, c, cx, cy, cz;
// Forward
vx = 0;
vy = 0;
vz = -1;
qdotv = qx * vx + qy * vy + qz * vz;
qdotq = qx * qx + qy * qy + qz * qz;
a = 2 * qdotv;
b = s * s - qdotq;
c = 2 * s;
cross(qx, qy, qz, vx, vy, vz, &cx, &cy, &cz);
float fx = a * qx + b * vx + c * cx;
float fy = a * qy + b * vy + c * cy;
float fz = a * qz + b * vz + c * cz;
// Up
vx = 0;
vy = 1;
vz = 0;
qdotv = qx * vx + qy * vy + qz * vz;
qdotq = qx * qx + qy * qy + qz * qz;
a = 2 * qdotv;
b = s * s - qdotq;
c = 2 * s;
cross(qx, qy, qz, vx, vy, vz, &cx, &cy, &cz);
float ux = a * qx + b * vx + c * cx;
float uy = a * qy + b * vy + c * cy;
float uz = a * qz + b * vz + c * cz;
ALfloat orientation[6] = { fx, fy, fz, ux, uy, uz };
alListenerfv(AL_ORIENTATION, orientation);
}
void lovrAudioSetPosition(float x, float y, float z) {
alListener3f(AL_POSITION, x, y, z);
}
void lovrAudioSetVolume(float volume) {
alListenerf(AL_GAIN, volume);
}
void lovrAudioStop() {
int i; Source* source;
vec_foreach(&state.sources, source, i) {
lovrSourceStop(source);
}
}

32
src/audio/audio.h Normal file
View File

@ -0,0 +1,32 @@
#include "audio/source.h"
#include "vendor/vec/vec.h"
#include <AL/al.h>
#include <AL/alc.h>
#include <AL/alext.h>
#ifndef LOVR_AUDIO_TYPES
#define LOVR_AUDIO_TYPES
typedef struct {
ALCdevice* device;
ALCcontext* context;
vec_void_t sources;
} AudioState;
#endif
void lovrAudioInit();
void lovrAudioDestroy();
void lovrAudioUpdate();
void lovrAudioAdd(Source* source);
void lovrAudioGetOrientation(float* angle, float* ax, float* ay, float* az);
void lovrAudioGetPosition(float* x, float* y, float* z);
float lovrAudioGetVolume();
int lovrAudioHas(Source* source);
void lovrAudioPause();
void lovrAudioResume();
void lovrAudioRewind();
void lovrAudioSetOrientation(float angle, float ax, float ay, float az);
void lovrAudioSetPosition(float x, float y, float z);
void lovrAudioSetVolume(float volume);
void lovrAudioStop();

222
src/audio/source.c Normal file
View File

@ -0,0 +1,222 @@
#include "audio/source.h"
#include "loaders/source.h"
static ALenum lovrSourceGetState(Source* source) {
ALenum state;
alGetSourcei(source->id, AL_SOURCE_STATE, &state);
return state;
}
Source* lovrSourceCreate(SourceData* sourceData) {
Source* source = lovrAlloc(sizeof(Source), lovrSourceDestroy);
if (!source) return NULL;
source->sourceData = sourceData;
source->isLooping = 0;
alGenSources(1, &source->id);
alGenBuffers(SOURCE_BUFFERS, source->buffers);
return source;
}
void lovrSourceDestroy(const Ref* ref) {
Source* source = containerof(ref, Source);
alDeleteSources(1, &source->id);
alDeleteBuffers(SOURCE_BUFFERS, source->buffers);
lovrSourceDataDestroy(source->sourceData);
free(source);
}
int lovrSourceGetBitDepth(Source* source) {
return source->sourceData->bitDepth;
}
int lovrSourceGetChannels(Source* source) {
return source->sourceData->channels;
}
int lovrSourceGetDuration(Source* source) {
return source->sourceData->samples;
}
// Get the OpenAL sound format for the sound
ALenum lovrSourceGetFormat(Source* source) {
int channels = source->sourceData->channels;
int bitDepth = source->sourceData->bitDepth;
if (bitDepth == 8 && channels == 1) {
return AL_FORMAT_MONO8;
} else if (bitDepth == 8 && channels == 2) {
return AL_FORMAT_STEREO8;
} else if (bitDepth == 16 && channels == 1) {
return AL_FORMAT_MONO16;
} else if (bitDepth == 16 && channels == 2) {
return AL_FORMAT_STEREO16;
}
return 0;
}
void lovrSourceGetOrientation(Source* source, float* x, float* y, float* z) {
alGetSource3f(source->id, AL_DIRECTION, x, y, z);
}
float lovrSourceGetPitch(Source* source) {
float pitch;
alGetSourcef(source->id, AL_PITCH, &pitch);
return pitch;
}
void lovrSourceGetPosition(Source* source, float* x, float* y, float* z) {
alGetSource3f(source->id, AL_POSITION, x, y, z);
}
int lovrSourceGetSampleRate(Source* source) {
return source->sourceData->sampleRate;
}
float lovrSourceGetVolume(Source* source) {
float volume;
alGetSourcef(source->id, AL_GAIN, &volume);
return volume;
}
int lovrSourceIsLooping(Source* source) {
return source->isLooping;
}
int lovrSourceIsPaused(Source* source) {
return lovrSourceGetState(source) == AL_PAUSED;
}
int lovrSourceIsPlaying(Source* source) {
return lovrSourceGetState(source) == AL_PLAYING;
}
int lovrSourceIsStopped(Source* source) {
return lovrSourceGetState(source) == AL_STOPPED;
}
void lovrSourcePause(Source* source) {
alSourcePause(source->id);
}
void lovrSourcePlay(Source* source) {
if (lovrSourceIsPlaying(source)) {
return;
} else if (lovrSourceIsPaused(source)) {
lovrSourceResume(source);
return;
}
lovrSourceStream(source, source->buffers, SOURCE_BUFFERS);
alSourcePlay(source->id);
}
void lovrSourceResume(Source* source) {
if (!lovrSourceIsPaused(source)) {
return;
}
alSourcePlay(source->id);
}
void lovrSourceRewind(Source* source) {
if (lovrSourceIsStopped(source)) {
return;
}
int wasPaused = lovrSourceIsPaused(source);
alSourceRewind(source->id);
lovrSourceStop(source);
lovrSourcePlay(source);
if (wasPaused) {
lovrSourcePause(source);
}
}
void lovrSourceSeek(Source* source, int sample) {
int wasPaused = lovrSourceIsPaused(source);
lovrSourceStop(source);
lovrSourceDataSeek(source->sourceData, sample);
lovrSourcePlay(source);
if (wasPaused) {
lovrSourcePause(source);
}
}
void lovrSourceSetLooping(Source* source, int isLooping) {
source->isLooping = isLooping;
}
void lovrSourceSetOrientation(Source* source, float dx, float dy, float dz) {
alSource3f(source->id, AL_DIRECTION, dx, dy, dz);
}
void lovrSourceSetPitch(Source* source, float pitch) {
alSourcef(source->id, AL_PITCH, pitch);
}
void lovrSourceSetPosition(Source* source, float x, float y, float z) {
alSource3f(source->id, AL_POSITION, x, y, z);
}
void lovrSourceSetVolume(Source* source, float volume) {
alSourcef(source->id, AL_GAIN, volume);
}
void lovrSourceStop(Source* source) {
if (lovrSourceIsStopped(source)) {
return;
}
// Empty the buffers
int count = 0;
alGetSourcei(source->id, AL_BUFFERS_QUEUED, &count);
alSourceUnqueueBuffers(source->id, count, NULL);
// Stop the source
alSourceStop(source->id);
alSourcei(source->id, AL_BUFFER, AL_NONE);
// Rewind the decoder
lovrSourceDataRewind(source->sourceData);
}
// Fills buffers with data and queues them, called once initially and over time to stream more data
void lovrSourceStream(Source* source, ALuint* buffers, int count) {
SourceData* sourceData = source->sourceData;
ALenum format = lovrSourceGetFormat(source);
int frequency = sourceData->sampleRate;
int samples = 0;
int n = 0;
// Keep decoding until there is nothing left to decode or all the buffers are filled
while (n < count && (samples = lovrSourceDataDecode(sourceData)) != 0) {
alBufferData(buffers[n++], format, sourceData->buffer, samples * sizeof(ALshort), frequency);
}
alSourceQueueBuffers(source->id, n, buffers);
if (samples == 0 && source->isLooping && n < count) {
lovrSourceDataRewind(sourceData);
lovrSourceStream(source, buffers + n, count - n);
return;
}
}
int lovrSourceTell(Source* source) {
int decoderOffset = lovrSourceDataTell(source->sourceData);
int samplesPerBuffer = source->sourceData->bufferSize / source->sourceData->channels / sizeof(ALshort);
int queuedBuffers, sampleOffset;
alGetSourcei(source->id, AL_BUFFERS_QUEUED, &queuedBuffers);
alGetSourcei(source->id, AL_SAMPLE_OFFSET, &sampleOffset);
int offset = decoderOffset - queuedBuffers * samplesPerBuffer + sampleOffset;
if (offset < 0) {
return offset + source->sourceData->samples;
} else {
return offset;
}
}

63
src/audio/source.h Normal file
View File

@ -0,0 +1,63 @@
#include "util.h"
#include <AL/al.h>
#include <AL/alc.h>
#ifndef LOVR_SOURCE_TYPES
#define LOVR_SOURCE_TYPES
#define SOURCE_BUFFERS 4
typedef enum {
UNIT_SECONDS,
UNIT_SAMPLES
} TimeUnit;
typedef struct {
int bitDepth;
int channels;
int sampleRate;
int samples;
int bufferSize;
void* buffer;
void* decoder;
void* data;
} SourceData;
typedef struct {
Ref ref;
SourceData* sourceData;
ALuint id;
ALuint buffers[SOURCE_BUFFERS];
int isLooping;
} Source;
#endif
Source* lovrSourceCreate(SourceData* sourceData);
void lovrSourceDestroy(const Ref* ref);
int lovrSourceGetBitDepth(Source* source);
int lovrSourceGetChannels(Source* source);
int lovrSourceGetDuration(Source* source);
ALenum lovrSourceGetFormat(Source* source);
void lovrSourceGetOrientation(Source* source, float* x, float* y, float* z);
float lovrSourceGetPitch(Source* source);
void lovrSourceGetPosition(Source* source, float* x, float* y, float* z);
int lovrSourceGetSampleRate(Source* source);
float lovrSourceGetVolume(Source* source);
int lovrSourceIsLooping(Source* source);
int lovrSourceIsPaused(Source* source);
int lovrSourceIsPlaying(Source* source);
int lovrSourceIsStopped(Source* source);
void lovrSourcePause(Source* source);
void lovrSourcePlay(Source* source);
void lovrSourceResume(Source* source);
void lovrSourceRewind(Source* source);
void lovrSourceSeek(Source* source, int sample);
void lovrSourceSetLooping(Source* source, int isLooping);
void lovrSourceSetPitch(Source* source, float pitch);
void lovrSourceSetOrientation(Source* source, float dx, float dy, float dz);
void lovrSourceSetPosition(Source* source, float x, float y, float z);
void lovrSourceSetVolume(Source* source, float volume);
void lovrSourceStop(Source* source);
void lovrSourceStream(Source* source, ALuint* buffers, int count);
int lovrSourceTell(Source* source);

66
src/loaders/source.c Normal file
View File

@ -0,0 +1,66 @@
#include "loaders/source.h"
#include "vendor/stb/stb_vorbis.h"
#include <stdlib.h>
SourceData* lovrSourceDataFromFile(void* data, int size) {
SourceData* sourceData = malloc(sizeof(SourceData));
if (!sourceData) return NULL;
stb_vorbis* decoder = stb_vorbis_open_memory(data, size, NULL, NULL);
if (!decoder) {
free(sourceData);
return NULL;
}
stb_vorbis_info info = stb_vorbis_get_info(decoder);
sourceData->bitDepth = 16;
sourceData->channels = info.channels;
sourceData->sampleRate = info.sample_rate;
sourceData->samples = stb_vorbis_stream_length_in_samples(decoder);
sourceData->decoder = decoder;
sourceData->bufferSize = sourceData->channels * 4096 * sizeof(short);
sourceData->buffer = malloc(sourceData->bufferSize);
sourceData->data = data;
return sourceData;
}
void lovrSourceDataDestroy(SourceData* sourceData) {
stb_vorbis_close(sourceData->decoder);
free(sourceData->data);
free(sourceData->buffer);
free(sourceData);
}
int lovrSourceDataDecode(SourceData* sourceData) {
stb_vorbis* decoder = (stb_vorbis*) sourceData->decoder;
short* buffer = (short*) sourceData->buffer;
int channels = sourceData->channels;
int capacity = sourceData->bufferSize / sizeof(short);
int samples = 0;
while (samples < capacity) {
int count = stb_vorbis_get_samples_short_interleaved(decoder, channels, buffer + samples, capacity - samples);
if (count == 0) break;
samples += count * channels;
}
return samples;
}
void lovrSourceDataRewind(SourceData* sourceData) {
stb_vorbis* decoder = (stb_vorbis*) sourceData->decoder;
stb_vorbis_seek_start(decoder);
}
void lovrSourceDataSeek(SourceData* sourceData, int sample) {
stb_vorbis* decoder = (stb_vorbis*) sourceData->decoder;
stb_vorbis_seek(decoder, sample);
}
int lovrSourceDataTell(SourceData* sourceData) {
stb_vorbis* decoder = (stb_vorbis*) sourceData->decoder;
return stb_vorbis_get_sample_offset(decoder);
}

8
src/loaders/source.h Normal file
View File

@ -0,0 +1,8 @@
#include "audio/source.h"
SourceData* lovrSourceDataFromFile(void* data, int size);
void lovrSourceDataDestroy(SourceData* sourceData);
int lovrSourceDataDecode(SourceData* sourceData);
void lovrSourceDataRewind(SourceData* sourceData);
void lovrSourceDataSeek(SourceData* sourceData, int sample);
int lovrSourceDataTell(SourceData* sourceData);

View File

@ -1,4 +1,5 @@
#include "lovr.h"
#include "lovr/audio.h"
#include "lovr/event.h"
#include "lovr/filesystem.h"
#include "lovr/graphics.h"
@ -56,6 +57,7 @@ void lovrInit(lua_State* L, int argc, char** argv) {
lua_setglobal(L, "lovr");
// Preload modules
luax_preloadmodule(L, "lovr.audio", l_lovrAudioInit);
luax_preloadmodule(L, "lovr.event", l_lovrEventInit);
luax_preloadmodule(L, "lovr.filesystem", l_lovrFilesystemInit);
luax_preloadmodule(L, "lovr.graphics", l_lovrGraphicsInit);
@ -63,10 +65,11 @@ void lovrInit(lua_State* L, int argc, char** argv) {
luax_preloadmodule(L, "lovr.timer", l_lovrTimerInit);
// Bootstrap
char buffer[2048];
char buffer[4096];
snprintf(buffer, sizeof(buffer), "%s",
"local conf = { "
" modules = { "
" audio = true, "
" event = true, "
" graphics = true, "
" headset = true, "
@ -85,7 +88,7 @@ void lovrInit(lua_State* L, int argc, char** argv) {
" success, err = pcall(lovr.conf, conf) "
"end "
"local modules = { 'event', 'graphics', 'headset', 'timer' } "
"local modules = { 'audio', 'event', 'graphics', 'headset', 'timer' } "
"for _, module in ipairs(modules) do "
" if conf.modules[module] then "
" lovr[module] = require('lovr.' .. module) "
@ -119,6 +122,13 @@ void lovrInit(lua_State* L, int argc, char** argv) {
" lovr.handlers[name](a, b, c, d) "
" end "
" local dt = lovr.timer.step() "
" if lovr.audio then "
" lovr.audio.update() "
" if lovr.headset and lovr.headset.isPresent() then "
" lovr.audio.setPosition(lovr.headset.getPosition()) "
" lovr.audio.setOrientation(lovr.headset.getOrientation()) "
" end "
" end "
" if lovr.update then lovr.update(dt) end "
" lovr.graphics.clear() "
" lovr.graphics.origin() "

124
src/lovr/audio.c Normal file
View File

@ -0,0 +1,124 @@
#include "lovr/audio.h"
#include "lovr/types/source.h"
#include "audio/audio.h"
#include "audio/source.h"
#include "loaders/source.h"
#include "filesystem/filesystem.h"
const luaL_Reg lovrAudio[] = {
{ "update", l_lovrAudioUpdate },
{ "getOrientation", l_lovrAudioGetOrientation },
{ "getPosition", l_lovrAudioGetPosition },
{ "getVolume", l_lovrAudioGetVolume },
{ "newSource", l_lovrAudioNewSource },
{ "pause", l_lovrAudioPause },
{ "resume", l_lovrAudioResume },
{ "rewind", l_lovrAudioRewind },
{ "setOrientation", l_lovrAudioSetOrientation },
{ "setPosition", l_lovrAudioSetPosition },
{ "setVolume", l_lovrAudioSetVolume },
{ "stop", l_lovrAudioStop },
{ NULL, NULL }
};
int l_lovrAudioInit(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrAudio);
luax_registertype(L, "Source", lovrSource);
map_init(&TimeUnits);
map_set(&TimeUnits, "seconds", UNIT_SECONDS);
map_set(&TimeUnits, "samples", UNIT_SAMPLES);
lovrAudioInit();
return 1;
}
int l_lovrAudioUpdate(lua_State* L) {
lovrAudioUpdate();
return 0;
}
int l_lovrAudioGetOrientation(lua_State* L) {
float angle, ax, ay, az;
lovrAudioGetOrientation(&angle, &ax, &ay, &az);
lua_pushnumber(L, angle);
lua_pushnumber(L, ax);
lua_pushnumber(L, ay);
lua_pushnumber(L, az);
return 4;
}
int l_lovrAudioGetPosition(lua_State* L) {
float x, y, z;
lovrAudioGetPosition(&x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
return 3;
}
int l_lovrAudioGetVolume(lua_State* L) {
lua_pushnumber(L, lovrAudioGetVolume());
return 1;
}
int l_lovrAudioNewSource(lua_State* L) {
const char* filename = luaL_checkstring(L, 1);
if (!strstr(filename, ".ogg")) {
return luaL_error(L, "Only .ogg files are supported");
}
int size;
void* data = lovrFilesystemRead(filename, &size);
if (!data) {
return luaL_error(L, "Could not load source from file '%s'", filename);
}
SourceData* sourceData = lovrSourceDataFromFile(data, size);
luax_pushtype(L, Source, lovrSourceCreate(sourceData));
return 1;
}
int l_lovrAudioPause(lua_State* L) {
lovrAudioPause();
return 0;
}
int l_lovrAudioResume(lua_State* L) {
lovrAudioResume();
return 0;
}
int l_lovrAudioRewind(lua_State* L) {
lovrAudioRewind();
return 0;
}
int l_lovrAudioSetOrientation(lua_State* L) {
float angle = luaL_checknumber(L, 1);
float ax = luaL_checknumber(L, 2);
float ay = luaL_checknumber(L, 3);
float az = luaL_checknumber(L, 4);
lovrAudioSetOrientation(angle, ax, ay, az);
return 0;
}
int l_lovrAudioSetPosition(lua_State* L) {
float x = luaL_checknumber(L, 1);
float y = luaL_checknumber(L, 2);
float z = luaL_checknumber(L, 3);
lovrAudioSetPosition(x, y, z);
return 0;
}
int l_lovrAudioSetVolume(lua_State* L) {
float volume = luaL_checknumber(L, 1);
lovrAudioSetVolume(volume);
return 0;
}
int l_lovrAudioStop(lua_State* L) {
lovrAudioStop();
return 0;
}

21
src/lovr/audio.h Normal file
View File

@ -0,0 +1,21 @@
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include "vendor/map/map.h"
map_int_t TimeUnits;
extern const luaL_Reg lovrAudio[];
int l_lovrAudioInit(lua_State* L);
int l_lovrAudioUpdate(lua_State* L);
int l_lovrAudioGetOrientation(lua_State* L);
int l_lovrAudioGetPosition(lua_State* L);
int l_lovrAudioGetVolume(lua_State* L);
int l_lovrAudioNewSource(lua_State* L);
int l_lovrAudioPause(lua_State* L);
int l_lovrAudioResume(lua_State* L);
int l_lovrAudioRewind(lua_State* L);
int l_lovrAudioSetOrientation(lua_State* L);
int l_lovrAudioSetPosition(lua_State* L);
int l_lovrAudioSetVolume(lua_State* L);
int l_lovrAudioStop(lua_State* L);

202
src/lovr/types/source.c Normal file
View File

@ -0,0 +1,202 @@
#include "lovr/types/source.h"
#include "lovr/audio.h"
#include "audio/audio.h"
const luaL_Reg lovrSource[] = {
{ "getBitDepth", l_lovrSourceGetBitDepth },
{ "getChannels", l_lovrSourceGetChannels },
{ "getDuration", l_lovrSourceGetDuration },
{ "getOrientation", l_lovrSourceGetOrientation },
{ "getPitch", l_lovrSourceGetPitch },
{ "getPosition", l_lovrSourceGetPosition },
{ "getSampleRate", l_lovrSourceGetSampleRate },
{ "getVolume", l_lovrSourceGetVolume },
{ "isLooping", l_lovrSourceIsLooping },
{ "isPaused", l_lovrSourceIsPaused },
{ "isPlaying", l_lovrSourceIsPlaying },
{ "isStopped", l_lovrSourceIsStopped },
{ "pause", l_lovrSourcePause },
{ "play", l_lovrSourcePlay },
{ "resume", l_lovrSourceResume },
{ "rewind", l_lovrSourceRewind },
{ "seek", l_lovrSourceSeek },
{ "setLooping", l_lovrSourceSetLooping },
{ "setOrientation", l_lovrSourceSetOrientation },
{ "setPitch", l_lovrSourceSetPitch },
{ "setPosition", l_lovrSourceSetPosition },
{ "setVolume", l_lovrSourceSetVolume },
{ "stop", l_lovrSourceStop },
{ "tell", l_lovrSourceTell },
{ NULL, NULL }
};
int l_lovrSourceGetBitDepth(lua_State* L) {
Source* source = luax_checktype(L, 1, Source);
lua_pushinteger(L, lovrSourceGetBitDepth(source));
return 1;
}
int l_lovrSourceGetChannels(lua_State* L) {
Source* source = luax_checktype(L, 1, Source);
lua_pushinteger(L, lovrSourceGetChannels(source));
return 1;
}
int l_lovrSourceGetDuration(lua_State* L) {
Source* source = luax_checktype(L, 1, Source);
TimeUnit* unit = luax_optenum(L, 2, "seconds", &TimeUnits, "unit");
int duration = lovrSourceGetDuration(source);
if (*unit == UNIT_SECONDS) {
lua_pushnumber(L, (float) duration / lovrSourceGetSampleRate(source));
} else {
lua_pushinteger(L, duration);
}
return 1;
}
int l_lovrSourceGetOrientation(lua_State* L) {
float x, y, z;
lovrSourceGetOrientation(luax_checktype(L, 1, Source), &x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
return 3;
}
int l_lovrSourceGetPitch(lua_State* L) {
Source* source = luax_checktype(L, 1, Source);
lua_pushnumber(L, lovrSourceGetPitch(source));
return 1;
}
int l_lovrSourceGetPosition(lua_State* L) {
float x, y, z;
lovrSourceGetPosition(luax_checktype(L, 1, Source), &x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
return 3;
}
int l_lovrSourceGetSampleRate(lua_State* L) {
Source* source = luax_checktype(L, 1, Source);
lua_pushinteger(L, lovrSourceGetSampleRate(source));
return 1;
}
int l_lovrSourceGetVolume(lua_State* L) {
Source* source = luax_checktype(L, 1, Source);
lua_pushnumber(L, lovrSourceGetVolume(source));
return 1;
}
int l_lovrSourceIsLooping(lua_State* L) {
lua_pushboolean(L, lovrSourceIsLooping(luax_checktype(L, 1, Source)));
return 1;
}
int l_lovrSourceIsPaused(lua_State* L) {
lua_pushnumber(L, lovrSourceIsPaused(luax_checktype(L, 1, Source)));
return 1;
}
int l_lovrSourceIsPlaying(lua_State* L) {
lua_pushnumber(L, lovrSourceIsPlaying(luax_checktype(L, 1, Source)));
return 1;
}
int l_lovrSourceIsStopped(lua_State* L) {
lua_pushnumber(L, lovrSourceIsStopped(luax_checktype(L, 1, Source)));
return 1;
}
int l_lovrSourcePause(lua_State* L) {
lovrSourcePause(luax_checktype(L, 1, Source));
return 0;
}
int l_lovrSourcePlay(lua_State* L) {
Source* source = luax_checktype(L, 1, Source);
lovrSourcePlay(source);
lovrAudioAdd(source);
return 0;
}
int l_lovrSourceResume(lua_State* L) {
lovrSourceResume(luax_checktype(L, 1, Source));
return 0;
}
int l_lovrSourceRewind(lua_State* L) {
lovrSourceRewind(luax_checktype(L, 1, Source));
return 0;
}
int l_lovrSourceSeek(lua_State* L) {
Source* source = luax_checktype(L, 1, Source);
TimeUnit* unit = luax_optenum(L, 3, "seconds", &TimeUnits, "unit");
if (*unit == UNIT_SECONDS) {
float seconds = luaL_checknumber(L, 2);
int sampleRate = lovrSourceGetSampleRate(source);
lovrSourceSeek(source, (int) (seconds * sampleRate + .5f));
} else {
lovrSourceSeek(source, luaL_checkinteger(L, 2));
}
return 0;
}
int l_lovrSourceSetLooping(lua_State* L) {
lovrSourceSetLooping(luax_checktype(L, 1, Source), lua_toboolean(L, 2));
return 0;
}
int l_lovrSourceSetOrientation(lua_State* L) {
Source* source = luax_checktype(L, 1, Source);
float x = luaL_checknumber(L, 2);
float y = luaL_checknumber(L, 3);
float z = luaL_checknumber(L, 4);
lovrSourceSetOrientation(source, x, y, z);
return 0;
}
int l_lovrSourceSetPitch(lua_State* L) {
lovrSourceSetPitch(luax_checktype(L, 1, Source), luaL_checknumber(L, 2));
return 0;
}
int l_lovrSourceSetPosition(lua_State* L) {
Source* source = luax_checktype(L, 1, Source);
float x = luaL_checknumber(L, 2);
float y = luaL_checknumber(L, 3);
float z = luaL_checknumber(L, 4);
lovrSourceSetPosition(source, x, y, z);
return 0;
}
int l_lovrSourceSetVolume(lua_State* L) {
lovrSourceSetVolume(luax_checktype(L, 1, Source), luaL_checknumber(L, 2));
return 0;
}
int l_lovrSourceStop(lua_State* L) {
lovrSourceStop(luax_checktype(L, 1, Source));
return 0;
}
int l_lovrSourceTell(lua_State* L) {
Source* source = luax_checktype(L, 1, Source);
TimeUnit* unit = luax_optenum(L, 2, "seconds", &TimeUnits, "unit");
int offset = lovrSourceTell(source);
if (*unit == UNIT_SECONDS) {
lua_pushnumber(L, (float) offset / lovrSourceGetSampleRate(source));
} else {
lua_pushinteger(L, offset);
}
return 1;
}

30
src/lovr/types/source.h Normal file
View File

@ -0,0 +1,30 @@
#include "audio/source.h"
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
extern const luaL_Reg lovrSource[];
int l_lovrSourceGetBitDepth(lua_State* L);
int l_lovrSourceGetChannels(lua_State* L);
int l_lovrSourceGetDuration(lua_State* L);
int l_lovrSourceGetOrientation(lua_State* L);
int l_lovrSourceGetPitch(lua_State* L);
int l_lovrSourceGetPosition(lua_State* L);
int l_lovrSourceGetSampleRate(lua_State* L);
int l_lovrSourceGetVolume(lua_State* L);
int l_lovrSourceIsLooping(lua_State* L);
int l_lovrSourceIsPaused(lua_State* L);
int l_lovrSourceIsPlaying(lua_State* L);
int l_lovrSourceIsStopped(lua_State* L);
int l_lovrSourcePause(lua_State* L);
int l_lovrSourcePlay(lua_State* L);
int l_lovrSourceResume(lua_State* L);
int l_lovrSourceRewind(lua_State* L);
int l_lovrSourceSeek(lua_State* L);
int l_lovrSourceSetLooping(lua_State* L);
int l_lovrSourceSetOrientation(lua_State* L);
int l_lovrSourceSetPitch(lua_State* L);
int l_lovrSourceSetPosition(lua_State* L);
int l_lovrSourceSetVolume(lua_State* L);
int l_lovrSourceStop(lua_State* L);
int l_lovrSourceTell(lua_State* L);

View File

@ -141,9 +141,9 @@ void mat4_getRotation(mat4 matrix, float* w, float* x, float* y, float* z) {
float qx = sqrt(MAX(0, 1 + matrix[0] - matrix[5] - matrix[10])) / 2;
float qy = sqrt(MAX(0, 1 - matrix[0] + matrix[5] - matrix[10])) / 2;
float qz = sqrt(MAX(0, 1 - matrix[0] - matrix[5] + matrix[10])) / 2;
qx = (matrix[9] - matrix[6]) < 0 ? -qx : qx;
qy = (matrix[2] - matrix[8]) < 0 ? -qy : qy;
qz = (matrix[4] - matrix[1]) < 0 ? -qz : qz;
qx = (matrix[9] - matrix[6]) > 0 ? -qx : qx;
qy = (matrix[2] - matrix[8]) > 0 ? -qy : qy;
qz = (matrix[4] - matrix[1]) > 0 ? -qz : qz;
float s = sqrt(1 - qw * qw);
s = s < .001 ? 1 : s;

View File

@ -1,6 +1,6 @@
#include "util.h"
#define STB_IMAGE_IMPLEMENTATION
#include "vendor/stb_image.h"
#include "vendor/stb/stb_image.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>

View File

@ -17,7 +17,7 @@
#define LOVR_COLOR_B(c) (c >> 8 & 0xff)
#define LOVR_COLOR_A(c) (c >> 0 & 0xff)
#define luax_checktype(L, i, T) *(T**) luaL_checkudata(L, i, #T);
#define luax_checktype(L, i, T) *(T**) luaL_checkudata(L, i, #T)
#define luax_pushtype(L, T, x) \
T** u = (T**) lua_newuserdata(L, sizeof(T**)); \

5067
src/vendor/stb/stb_vorbis.c vendored Normal file

File diff suppressed because it is too large Load Diff

332
src/vendor/stb/stb_vorbis.h vendored Normal file
View File

@ -0,0 +1,332 @@
//////////////////////////////////////////////////////////////////////////////
//
// HEADER BEGINS HERE
//
#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H
#define STB_VORBIS_INCLUDE_STB_VORBIS_H
#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO)
#define STB_VORBIS_NO_STDIO 1
#endif
#ifndef STB_VORBIS_NO_STDIO
#include <stdio.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
/////////// THREAD SAFETY
// Individual stb_vorbis* handles are not thread-safe; you cannot decode from
// them from multiple threads at the same time. However, you can have multiple
// stb_vorbis* handles and decode from them independently in multiple thrads.
/////////// MEMORY ALLOCATION
// normally stb_vorbis uses malloc() to allocate memory at startup,
// and alloca() to allocate temporary memory during a frame on the
// stack. (Memory consumption will depend on the amount of setup
// data in the file and how you set the compile flags for speed
// vs. size. In my test files the maximal-size usage is ~150KB.)
//
// You can modify the wrapper functions in the source (setup_malloc,
// setup_temp_malloc, temp_malloc) to change this behavior, or you
// can use a simpler allocation model: you pass in a buffer from
// which stb_vorbis will allocate _all_ its memory (including the
// temp memory). "open" may fail with a VORBIS_outofmem if you
// do not pass in enough data; there is no way to determine how
// much you do need except to succeed (at which point you can
// query get_info to find the exact amount required. yes I know
// this is lame).
//
// If you pass in a non-NULL buffer of the type below, allocation
// will occur from it as described above. Otherwise just pass NULL
// to use malloc()/alloca()
typedef struct
{
char *alloc_buffer;
int alloc_buffer_length_in_bytes;
} stb_vorbis_alloc;
/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES
typedef struct stb_vorbis stb_vorbis;
typedef struct
{
unsigned int sample_rate;
int channels;
unsigned int setup_memory_required;
unsigned int setup_temp_memory_required;
unsigned int temp_memory_required;
int max_frame_size;
} stb_vorbis_info;
// get general information about the file
extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f);
// get the last error detected (clears it, too)
extern int stb_vorbis_get_error(stb_vorbis *f);
// close an ogg vorbis file and free all memory in use
extern void stb_vorbis_close(stb_vorbis *f);
// this function returns the offset (in samples) from the beginning of the
// file that will be returned by the next decode, if it is known, or -1
// otherwise. after a flush_pushdata() call, this may take a while before
// it becomes valid again.
// NOT WORKING YET after a seek with PULLDATA API
extern int stb_vorbis_get_sample_offset(stb_vorbis *f);
// returns the current seek point within the file, or offset from the beginning
// of the memory buffer. In pushdata mode it returns 0.
extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f);
/////////// PUSHDATA API
#ifndef STB_VORBIS_NO_PUSHDATA_API
// this API allows you to get blocks of data from any source and hand
// them to stb_vorbis. you have to buffer them; stb_vorbis will tell
// you how much it used, and you have to give it the rest next time;
// and stb_vorbis may not have enough data to work with and you will
// need to give it the same data again PLUS more. Note that the Vorbis
// specification does not bound the size of an individual frame.
extern stb_vorbis *stb_vorbis_open_pushdata(
const unsigned char * datablock, int datablock_length_in_bytes,
int *datablock_memory_consumed_in_bytes,
int *error,
const stb_vorbis_alloc *alloc_buffer);
// create a vorbis decoder by passing in the initial data block containing
// the ogg&vorbis headers (you don't need to do parse them, just provide
// the first N bytes of the file--you're told if it's not enough, see below)
// on success, returns an stb_vorbis *, does not set error, returns the amount of
// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes;
// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed
// if returns NULL and *error is VORBIS_need_more_data, then the input block was
// incomplete and you need to pass in a larger block from the start of the file
extern int stb_vorbis_decode_frame_pushdata(
stb_vorbis *f,
const unsigned char *datablock, int datablock_length_in_bytes,
int *channels, // place to write number of float * buffers
float ***output, // place to write float ** array of float * buffers
int *samples // place to write number of output samples
);
// decode a frame of audio sample data if possible from the passed-in data block
//
// return value: number of bytes we used from datablock
//
// possible cases:
// 0 bytes used, 0 samples output (need more data)
// N bytes used, 0 samples output (resynching the stream, keep going)
// N bytes used, M samples output (one frame of data)
// note that after opening a file, you will ALWAYS get one N-bytes,0-sample
// frame, because Vorbis always "discards" the first frame.
//
// Note that on resynch, stb_vorbis will rarely consume all of the buffer,
// instead only datablock_length_in_bytes-3 or less. This is because it wants
// to avoid missing parts of a page header if they cross a datablock boundary,
// without writing state-machiney code to record a partial detection.
//
// The number of channels returned are stored in *channels (which can be
// NULL--it is always the same as the number of channels reported by
// get_info). *output will contain an array of float* buffers, one per
// channel. In other words, (*output)[0][0] contains the first sample from
// the first channel, and (*output)[1][0] contains the first sample from
// the second channel.
extern void stb_vorbis_flush_pushdata(stb_vorbis *f);
// inform stb_vorbis that your next datablock will not be contiguous with
// previous ones (e.g. you've seeked in the data); future attempts to decode
// frames will cause stb_vorbis to resynchronize (as noted above), and
// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it
// will begin decoding the _next_ frame.
//
// if you want to seek using pushdata, you need to seek in your file, then
// call stb_vorbis_flush_pushdata(), then start calling decoding, then once
// decoding is returning you data, call stb_vorbis_get_sample_offset, and
// if you don't like the result, seek your file again and repeat.
#endif
////////// PULLING INPUT API
#ifndef STB_VORBIS_NO_PULLDATA_API
// This API assumes stb_vorbis is allowed to pull data from a source--
// either a block of memory containing the _entire_ vorbis stream, or a
// FILE * that you or it create, or possibly some other reading mechanism
// if you go modify the source to replace the FILE * case with some kind
// of callback to your code. (But if you don't support seeking, you may
// just want to go ahead and use pushdata.)
#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION)
extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output);
#endif
#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION)
extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output);
#endif
// decode an entire file and output the data interleaved into a malloc()ed
// buffer stored in *output. The return value is the number of samples
// decoded, or -1 if the file could not be opened or was not an ogg vorbis file.
// When you're done with it, just free() the pointer returned in *output.
extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len,
int *error, const stb_vorbis_alloc *alloc_buffer);
// create an ogg vorbis decoder from an ogg vorbis stream in memory (note
// this must be the entire stream!). on failure, returns NULL and sets *error
#ifndef STB_VORBIS_NO_STDIO
extern stb_vorbis * stb_vorbis_open_filename(const char *filename,
int *error, const stb_vorbis_alloc *alloc_buffer);
// create an ogg vorbis decoder from a filename via fopen(). on failure,
// returns NULL and sets *error (possibly to VORBIS_file_open_failure).
extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close,
int *error, const stb_vorbis_alloc *alloc_buffer);
// create an ogg vorbis decoder from an open FILE *, looking for a stream at
// the _current_ seek point (ftell). on failure, returns NULL and sets *error.
// note that stb_vorbis must "own" this stream; if you seek it in between
// calls to stb_vorbis, it will become confused. Morever, if you attempt to
// perform stb_vorbis_seek_*() operations on this file, it will assume it
// owns the _entire_ rest of the file after the start point. Use the next
// function, stb_vorbis_open_file_section(), to limit it.
extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close,
int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len);
// create an ogg vorbis decoder from an open FILE *, looking for a stream at
// the _current_ seek point (ftell); the stream will be of length 'len' bytes.
// on failure, returns NULL and sets *error. note that stb_vorbis must "own"
// this stream; if you seek it in between calls to stb_vorbis, it will become
// confused.
#endif
extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number);
extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number);
// these functions seek in the Vorbis file to (approximately) 'sample_number'.
// after calling seek_frame(), the next call to get_frame_*() will include
// the specified sample. after calling stb_vorbis_seek(), the next call to
// stb_vorbis_get_samples_* will start with the specified sample. If you
// do not need to seek to EXACTLY the target sample when using get_samples_*,
// you can also use seek_frame().
extern void stb_vorbis_seek_start(stb_vorbis *f);
// this function is equivalent to stb_vorbis_seek(f,0)
extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f);
extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f);
// these functions return the total length of the vorbis stream
extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output);
// decode the next frame and return the number of samples. the number of
// channels returned are stored in *channels (which can be NULL--it is always
// the same as the number of channels reported by get_info). *output will
// contain an array of float* buffers, one per channel. These outputs will
// be overwritten on the next call to stb_vorbis_get_frame_*.
//
// You generally should not intermix calls to stb_vorbis_get_frame_*()
// and stb_vorbis_get_samples_*(), since the latter calls the former.
#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts);
extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples);
#endif
// decode the next frame and return the number of *samples* per channel.
// Note that for interleaved data, you pass in the number of shorts (the
// size of your array), but the return value is the number of samples per
// channel, not the total number of samples.
//
// The data is coerced to the number of channels you request according to the
// channel coercion rules (see below). You must pass in the size of your
// buffer(s) so that stb_vorbis will not overwrite the end of the buffer.
// The maximum buffer size needed can be gotten from get_info(); however,
// the Vorbis I specification implies an absolute maximum of 4096 samples
// per channel.
// Channel coercion rules:
// Let M be the number of channels requested, and N the number of channels present,
// and Cn be the nth channel; let stereo L be the sum of all L and center channels,
// and stereo R be the sum of all R and center channels (channel assignment from the
// vorbis spec).
// M N output
// 1 k sum(Ck) for all k
// 2 * stereo L, stereo R
// k l k > l, the first l channels, then 0s
// k l k <= l, the first k channels
// Note that this is not _good_ surround etc. mixing at all! It's just so
// you get something useful.
extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats);
extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples);
// gets num_samples samples, not necessarily on a frame boundary--this requires
// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES.
// Returns the number of samples stored per channel; it may be less than requested
// at the end of the file. If there are no more samples in the file, returns 0.
#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts);
extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples);
#endif
// gets num_samples samples, not necessarily on a frame boundary--this requires
// buffering so you have to supply the buffers. Applies the coercion rules above
// to produce 'channels' channels. Returns the number of samples stored per channel;
// it may be less than requested at the end of the file. If there are no more
// samples in the file, returns 0.
#endif
//////// ERROR CODES
enum STBVorbisError
{
VORBIS__no_error,
VORBIS_need_more_data=1, // not a real error
VORBIS_invalid_api_mixing, // can't mix API modes
VORBIS_outofmem, // not enough memory
VORBIS_feature_not_supported, // uses floor 0
VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small
VORBIS_file_open_failure, // fopen() failed
VORBIS_seek_without_length, // can't seek in unknown-length file
VORBIS_unexpected_eof=10, // file is truncated?
VORBIS_seek_invalid, // seek past EOF
// decoding errors (corrupt/invalid stream) -- you probably
// don't care about the exact details of these
// vorbis errors:
VORBIS_invalid_setup=20,
VORBIS_invalid_stream,
// ogg errors:
VORBIS_missing_capture_pattern=30,
VORBIS_invalid_stream_structure_version,
VORBIS_continued_packet_flag_invalid,
VORBIS_incorrect_stream_serial_number,
VORBIS_invalid_first_page,
VORBIS_bad_packet_type,
VORBIS_cant_find_last_page,
VORBIS_seek_failed
};
#ifdef __cplusplus
}
#endif
#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H
//
// HEADER ENDS HERE
//
//////////////////////////////////////////////////////////////////////////////