Skip to content

Commit

Permalink
Use libsamplerate on WAV loads
Browse files Browse the repository at this point in the history
  • Loading branch information
DeeJayLSP committed Dec 5, 2024
1 parent 47bc374 commit f43a435
Show file tree
Hide file tree
Showing 14 changed files with 25,316 additions and 5 deletions.
5 changes: 5 additions & 0 deletions COPYRIGHT.txt
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,11 @@ Copyright: 1995-2024, The PNG Reference Library Authors.
1995-1996, Guy Eric Schalnat, Group 42, Inc.
License: Zlib

Files: ./thirdparty/libsamplerate/
Comment: libsamplerate
Copyright: 2012-2016, Erik de Castro Lopo.
License: BSD-2-clause

Files: ./thirdparty/libtheora/
Comment: OggTheora
Copyright: 2002-2009, Xiph.org Foundation
Expand Down
1 change: 1 addition & 0 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ opts.Add(BoolVariable("builtin_harfbuzz", "Use the built-in HarfBuzz library", T
opts.Add(BoolVariable("builtin_icu4c", "Use the built-in ICU library", True))
opts.Add(BoolVariable("builtin_libogg", "Use the built-in libogg library", True))
opts.Add(BoolVariable("builtin_libpng", "Use the built-in libpng library", True))
opts.Add(BoolVariable("builtin_libsamplerate", "Use the built-in libsamplerate library", True))
opts.Add(BoolVariable("builtin_libtheora", "Use the built-in libtheora library", True))
opts.Add(BoolVariable("builtin_libvorbis", "Use the built-in libvorbis library", True))
opts.Add(BoolVariable("builtin_libwebp", "Use the built-in libwebp library", True))
Expand Down
3 changes: 3 additions & 0 deletions platform/linuxbsd/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ def configure(env: "SConsEnvironment"):
# Sound and video libraries
# Keep the order as it triggers chained dependencies (ogg needed by others, etc.)

if not env["builtin_libsamplerate"]:
env.ParseConfig("pkg-config samplerate --cflags --libs")

if not env["builtin_libtheora"]:
env["builtin_libogg"] = False # Needed to link against system libtheora
env["builtin_libvorbis"] = False # Needed to link against system libtheora
Expand Down
36 changes: 36 additions & 0 deletions scene/resources/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,42 @@ thirdparty_obj = []

thirdparty_sources = "#thirdparty/misc/mikktspace.c"

if env.editor_build:
if env["builtin_libsamplerate"]:
thirdparty_libsamplerate_dir = "#thirdparty/libsamplerate/"
thirdparty_libsamplerate_src = thirdparty_libsamplerate_dir + "src/"
thirdparty_libsamplerate_sources = [
"samplerate.c",
"src_linear.c",
"src_sinc.c",
"src_zoh.c",
]

thirdparty_libsamplerate_sources = [
thirdparty_libsamplerate_src + file
for file in thirdparty_libsamplerate_sources
]

env_libsamplerate = env.Clone()
env_libsamplerate.Prepend(
CPPDEFINES=[
"ENABLE_SINC_MEDIUM_CONVERTER",
"HAVE_STDBOOL_H",
# These last two are irrelevant for the engine, but required for compiling.
("PACKAGE", f'\\"\\"'),
("VERSION", f'\\"\\"'),
]
)
env.Prepend(
CPPPATH=[
thirdparty_libsamplerate_dir + "include/",
thirdparty_libsamplerate_src,
]
)
env_libsamplerate.add_source_files(
thirdparty_obj, thirdparty_libsamplerate_sources
)

env_thirdparty = env.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
Expand Down
18 changes: 13 additions & 5 deletions scene/resources/audio_stream_wav.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
#include "core/io/file_access_memory.h"
#include "core/io/marshalls.h"

#ifdef TOOLS_ENABLED
#include <samplerate.h>
#endif

const float TRIM_DB_LIMIT = -50;
const int TRIM_FADE_OUT_FRAMES = 500;

Expand Down Expand Up @@ -1001,23 +1005,26 @@ Ref<AudioStreamWAV> AudioStreamWAV::load_from_buffer(const Vector<uint8_t> &p_fi
print_line("\tloop end: " + itos(loop_end));
*/

//apply frequency limit
// Apply sample rate conversion

bool limit_rate = p_options["force/max_rate"];
int limit_rate_hz = p_options["force/max_rate_hz"];
if (limit_rate && rate > limit_rate_hz && rate > 0 && frames > 0) {
// resample!
int new_data_frames = (int)(frames * (float)limit_rate_hz / (float)rate);
double ratio = (double)limit_rate_hz / rate;
int new_data_frames = (int)(ratio * frames);

Vector<float> new_data;
new_data.resize(new_data_frames * format_channels);

#ifdef TOOLS_ENABLED // libsamplerate's Sinc resampler, medium quality.
SRC_DATA src_data = { data.ptr(), new_data.ptrw(), frames, new_data_frames, 0, 0, 1, ratio };
src_simple(&src_data, SRC_SINC_MEDIUM_QUALITY, format_channels);
#else // Cubic hermite spline interpolation.
for (int c = 0; c < format_channels; c++) {
float frac = 0.0;
int ipos = 0;

for (int i = 0; i < new_data_frames; i++) {
// Cubic interpolation should be enough.

float y0 = data[MAX(0, ipos - 1) * format_channels + c];
float y1 = data[ipos * format_channels + c];
float y2 = data[MIN(frames - 1, ipos + 1) * format_channels + c];
Expand All @@ -1034,6 +1041,7 @@ Ref<AudioStreamWAV> AudioStreamWAV::load_from_buffer(const Vector<uint8_t> &p_fi
frac -= tpos;
}
}
#endif

if (loop_mode) {
loop_begin = (int)(loop_begin * (float)new_data_frames / (float)frames);
Expand Down
14 changes: 14 additions & 0 deletions thirdparty/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,20 @@ Files extracted from upstream source:
- `LICENSE`


## libsamplerate

- Upstream: https://libsndfile.github.io/libsamplerate/
- Version: 0.2.2 (c96f5e3de9c4488f4e6c97f59f5245f22fda22f7, 2024)
- License: BSD-2-Clause

Files extracted from upstream source:

- `include/`
- `src/*.c`
- `src/common.h` and `src/mid_qual_coeffs.h`
- `COPYING`


## libtheora

- Upstream: https://www.theora.org
Expand Down
25 changes: 25 additions & 0 deletions thirdparty/libsamplerate/COPYING
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Copyright (c) 2012-2016, Erik de Castro Lopo <[email protected]>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
189 changes: 189 additions & 0 deletions thirdparty/libsamplerate/include/samplerate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
** Copyright (c) 2002-2016, Erik de Castro Lopo <[email protected]>
** All rights reserved.
**
** This code is released under 2-clause BSD license. Please see the
** file at : https://github.com/libsndfile/libsamplerate/blob/master/COPYING
*/

/*
** API documentation is available here:
** http://libsndfile.github.io/libsamplerate/api.html
*/

#ifndef SAMPLERATE_H
#define SAMPLERATE_H

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */


/* Opaque data type SRC_STATE. */
typedef struct SRC_STATE_tag SRC_STATE ;

/* SRC_DATA is used to pass data to src_simple() and src_process(). */
typedef struct
{ const float *data_in ;
float *data_out ;

long input_frames, output_frames ;
long input_frames_used, output_frames_gen ;

int end_of_input ;

double src_ratio ;
} SRC_DATA ;

/*
** User supplied callback function type for use with src_callback_new()
** and src_callback_read(). First parameter is the same pointer that was
** passed into src_callback_new(). Second parameter is pointer to a
** pointer. The user supplied callback function must modify *data to
** point to the start of the user supplied float array. The user supplied
** function must return the number of frames that **data points to.
*/

typedef long (*src_callback_t) (void *cb_data, float **data) ;

/*
** Standard initialisation function : return an anonymous pointer to the
** internal state of the converter. Choose a converter from the enums below.
** Error returned in *error.
*/

SRC_STATE* src_new (int converter_type, int channels, int *error) ;

/*
** Clone a handle : return an anonymous pointer to a new converter
** containing the same internal state as orig. Error returned in *error.
*/
SRC_STATE* src_clone (SRC_STATE* orig, int *error) ;

/*
** Initilisation for callback based API : return an anonymous pointer to the
** internal state of the converter. Choose a converter from the enums below.
** The cb_data pointer can point to any data or be set to NULL. Whatever the
** value, when processing, user supplied function "func" gets called with
** cb_data as first parameter.
*/

SRC_STATE* src_callback_new (src_callback_t func, int converter_type, int channels,
int *error, void* cb_data) ;

/*
** Cleanup all internal allocations.
** Always returns NULL.
*/

SRC_STATE* src_delete (SRC_STATE *state) ;

/*
** Standard processing function.
** Returns non zero on error.
*/

int src_process (SRC_STATE *state, SRC_DATA *data) ;

/*
** Callback based processing function. Read up to frames worth of data from
** the converter int *data and return frames read or -1 on error.
*/
long src_callback_read (SRC_STATE *state, double src_ratio, long frames, float *data) ;

/*
** Simple interface for performing a single conversion from input buffer to
** output buffer at a fixed conversion ratio.
** Simple interface does not require initialisation as it can only operate on
** a single buffer worth of audio.
*/

int src_simple (SRC_DATA *data, int converter_type, int channels) ;

/*
** This library contains a number of different sample rate converters,
** numbered 0 through N.
**
** Return a string giving either a name or a more full description of each
** sample rate converter or NULL if no sample rate converter exists for
** the given value. The converters are sequentially numbered from 0 to N.
*/

const char *src_get_name (int converter_type) ;
const char *src_get_description (int converter_type) ;
const char *src_get_version (void) ;

/*
** Set a new SRC ratio. This allows step responses
** in the conversion ratio.
** Returns non zero on error.
*/

int src_set_ratio (SRC_STATE *state, double new_ratio) ;

/*
** Get the current channel count.
** Returns negative on error, positive channel count otherwise
*/

int src_get_channels (SRC_STATE *state) ;

/*
** Reset the internal SRC state.
** Does not modify the quality settings.
** Does not free any memory allocations.
** Returns non zero on error.
*/

int src_reset (SRC_STATE *state) ;

/*
** Return TRUE if ratio is a valid conversion ratio, FALSE
** otherwise.
*/

int src_is_valid_ratio (double ratio) ;

/*
** Return an error number.
*/

int src_error (SRC_STATE *state) ;

/*
** Convert the error number into a string.
*/
const char* src_strerror (int error) ;

/*
** The following enums can be used to set the interpolator type
** using the function src_set_converter().
*/

enum
{
SRC_SINC_BEST_QUALITY = 0,
SRC_SINC_MEDIUM_QUALITY = 1,
SRC_SINC_FASTEST = 2,
SRC_ZERO_ORDER_HOLD = 3,
SRC_LINEAR = 4,
} ;

/*
** Extra helper functions for converting from short to float and
** back again.
*/

void src_short_to_float_array (const short *in, float *out, int len) ;
void src_float_to_short_array (const float *in, short *out, int len) ;

void src_int_to_float_array (const int *in, float *out, int len) ;
void src_float_to_int_array (const float *in, int *out, int len) ;


#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */

#endif /* SAMPLERATE_H */

Loading

0 comments on commit f43a435

Please sign in to comment.