mirror of https://github.com/bjornbytes/lovr.git
Change mic capture API to return SoundData stream instead of individual chunks
This commit is contained in:
parent
044a5a47d9
commit
5fcaad9bde
|
@ -12,12 +12,6 @@ StringEntry lovrAudioType[] = {
|
|||
{ 0 }
|
||||
};
|
||||
|
||||
StringEntry lovrTimeUnit[] = {
|
||||
[UNIT_SECONDS] = ENTRY("seconds"),
|
||||
[UNIT_SAMPLES] = ENTRY("samples"),
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static int l_lovrAudioReset(lua_State* L) {
|
||||
lovrAudioReset();
|
||||
return 0;
|
||||
|
@ -86,37 +80,9 @@ static int l_lovrAudioSetListenerPose(lua_State *L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int l_lovrAudioGetCaptureDuration(lua_State *L) {
|
||||
TimeUnit units = luax_checkenum(L, 1, TimeUnit, "seconds");
|
||||
size_t sampleCount = lovrAudioGetCaptureSampleCount();
|
||||
|
||||
if (units == UNIT_SECONDS) {
|
||||
lua_pushnumber(L, lovrAudioConvertToSeconds(sampleCount, AUDIO_CAPTURE));
|
||||
} else {
|
||||
lua_pushinteger(L, sampleCount);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrAudioCapture(lua_State* L) {
|
||||
int index = 1;
|
||||
|
||||
size_t samples = lua_type(L, index) == LUA_TNUMBER ? lua_tointeger(L, index++) : lovrAudioGetCaptureSampleCount();
|
||||
|
||||
if (samples == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SoundData* soundData = luax_totype(L, index++, SoundData);
|
||||
size_t offset = soundData ? luaL_optinteger(L, index, 0) : 0;
|
||||
|
||||
if (soundData) {
|
||||
lovrRetain(soundData);
|
||||
}
|
||||
|
||||
soundData = lovrAudioCapture(samples, soundData, offset);
|
||||
static int l_lovrAudioGetCaptureStream(lua_State* L) {
|
||||
SoundData* soundData = lovrAudioGetCaptureStream();
|
||||
luax_pushtype(L, SoundData, soundData);
|
||||
lovrRelease(SoundData, soundData);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -165,8 +131,7 @@ static const luaL_Reg lovrAudio[] = {
|
|||
{ "setVolume", l_lovrAudioSetVolume },
|
||||
{ "newSource", l_lovrAudioNewSource },
|
||||
{ "setListenerPose", l_lovrAudioSetListenerPose },
|
||||
{ "capture", l_lovrAudioCapture },
|
||||
{ "getCaptureDuration", l_lovrAudioGetCaptureDuration },
|
||||
{ "getCaptureStream", l_lovrAudioGetCaptureStream },
|
||||
{ "getDevices", l_lovrAudioGetDevices },
|
||||
{ "useDevice", l_lovrUseDevice },
|
||||
{ NULL, NULL }
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
#include "api.h"
|
||||
#include "data/soundData.h"
|
||||
#include "data/blob.h"
|
||||
#include "core/ref.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
StringEntry lovrTimeUnit[] = {
|
||||
[UNIT_SECONDS] = ENTRY("seconds"),
|
||||
[UNIT_SAMPLES] = ENTRY("samples"),
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static int l_lovrSoundDataGetBlob(lua_State* L) {
|
||||
SoundData* soundData = luax_checktype(L, 1, SoundData);
|
||||
|
@ -9,6 +17,51 @@ static int l_lovrSoundDataGetBlob(lua_State* L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrSoundDataGetDuration(lua_State* L) {
|
||||
SoundData* soundData = luax_checktype(L, 1, SoundData);
|
||||
TimeUnit units = luax_checkenum(L, 2, TimeUnit, "seconds");
|
||||
uint32_t frames = lovrSoundDataGetDuration(soundData);
|
||||
if (units == UNIT_SECONDS) {
|
||||
lua_pushnumber(L, (double) frames / soundData->sampleRate);
|
||||
} else {
|
||||
lua_pushinteger(L, frames);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *format2string(SampleFormat f) { return f == SAMPLE_I16 ? "i16" : "f32"; }
|
||||
|
||||
// soundData:read(dest, {size}, {offset}) -> framesRead
|
||||
// soundData:read({size}) -> framesRead
|
||||
static int l_lovrSoundDataRead(lua_State* L) {
|
||||
//struct SoundData* soundData, uint32_t offset, uint32_t count, void* data
|
||||
SoundData* source = luax_checktype(L, 1, SoundData);
|
||||
int index = 2;
|
||||
SoundData* dest = luax_totype(L, index, SoundData);
|
||||
size_t frameCount = lua_type(L, index) == LUA_TNUMBER ? lua_tointeger(L, index++) : lovrSoundDataGetDuration(source);
|
||||
size_t offset = dest ? luaL_optinteger(L, index, 0) : 0;
|
||||
bool shouldRelease = false;
|
||||
if (dest == NULL) {
|
||||
dest = lovrSoundDataCreateRaw(frameCount, source->channels, source->sampleRate, source->format, NULL);
|
||||
shouldRelease = true;
|
||||
} else {
|
||||
lovrAssert(dest->channels == source->channels, "Source (%d) and destination (%d) channel counts must match", source->channels, dest->channels);
|
||||
lovrAssert(dest->sampleRate == source->sampleRate, "Source (%d) and destination (%d) sample rates must match", source->sampleRate, dest->sampleRate);
|
||||
lovrAssert(dest->format == source->format, "Source (%s) and destination (%s) formats must match", format2string(source->format), format2string(dest->format));
|
||||
lovrAssert(offset + frameCount <= dest->frames, "Tried to write samples past the end of a SoundData buffer");
|
||||
lovrAssert(dest->blob, "Can't read into a stream destination");
|
||||
}
|
||||
|
||||
size_t outFrames = source->read(source, offset, frameCount, dest->blob->data);
|
||||
dest->frames = outFrames;
|
||||
dest->blob->size = outFrames * SampleFormatBytesPerFrame(dest->channels, dest->format);
|
||||
luax_pushtype(L, SoundData, dest);
|
||||
if (shouldRelease) lovrRelease(SoundData, dest);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrSoundDataAppend(lua_State* L) {
|
||||
SoundData* soundData = luax_checktype(L, 1, SoundData);
|
||||
Blob* blob = luax_totype(L, 2, Blob);
|
||||
|
@ -35,6 +88,8 @@ static int l_lovrSoundDataSetSample(lua_State* L) {
|
|||
|
||||
const luaL_Reg lovrSoundData[] = {
|
||||
{ "getBlob", l_lovrSoundDataGetBlob },
|
||||
{ "getDuration", l_lovrSoundDataGetDuration },
|
||||
{ "read", l_lovrSoundDataRead },
|
||||
{ "append", l_lovrSoundDataAppend },
|
||||
{ "setSample", l_lovrSoundDataSetSample },
|
||||
{ NULL, NULL }
|
||||
|
|
|
@ -42,7 +42,7 @@ static struct {
|
|||
ma_device devices[AUDIO_TYPE_COUNT];
|
||||
ma_mutex playbackLock;
|
||||
Source* sources;
|
||||
ma_pcm_rb captureRingbuffer;
|
||||
SoundData *captureStream;
|
||||
arr_t(ma_data_converter*) converters;
|
||||
Spatializer* spatializer;
|
||||
|
||||
|
@ -115,23 +115,9 @@ static void onPlayback(ma_device* device, void* output, const void* _, uint32_t
|
|||
}
|
||||
|
||||
static void onCapture(ma_device* device, void* output, const void* input, uint32_t frames) {
|
||||
// note: ma_pcm_rb is lockless
|
||||
void *store;
|
||||
// note: uses ma_pcm_rb which is lockless
|
||||
size_t bytesPerFrame = SampleFormatBytesPerFrame(CAPTURE_CHANNELS, OUTPUT_FORMAT);
|
||||
while(frames > 0) {
|
||||
uint32_t availableFrames = frames;
|
||||
ma_result acquire_status = ma_pcm_rb_acquire_write(&state.captureRingbuffer, &availableFrames, &store);
|
||||
if (acquire_status != MA_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
memcpy(store, input, availableFrames * bytesPerFrame);
|
||||
ma_result commit_status = ma_pcm_rb_commit_write(&state.captureRingbuffer, availableFrames, store);
|
||||
if (commit_status != MA_SUCCESS || availableFrames == 0) {
|
||||
return;
|
||||
}
|
||||
frames -= availableFrames;
|
||||
input += availableFrames * bytesPerFrame;
|
||||
}
|
||||
lovrSoundDataStreamAppendBuffer(state.captureStream, input, frames*bytesPerFrame);
|
||||
}
|
||||
|
||||
static const ma_device_callback_proc callbacks[] = { onPlayback, onCapture };
|
||||
|
@ -167,12 +153,6 @@ bool lovrAudioInit(AudioConfig config[2]) {
|
|||
}
|
||||
}
|
||||
|
||||
ma_result rbstatus = ma_pcm_rb_init(miniAudioFormatFromLovr[OUTPUT_FORMAT], CAPTURE_CHANNELS, state.config[AUDIO_CAPTURE].sampleRate * 1.0, NULL, NULL, &state.captureRingbuffer);
|
||||
if (rbstatus != MA_SUCCESS) {
|
||||
lovrAudioDestroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sizeof(spatializers) / sizeof(spatializers[0]); i++) {
|
||||
if (spatializers[i]->init()) {
|
||||
state.spatializer = spatializers[i];
|
||||
|
@ -192,6 +172,7 @@ void lovrAudioDestroy() {
|
|||
ma_device_uninit(&state.devices[AUDIO_CAPTURE]);
|
||||
ma_mutex_uninit(&state.playbackLock);
|
||||
ma_context_uninit(&state.context);
|
||||
lovrRelease(SoundData, state.captureStream);
|
||||
if (state.spatializer) state.spatializer->destroy();
|
||||
for(int i = 0; i < state.converters.length; i++) {
|
||||
ma_data_converter_uninit(state.converters.data[i]);
|
||||
|
@ -221,6 +202,17 @@ bool lovrAudioInitDevice(AudioType type) {
|
|||
lovrLog(LOG_WARN, "audio", "Failed to enable audio device %d: %d\n", type, err);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type == AUDIO_CAPTURE) {
|
||||
lovrRelease(SoundData, state.captureStream);
|
||||
state.captureStream = lovrSoundDataCreateStream(state.config[type].sampleRate * 1.0, CAPTURE_CHANNELS, state.config[type].sampleRate, OUTPUT_FORMAT);
|
||||
if (!state.captureStream) {
|
||||
lovrLog(LOG_WARN, "audio", "Failed to init audio device %d\n", type);
|
||||
lovrAudioDestroy();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -406,55 +398,12 @@ SoundData* lovrSourceGetSoundData(Source* source) {
|
|||
|
||||
// Capture
|
||||
|
||||
uint32_t lovrAudioGetCaptureSampleCount() {
|
||||
// note: must only be called from ONE thread!! ma_pcm_rb only promises
|
||||
// thread safety with ONE reader and ONE writer thread.
|
||||
return ma_pcm_rb_available_read(&state.captureRingbuffer);
|
||||
struct SoundData* lovrAudioGetCaptureStream()
|
||||
{
|
||||
return state.captureStream;
|
||||
}
|
||||
|
||||
static const char *format2string(SampleFormat f) { return f == SAMPLE_I16 ? "i16" : "f32"; }
|
||||
|
||||
struct SoundData* lovrAudioCapture(uint32_t frameCount, SoundData *soundData, uint32_t offset) {
|
||||
|
||||
uint32_t bufferedFrames = lovrAudioGetCaptureSampleCount();
|
||||
if (frameCount == 0 || frameCount > bufferedFrames) {
|
||||
frameCount = bufferedFrames;
|
||||
}
|
||||
|
||||
if (frameCount == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (soundData == NULL) {
|
||||
soundData = lovrSoundDataCreateRaw(frameCount, CAPTURE_CHANNELS, state.config[AUDIO_CAPTURE].sampleRate, state.config[AUDIO_CAPTURE].format, NULL);
|
||||
} else {
|
||||
lovrAssert(soundData->channels == CAPTURE_CHANNELS, "Capture (%d) and SoundData (%d) channel counts must match", CAPTURE_CHANNELS, soundData->channels);
|
||||
lovrAssert(soundData->sampleRate == state.config[AUDIO_CAPTURE].sampleRate, "Capture (%d) and SoundData (%d) sample rates must match", state.config[AUDIO_CAPTURE].sampleRate, soundData->sampleRate);
|
||||
lovrAssert(soundData->format == state.config[AUDIO_CAPTURE].format, "Capture (%s) and SoundData (%s) formats must match", format2string(state.config[AUDIO_CAPTURE].format), format2string(soundData->format));
|
||||
lovrAssert(offset + frameCount <= soundData->frames, "Tried to write samples past the end of a SoundData buffer");
|
||||
}
|
||||
|
||||
uint32_t bytesPerFrame = SampleFormatBytesPerFrame(CAPTURE_CHANNELS, state.config[AUDIO_CAPTURE].format);
|
||||
while(frameCount > 0) {
|
||||
uint32_t availableFramesInRB = frameCount;
|
||||
void *store;
|
||||
ma_result acquire_status = ma_pcm_rb_acquire_read(&state.captureRingbuffer, &availableFramesInRB, &store);
|
||||
if (acquire_status != MA_SUCCESS) {
|
||||
lovrAssert(false, "Failed to acquire ring buffer for read: %d\n", acquire_status);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(soundData->blob->data + offset * bytesPerFrame, store, availableFramesInRB * bytesPerFrame);
|
||||
ma_result commit_status = ma_pcm_rb_commit_read(&state.captureRingbuffer, availableFramesInRB, store);
|
||||
if (commit_status != MA_SUCCESS) {
|
||||
lovrAssert(false, "Failed to commit ring buffer for read: %d\n", acquire_status);
|
||||
return NULL;
|
||||
}
|
||||
frameCount -= availableFramesInRB;
|
||||
offset += availableFramesInRB;
|
||||
}
|
||||
|
||||
return soundData;
|
||||
}
|
||||
// Devices
|
||||
|
||||
void lovrAudioGetDevices(AudioDevice **outDevices, size_t *outCount) {
|
||||
if(state.deviceInfos)
|
||||
|
|
|
@ -21,11 +21,6 @@ typedef enum {
|
|||
SOURCE_STREAM
|
||||
} SourceType;
|
||||
|
||||
typedef enum {
|
||||
UNIT_SECONDS,
|
||||
UNIT_SAMPLES
|
||||
} TimeUnit;
|
||||
|
||||
typedef void* AudioDeviceIdentifier;
|
||||
|
||||
typedef struct {
|
||||
|
@ -70,8 +65,7 @@ uint32_t lovrSourceGetTime(Source* source);
|
|||
void lovrSourceSetTime(Source* source, uint32_t sample);
|
||||
struct SoundData* lovrSourceGetSoundData(Source* source);
|
||||
|
||||
uint32_t lovrAudioGetCaptureSampleCount();
|
||||
struct SoundData* lovrAudioCapture(uint32_t sampleCount, struct SoundData *soundData, uint32_t offset);
|
||||
struct SoundData* lovrAudioGetCaptureStream();
|
||||
|
||||
void lovrAudioGetDevices(AudioDevice **outDevices, size_t *outCount);
|
||||
void lovrAudioUseDevice(AudioDeviceIdentifier identifier, int sampleRate, SampleFormat format);
|
|
@ -158,18 +158,23 @@ SoundData* lovrSoundDataCreateFromFile(struct Blob* blob, bool decode) {
|
|||
}
|
||||
|
||||
size_t lovrSoundDataStreamAppendBlob(SoundData *dest, struct Blob* blob) {
|
||||
lovrSoundDataStreamAppendBuffer(dest, blob->data, blob->size);
|
||||
}
|
||||
|
||||
size_t lovrSoundDataStreamAppendBuffer(SoundData *dest, const void *buf, size_t byteSize) {
|
||||
lovrAssert(dest->ring, "Data can only be appended to a SoundData stream");
|
||||
|
||||
const uint8_t *charBuf = (const uint8_t*)buf;
|
||||
void *store;
|
||||
size_t blobOffset = 0;
|
||||
size_t bytesPerFrame = SampleFormatBytesPerFrame(dest->channels, dest->format);
|
||||
size_t frameCount = blob->size / bytesPerFrame;
|
||||
size_t frameCount = byteSize / bytesPerFrame;
|
||||
size_t framesAppended = 0;
|
||||
while(frameCount > 0) {
|
||||
uint32_t availableFrames = frameCount;
|
||||
ma_result acquire_status = ma_pcm_rb_acquire_write(dest->ring, &availableFrames, &store);
|
||||
lovrAssert(acquire_status == MA_SUCCESS, "Failed to acquire ring buffer");
|
||||
memcpy(store, blob->data + blobOffset, availableFrames * bytesPerFrame);
|
||||
memcpy(store, charBuf + blobOffset, availableFrames * bytesPerFrame);
|
||||
ma_result commit_status = ma_pcm_rb_commit_write(dest->ring, availableFrames, store);
|
||||
lovrAssert(commit_status == MA_SUCCESS, "Failed to commit to ring buffer");
|
||||
if (availableFrames == 0) {
|
||||
|
|
|
@ -16,6 +16,11 @@ typedef enum {
|
|||
SAMPLE_INVALID
|
||||
} SampleFormat;
|
||||
|
||||
typedef enum {
|
||||
UNIT_SECONDS,
|
||||
UNIT_SAMPLES
|
||||
} TimeUnit;
|
||||
|
||||
size_t SampleFormatBytesPerFrame(int channelCount, SampleFormat fmt);
|
||||
|
||||
typedef struct SoundData {
|
||||
|
@ -35,6 +40,7 @@ SoundData* lovrSoundDataCreateStream(uint32_t bufferSizeInFrames, uint32_t chann
|
|||
SoundData* lovrSoundDataCreateFromFile(struct Blob* blob, bool decode);
|
||||
|
||||
// returns the number of frames successfully appended (if it's less than the size of blob, the internal ring buffer is full)
|
||||
size_t lovrSoundDataStreamAppendBuffer(SoundData *dest, const void *buf, size_t byteSize);
|
||||
size_t lovrSoundDataStreamAppendBlob(SoundData *dest, struct Blob* blob);
|
||||
size_t lovrSoundDataStreamAppendSound(SoundData *dest, SoundData *src);
|
||||
void lovrSoundDataSetSample(SoundData* soundData, size_t index, float value);
|
||||
|
|
Loading…
Reference in New Issue