Skip to content

Commit

Permalink
Implement resampler using libsamplerate by Erik de Castro Lopo
Browse files Browse the repository at this point in the history
  • Loading branch information
norihiro committed Dec 26, 2023
1 parent 699299c commit a482529
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 0 deletions.
14 changes: 14 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ include(cmake/ObsPluginHelpers.cmake)

if(OS_LINUX)
option(USE_FFMPEG_SWRESAMPLE "Use swresample from FFmpeg" ON)
option(USE_ERIKD_LIBSAMPLERATE "Use libsamplerate by Erik de Castro Lopo" OFF)
else()
option(USE_FFMPEG_SWRESAMPLE "Use swresample from FFmpeg" OFF)
option(USE_ERIKD_LIBSAMPLERATE "Use libsamplerate by Erik de Castro Lopo" ON)
endif()

if(USE_FFMPEG_SWRESAMPLE)
Expand Down Expand Up @@ -67,6 +69,18 @@ if(USE_FFMPEG_SWRESAMPLE)
)
endif()

if (USE_ERIKD_LIBSAMPLERATE)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/deps/libsamplerate/cmake/")
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(deps/libsamplerate)
target_link_libraries(${CMAKE_PROJECT_NAME}
samplerate
)
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
src/resampler-erikd.c
)
endif()

if(OS_WINDOWS)
# Enable Multicore Builds and disable FH4 (to not depend on VCRUNTIME140_1.DLL when building with VS2019)
if (MSVC)
Expand Down
1 change: 1 addition & 0 deletions src/plugin-macros.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define ID_PREFIX "@ID_PREFIX@"

#cmakedefine USE_FFMPEG_SWRESAMPLE
#cmakedefine USE_ERIKD_LIBSAMPLERATE

#define blog(level, msg, ...) blog(level, "[" PLUGIN_NAME "] " msg, ##__VA_ARGS__)

Expand Down
155 changes: 155 additions & 0 deletions src/resampler-erikd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#include <inttypes.h>
#include <obs-module.h>
#include "plugin-macros.generated.h"
#include "resampler.h"
#include "samplerate.h"

#define BUFFER_SIZE_MARGIN 4

typedef DARRAY(float) float_array_t;

struct ctx_s
{
uint32_t samples_per_sec;
uint32_t channels;

double compensation;

// resampler context
SRC_STATE *src_state;

// Buffer
float_array_t buffer1;
float_array_t buffer2;
};

static void resampler_deinit(struct ctx_s *ctx);

static void *erikd_resampler_create(resampler_t *rs)
{
UNUSED_PARAMETER(rs);
struct ctx_s *ctx = bzalloc(sizeof(struct ctx_s));
return ctx;
}

static void erikd_resampler_destroy(resampler_t *rs)
{
struct ctx_s *ctx = rs->ctx;
resampler_deinit(ctx);
da_free(ctx->buffer1);
da_free(ctx->buffer2);
bfree(ctx);
}

static inline bool check_formats(struct ctx_s *ctx, struct obs_audio_info *oai)
{
if (ctx->samples_per_sec != oai->samples_per_sec)
return false;

if (ctx->channels != get_audio_channels(oai->speakers))
return false;

return true;
}

static inline void resampler_init_if_necessary(resampler_t *rs)
{
struct ctx_s *ctx = rs->ctx;
struct obs_audio_info oai;
obs_get_audio_info(&oai);

if (check_formats(ctx, &oai))
return;

resampler_deinit(ctx);

ctx->samples_per_sec = oai.samples_per_sec;
ctx->channels = get_audio_channels(oai.speakers);

int error = 0;
ctx->src_state = src_new(SRC_SINC_BEST_QUALITY, ctx->channels, &error);
if (!ctx->src_state) {
blog(LOG_ERROR, "Failed to initialize resampler: %s", src_strerror(error));
return;
}
}

static void resampler_deinit(struct ctx_s *ctx)
{
if (ctx->src_state)
src_delete(ctx->src_state);
ctx->src_state = NULL;
}

static void planar_to_packed(float *dst, const float *src[], uint32_t channels, uint32_t frames)
{
for (uint32_t c = 0; c < channels; c++) {
float *ptr = dst + c;
for (uint32_t f = 0; f < frames; f++) {
*ptr = src[c][f];
ptr += channels;
}
}
}

static void packed_to_planar(float *dst[], const float *src, uint32_t channels, uint32_t frames)
{
for (uint32_t c = 0; c < channels; c++) {
const float *ptr = src + c;
for (uint32_t f = 0; f < frames; f++) {
dst[c][f] = *ptr;
ptr += channels;
}
}
}

static uint32_t erikd_resampler_audio(resampler_t *rs, float *data[], uint32_t frames, uint64_t *pts)
{
struct ctx_s *ctx = rs->ctx;
resampler_init_if_necessary(rs);

da_resize(ctx->buffer1, frames * ctx->channels);
planar_to_packed(ctx->buffer1.array, (const float **)data, ctx->channels, frames);

da_resize(ctx->buffer2, (frames + BUFFER_SIZE_MARGIN) * ctx->channels);

SRC_DATA src_data = {
.data_in = ctx->buffer1.array,
.data_out = ctx->buffer2.array,
.input_frames= frames,
.output_frames = frames + BUFFER_SIZE_MARGIN,
.end_of_input = 0,
.src_ratio = 1.0 + ctx->compensation / ctx->samples_per_sec,
};

int ret = src_process(ctx->src_state, &src_data);

for (uint32_t c = 0; c < ctx->channels; c++)
data[c] = ctx->buffer1.array + c * src_data.output_frames_gen;
packed_to_planar(data, ctx->buffer2.array, ctx->channels, src_data.output_frames_gen);

// TODO: Calculate *pts.

if (src_data.input_frames != src_data.input_frames_used || src_data.input_frames != src_data.output_frames_gen)
blog(LOG_INFO, "input_frames=%d input_frames_used=%d output_frames_gen=%d ratio=%f",
src_data.input_frames,
src_data.input_frames_used,
src_data.output_frames_gen,
src_data.src_ratio - 1.0);

return src_data.output_frames_gen;
}

static void erikd_resampler_compensate(resampler_t *rs, double compensation)
{
struct ctx_s *ctx = rs->ctx;
ctx->compensation = compensation;
}

const struct resampler_type erikd_resampler_type = {
.type_name = "libsamplerate",
.create = erikd_resampler_create,
.destroy = erikd_resampler_destroy,
.audio = erikd_resampler_audio,
.compensate = erikd_resampler_compensate,
};
4 changes: 4 additions & 0 deletions src/resampler.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
#include "plugin-macros.generated.h"
#include "resampler.h"

extern const struct resampler_type erikd_resampler_type;
extern const struct resampler_type ffmpeg_resampler_type;

const static struct resampler_type *types[] = {
#ifdef USE_ERIKD_LIBSAMPLERATE
&erikd_resampler_type,
#endif
#ifdef USE_FFMPEG_SWRESAMPLE
&ffmpeg_resampler_type,
#endif
Expand Down

0 comments on commit a482529

Please sign in to comment.