mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
integrate libsamplerate and other audio fixes
This commit is contained in:
@@ -78,6 +78,40 @@ FetchContent_Declare(
|
||||
)
|
||||
FetchContent_MakeAvailable(tomlplusplus)
|
||||
|
||||
# --- libsamplerate ---
|
||||
set(LIBSAMPLERATE_GIT_REV "c96f5e3de9c4488f4e6c97f59f5245f22fda22f7")
|
||||
set(_xemu_saved_build_shared_libs "${BUILD_SHARED_LIBS}")
|
||||
set(_xemu_had_build_shared_libs FALSE)
|
||||
if(DEFINED BUILD_SHARED_LIBS)
|
||||
set(_xemu_had_build_shared_libs TRUE)
|
||||
endif()
|
||||
set(_xemu_saved_build_testing "${BUILD_TESTING}")
|
||||
set(_xemu_had_build_testing FALSE)
|
||||
if(DEFINED BUILD_TESTING)
|
||||
set(_xemu_had_build_testing TRUE)
|
||||
endif()
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
|
||||
set(LIBSAMPLERATE_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
set(LIBSAMPLERATE_INSTALL OFF CACHE BOOL "" FORCE)
|
||||
FetchContent_Declare(
|
||||
libsamplerate
|
||||
GIT_REPOSITORY "https://github.com/libsndfile/libsamplerate.git"
|
||||
GIT_TAG "${LIBSAMPLERATE_GIT_REV}"
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
FetchContent_MakeAvailable(libsamplerate)
|
||||
if(_xemu_had_build_shared_libs)
|
||||
set(BUILD_SHARED_LIBS "${_xemu_saved_build_shared_libs}" CACHE BOOL "" FORCE)
|
||||
else()
|
||||
unset(BUILD_SHARED_LIBS CACHE)
|
||||
endif()
|
||||
if(_xemu_had_build_testing)
|
||||
set(BUILD_TESTING "${_xemu_saved_build_testing}" CACHE BOOL "" FORCE)
|
||||
else()
|
||||
unset(BUILD_TESTING CACHE)
|
||||
endif()
|
||||
|
||||
# --- nv2a_vsh_cpu ---
|
||||
set(NV2A_VSH_CPU_GIT_REV "1115255708c10c4841b65dcd2223262e7a316598")
|
||||
set(nv2a_vsh_cpu_UNIT_TEST OFF CACHE BOOL "" FORCE)
|
||||
@@ -727,7 +761,6 @@ list(APPEND XEMU_CORE_SOURCES
|
||||
"${REPO_ROOT}/ui/vnc-stubs.c"
|
||||
"${REPO_ROOT}/target/i386/kvm/hyperv-stub.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/kvmclock_stub.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/samplerate_stub.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/fast_hash_stub.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/libintl_stub.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/qmp_stub.c"
|
||||
@@ -1006,6 +1039,7 @@ target_include_directories(xemu_core PRIVATE
|
||||
add_dependencies(xemu_core glib_ep)
|
||||
|
||||
target_link_libraries(xemu_core PRIVATE
|
||||
SampleRate::samplerate
|
||||
nv2a_vsh_emulator
|
||||
nv2a_vsh_cpu
|
||||
nv2a_vsh_disassembler
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "samplerate.h"
|
||||
|
||||
/*
|
||||
* Minimal libsamplerate replacement for Android.
|
||||
*
|
||||
* Uses linear interpolation for sample-rate conversion. Quality is lower
|
||||
* than the sinc resampler used on desktop, but the ratio is correctly
|
||||
* applied so voices recorded at rates other than 48 kHz (e.g. 22050 Hz
|
||||
* dialogue in Halo CE) play at the right pitch instead of chipmunk-fast.
|
||||
*
|
||||
* The original stub did: (void)ratio; — i.e. passed samples through
|
||||
* unchanged regardless of the conversion ratio, causing every sub-48 kHz
|
||||
* voice to play back at 48000/source_rate times normal speed.
|
||||
*/
|
||||
|
||||
struct SRC_STATE {
|
||||
src_callback_t cb;
|
||||
void *cb_data;
|
||||
int channels;
|
||||
|
||||
/* Current input block (pointer owned by the callback, not by us). */
|
||||
float *buf;
|
||||
long buf_len; /* frames in buf */
|
||||
|
||||
/* Fractional read position within buf (advances by 1/ratio per output frame). */
|
||||
double buf_pos;
|
||||
};
|
||||
|
||||
SRC_STATE *src_callback_new(src_callback_t cb, int converter_type, int channels,
|
||||
int *error, void *cb_data)
|
||||
{
|
||||
(void)converter_type;
|
||||
if (error) {
|
||||
*error = 0;
|
||||
}
|
||||
SRC_STATE *state = (SRC_STATE *)calloc(1, sizeof(*state));
|
||||
if (!state) {
|
||||
if (error) {
|
||||
*error = -1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
state->cb = cb;
|
||||
state->cb_data = cb_data;
|
||||
state->channels = channels;
|
||||
state->buf = NULL;
|
||||
state->buf_len = 0;
|
||||
state->buf_pos = 0.0;
|
||||
return state;
|
||||
}
|
||||
|
||||
/*
|
||||
* ratio = output_sample_rate / input_sample_rate
|
||||
* > 1 : upsampling (e.g. 22050 -> 48000, ratio ≈ 2.177)
|
||||
* < 1 : downsampling
|
||||
* = 1 : pass-through (still goes through the interpolator for simplicity)
|
||||
*
|
||||
* step = 1/ratio = input frames consumed per output frame produced.
|
||||
*/
|
||||
long src_callback_read(SRC_STATE *state, double ratio, long frames, float *data)
|
||||
{
|
||||
if (!state || !state->cb || !data || frames <= 0 || ratio <= 0.0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const double step = 1.0 / ratio;
|
||||
long out = 0;
|
||||
|
||||
while (out < frames) {
|
||||
long idx = (long)state->buf_pos;
|
||||
|
||||
/* Refill the input buffer when we have consumed it. */
|
||||
if (state->buf == NULL || idx >= state->buf_len) {
|
||||
/*
|
||||
* Carry the fractional overshoot past the end of the old block
|
||||
* into the start of the new block. This preserves continuity
|
||||
* when ratio > 1 (step < 1) and we drain the buffer gradually.
|
||||
*/
|
||||
double carry = (state->buf_len > 0)
|
||||
? state->buf_pos - (double)state->buf_len
|
||||
: 0.0;
|
||||
if (carry < 0.0) {
|
||||
carry = 0.0;
|
||||
}
|
||||
|
||||
float *new_buf = NULL;
|
||||
long got = state->cb(state->cb_data, &new_buf);
|
||||
if (got <= 0 || new_buf == NULL) {
|
||||
break; /* source exhausted */
|
||||
}
|
||||
|
||||
state->buf = new_buf;
|
||||
state->buf_len = got;
|
||||
state->buf_pos = carry;
|
||||
idx = (long)state->buf_pos;
|
||||
|
||||
if (idx >= state->buf_len) {
|
||||
break; /* carry >= new block length — shouldn't happen */
|
||||
}
|
||||
}
|
||||
|
||||
/* Linear interpolation between sample[idx] and sample[idx+1]. */
|
||||
float alpha = (float)(state->buf_pos - (double)idx);
|
||||
long next_idx = idx + 1;
|
||||
|
||||
for (int ch = 0; ch < state->channels; ch++) {
|
||||
float s0 = state->buf[idx * state->channels + ch];
|
||||
float s1 = (next_idx < state->buf_len)
|
||||
? state->buf[next_idx * state->channels + ch]
|
||||
: s0; /* hold last sample at block boundary */
|
||||
data[out * state->channels + ch] = s0 + alpha * (s1 - s0);
|
||||
}
|
||||
|
||||
state->buf_pos += step;
|
||||
out++;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
int src_reset(SRC_STATE *state)
|
||||
{
|
||||
if (state) {
|
||||
state->buf = NULL;
|
||||
state->buf_len = 0;
|
||||
state->buf_pos = 0.0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *src_strerror(int error)
|
||||
{
|
||||
(void)error;
|
||||
return "libsamplerate stub (linear)";
|
||||
}
|
||||
|
||||
void src_float_to_short_array(const float *in, short *out, int len)
|
||||
{
|
||||
if (!in || !out || len <= 0) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < len; ++i) {
|
||||
float v = in[i];
|
||||
if (v > 1.0f) v = 1.0f;
|
||||
else if (v < -1.0f) v = -1.0f;
|
||||
out[i] = (short)(v * 32767.0f);
|
||||
}
|
||||
}
|
||||
+80
-14
@@ -134,6 +134,16 @@ static void throttle(MCPXAPUState *d)
|
||||
queued_bytes = monitor_num_used_bytes(d);
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
/* Android scheduler granularity is often too coarse for the extra
|
||||
* low-watermark pacing below and can make speech sound dragged out.
|
||||
* Keep FIFO backpressure, but let the output callback set the pace.
|
||||
*/
|
||||
d->next_frame_time_us = 0;
|
||||
d->sleep_acc_us += qemu_clock_get_us(QEMU_CLOCK_REALTIME) - start_us;
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (queued_bytes > d->monitor.queued_bytes_low) {
|
||||
int64_t now_us = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
|
||||
if (d->next_frame_time_us == 0 ||
|
||||
@@ -264,14 +274,49 @@ static int getenv_int_clamped(const char *name, int min_value, int max_value,
|
||||
return (int)parsed;
|
||||
}
|
||||
|
||||
static void monitor_hold_last_sample(MCPXAPUState *s, uint8_t *stream, int len)
|
||||
static void monitor_apply_fade_in(uint8_t *stream, int len)
|
||||
{
|
||||
int frame_bytes = sizeof(s->monitor.last_output_sample);
|
||||
int frame_bytes = sizeof(int16_t[2]);
|
||||
int frames = len / frame_bytes;
|
||||
|
||||
if (frames <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
int fade_frames = MIN(frames, 64);
|
||||
int16_t *samples = (int16_t *)stream;
|
||||
for (int i = 0; i < fade_frames; i++) {
|
||||
int gain_num = i;
|
||||
int gain_den = fade_frames - 1;
|
||||
samples[i * 2 + 0] = (int16_t)((samples[i * 2 + 0] * gain_num) / gain_den);
|
||||
samples[i * 2 + 1] = (int16_t)((samples[i * 2 + 1] * gain_num) / gain_den);
|
||||
}
|
||||
}
|
||||
|
||||
static void monitor_fill_underrun(const int16_t start_sample[2], uint8_t *stream,
|
||||
int len)
|
||||
{
|
||||
int frame_bytes = sizeof(int16_t[2]);
|
||||
int frames = len / frame_bytes;
|
||||
int16_t *out = (int16_t *)stream;
|
||||
for (int i = 0; i < frames; i++) {
|
||||
out[i * 2 + 0] = s->monitor.last_output_sample[0];
|
||||
out[i * 2 + 1] = s->monitor.last_output_sample[1];
|
||||
|
||||
if (frames == 1) {
|
||||
out[0] = 0;
|
||||
out[1] = 0;
|
||||
} else if (frames > 1) {
|
||||
int fade_frames = MIN(frames, 64);
|
||||
for (int i = 0; i < fade_frames; i++) {
|
||||
int gain_num = fade_frames - 1 - i;
|
||||
int gain_den = fade_frames - 1;
|
||||
out[i * 2 + 0] =
|
||||
(int16_t)((start_sample[0] * gain_num) / gain_den);
|
||||
out[i * 2 + 1] =
|
||||
(int16_t)((start_sample[1] * gain_num) / gain_den);
|
||||
}
|
||||
if (fade_frames < frames) {
|
||||
memset(stream + (fade_frames * frame_bytes), 0,
|
||||
(frames - fade_frames) * frame_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
int tail_bytes = len - (frames * frame_bytes);
|
||||
@@ -290,11 +335,7 @@ static void monitor_sink_cb(void *opaque, uint8_t *stream, int free_b)
|
||||
}
|
||||
|
||||
int avail = 0;
|
||||
#ifdef __ANDROID__
|
||||
int wait_attempts = 24;
|
||||
#else
|
||||
int wait_attempts = 10;
|
||||
#endif
|
||||
for (int i = 0; i < wait_attempts; i++) {
|
||||
qemu_spin_lock(&s->monitor.fifo_lock);
|
||||
avail = fifo8_num_used(&s->monitor.fifo);
|
||||
@@ -324,8 +365,22 @@ static void monitor_sink_cb(void *opaque, uint8_t *stream, int free_b)
|
||||
copied += chunk_len;
|
||||
}
|
||||
|
||||
if (copied > 0 && s->monitor.resume_fade_pending) {
|
||||
monitor_apply_fade_in(stream, copied);
|
||||
s->monitor.resume_fade_pending = false;
|
||||
}
|
||||
|
||||
if (copied < free_b) {
|
||||
monitor_hold_last_sample(s, stream + copied, free_b - copied);
|
||||
int16_t fill_from[2] = {
|
||||
s->monitor.last_output_sample[0],
|
||||
s->monitor.last_output_sample[1],
|
||||
};
|
||||
if (copied >= sizeof(fill_from)) {
|
||||
memcpy(fill_from, stream + copied - sizeof(fill_from),
|
||||
sizeof(fill_from));
|
||||
}
|
||||
monitor_fill_underrun(fill_from, stream + copied, free_b - copied);
|
||||
s->monitor.resume_fade_pending = true;
|
||||
}
|
||||
|
||||
if (free_b >= sizeof(s->monitor.last_output_sample)) {
|
||||
@@ -347,15 +402,17 @@ static void monitor_init(MCPXAPUState *d)
|
||||
d->monitor.queued_bytes_high = 0;
|
||||
d->monitor.last_output_sample[0] = 0;
|
||||
d->monitor.last_output_sample[1] = 0;
|
||||
d->monitor.resume_fade_pending = false;
|
||||
|
||||
int fifo_frames = 3;
|
||||
int audio_samples = 512;
|
||||
#ifdef __ANDROID__
|
||||
/* Give Android a little more audio headroom to ride out short stalls
|
||||
* without increasing the device callback size again.
|
||||
/* Keep Android closer to the desktop callback size now that voice
|
||||
* resampling is handled by libsamplerate instead of the old stub. The
|
||||
* FIFO still provides extra headroom for short scheduling stalls.
|
||||
*/
|
||||
fifo_frames = 24;
|
||||
audio_samples = 2048;
|
||||
fifo_frames = 16;
|
||||
audio_samples = 512;
|
||||
fifo_frames = getenv_int_clamped("XEMU_ANDROID_AUDIO_FIFO_FRAMES", 3, 32,
|
||||
fifo_frames);
|
||||
audio_samples = getenv_int_clamped("XEMU_ANDROID_AUDIO_SAMPLES", 256, 4096,
|
||||
@@ -409,8 +466,17 @@ static void monitor_init(MCPXAPUState *d)
|
||||
int max_high = MAX(fifo_capacity_bytes - frame_bytes, frame_bytes);
|
||||
d->monitor.fifo_capacity_bytes = fifo_capacity_bytes;
|
||||
d->monitor.device_buffer_bytes = device_buffer_bytes;
|
||||
#ifdef __ANDROID__
|
||||
/* Keep the Android queue short enough to avoid noticeable drift/latency,
|
||||
* but still allow about two callback drains of headroom for scheduler
|
||||
* jitter before backpressuring the APU.
|
||||
*/
|
||||
d->monitor.queued_bytes_high = MIN(2 * drain_bytes, max_high);
|
||||
d->monitor.queued_bytes_low = 0;
|
||||
#else
|
||||
d->monitor.queued_bytes_high = MIN(3 * drain_bytes, max_high);
|
||||
d->monitor.queued_bytes_low = MIN(drain_bytes, d->monitor.queued_bytes_high);
|
||||
#endif
|
||||
|
||||
SDL_PauseAudioDevice(sdl_audio_dev, 0);
|
||||
}
|
||||
|
||||
@@ -103,6 +103,7 @@ typedef struct MCPXAPUState {
|
||||
McpxApuDebugMonitorPoint point;
|
||||
int16_t frame_buf[256][2]; // 1 EP frame (0x400 bytes), 8 buffered
|
||||
int16_t last_output_sample[2];
|
||||
bool resume_fade_pending;
|
||||
QemuSpin fifo_lock;
|
||||
Fifo8 fifo;
|
||||
int fifo_capacity_bytes;
|
||||
|
||||
+73
-20
@@ -1140,6 +1140,7 @@ static long voice_resample_callback(void *cb_data, float **data)
|
||||
uint16_t v = filter->voice;
|
||||
assert(v < MCPX_HW_MAX_VOICES);
|
||||
MCPXAPUState *d = container_of(filter, MCPXAPUState, vp.filters[v]);
|
||||
int channels = filter->resampler_channels ?: 2;
|
||||
|
||||
int sample_count = 0;
|
||||
while (sample_count < NUM_SAMPLES_PER_FRAME) {
|
||||
@@ -1148,9 +1149,20 @@ static long voice_resample_callback(void *cb_data, float **data)
|
||||
if (!active) {
|
||||
break;
|
||||
}
|
||||
int count = voice_get_samples(
|
||||
d, v, (float(*)[2]) & filter->resample_buf[2 * sample_count],
|
||||
NUM_SAMPLES_PER_FRAME - sample_count);
|
||||
int count;
|
||||
if (channels == 1) {
|
||||
count = voice_get_samples(
|
||||
d, v, (float(*)[2]) filter->resample_buf,
|
||||
NUM_SAMPLES_PER_FRAME - sample_count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
filter->mono_resample_buf[sample_count + i] =
|
||||
filter->resample_buf[i * 2];
|
||||
}
|
||||
} else {
|
||||
count = voice_get_samples(
|
||||
d, v, (float(*)[2]) &filter->resample_buf[2 * sample_count],
|
||||
NUM_SAMPLES_PER_FRAME - sample_count);
|
||||
}
|
||||
if (count < 0) {
|
||||
break;
|
||||
}
|
||||
@@ -1159,41 +1171,76 @@ static long voice_resample_callback(void *cb_data, float **data)
|
||||
|
||||
if (sample_count < NUM_SAMPLES_PER_FRAME) {
|
||||
/* Starvation causes SRC hang on repeated calls. Provide silence. */
|
||||
memset(&filter->resample_buf[2*sample_count], 0,
|
||||
2*(NUM_SAMPLES_PER_FRAME-sample_count)*sizeof(float));
|
||||
if (channels == 1) {
|
||||
memset(&filter->mono_resample_buf[sample_count], 0,
|
||||
(NUM_SAMPLES_PER_FRAME - sample_count) * sizeof(float));
|
||||
} else {
|
||||
memset(&filter->resample_buf[2 * sample_count], 0,
|
||||
2 * (NUM_SAMPLES_PER_FRAME - sample_count) * sizeof(float));
|
||||
}
|
||||
sample_count = NUM_SAMPLES_PER_FRAME;
|
||||
}
|
||||
|
||||
*data = filter->resample_buf;
|
||||
*data = channels == 1 ? filter->mono_resample_buf : filter->resample_buf;
|
||||
return sample_count;
|
||||
}
|
||||
|
||||
static int voice_resampler_converter_type(void)
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
/* The desktop sinc converter is expensive enough to drag mobile
|
||||
* frametimes, which in turn makes voice playback sound slow/raspy.
|
||||
* Use libsamplerate's linear converter on Android to keep the ratio
|
||||
* correction from the real library without the full sinc cost.
|
||||
*/
|
||||
return SRC_LINEAR;
|
||||
#else
|
||||
return SRC_SINC_FASTEST;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int voice_resample(MCPXAPUState *d, uint16_t v, float samples[][2],
|
||||
int requested_num, float rate)
|
||||
int requested_num, float rate, int channels)
|
||||
{
|
||||
assert(v < MCPX_HW_MAX_VOICES);
|
||||
MCPXAPUVoiceFilter *filter = &d->vp.filters[v];
|
||||
|
||||
if (filter->resampler && filter->resampler_channels != channels) {
|
||||
src_delete(filter->resampler);
|
||||
filter->resampler = NULL;
|
||||
}
|
||||
|
||||
if (filter->resampler == NULL) {
|
||||
filter->voice = v;
|
||||
filter->resampler_channels = channels;
|
||||
int err;
|
||||
|
||||
/* Note: Using a sinc based resampler for quality. Unsure about
|
||||
* hardware's actual interpolation method; it could just be linear, in
|
||||
* which case using this resampler is overkill, but quality is good
|
||||
* so use it for now.
|
||||
/* Unsure about the hardware's exact interpolation method. Desktop uses
|
||||
* libsamplerate's faster sinc mode for quality; Android uses the
|
||||
* lighter linear converter to keep mobile frametimes stable.
|
||||
*/
|
||||
// FIXME: Don't do 2ch resampling if this is a mono voice
|
||||
filter->resampler = src_callback_new(&voice_resample_callback,
|
||||
SRC_SINC_FASTEST, 2, &err, filter);
|
||||
voice_resampler_converter_type(),
|
||||
channels, &err, filter);
|
||||
if (filter->resampler == NULL) {
|
||||
fprintf(stderr, "src error: %s\n", src_strerror(err));
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
int count = src_callback_read(filter->resampler, rate, requested_num,
|
||||
int count;
|
||||
if (channels == 1) {
|
||||
float mono_samples[NUM_SAMPLES_PER_FRAME];
|
||||
count = src_callback_read(filter->resampler, rate, requested_num,
|
||||
mono_samples);
|
||||
for (int i = 0; i < count; i++) {
|
||||
samples[i][0] = mono_samples[i];
|
||||
samples[i][1] = mono_samples[i];
|
||||
}
|
||||
} else {
|
||||
count = src_callback_read(filter->resampler, rate, requested_num,
|
||||
(float *)samples);
|
||||
}
|
||||
if (count == -1) {
|
||||
DPRINTF("resample error\n");
|
||||
}
|
||||
@@ -1359,7 +1406,8 @@ static void voice_process(MCPXAPUState *d,
|
||||
}
|
||||
int count =
|
||||
voice_resample(d, v, &samples[sample_count],
|
||||
NUM_SAMPLES_PER_FRAME - sample_count, rate);
|
||||
NUM_SAMPLES_PER_FRAME - sample_count, rate,
|
||||
channels);
|
||||
if (count < 0) {
|
||||
break;
|
||||
}
|
||||
@@ -1819,9 +1867,8 @@ static int mcpx_apu_default_vp_worker_count(void)
|
||||
#ifdef __ANDROID__
|
||||
/*
|
||||
* Mobile SoCs are often oversubscribed already (TCG + render + I/O).
|
||||
* Keep VP worker defaults very conservative to reduce thread contention and
|
||||
* leave more CPU time for the emulation threads that gate frametime.
|
||||
* Allow explicit override via env.
|
||||
* Stay under the desktop default, but give the VP path enough parallelism
|
||||
* for libsamplerate-backed voice mixing. Allow explicit override via env.
|
||||
*/
|
||||
const char *value = getenv("XEMU_ANDROID_VP_WORKERS");
|
||||
if (value && value[0] != '\0') {
|
||||
@@ -1832,10 +1879,16 @@ static int mcpx_apu_default_vp_worker_count(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu_count <= 6) {
|
||||
if (cpu_count <= 2) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
if (cpu_count <= 4) {
|
||||
return 2;
|
||||
}
|
||||
if (cpu_count <= 6) {
|
||||
return 3;
|
||||
}
|
||||
return 4;
|
||||
#else
|
||||
return cpu_count;
|
||||
#endif
|
||||
|
||||
@@ -43,7 +43,9 @@ typedef struct MCPXAPUVPSSLData {
|
||||
typedef struct MCPXAPUVoiceFilter {
|
||||
uint16_t voice;
|
||||
float resample_buf[NUM_SAMPLES_PER_FRAME * 2];
|
||||
float mono_resample_buf[NUM_SAMPLES_PER_FRAME];
|
||||
SRC_STATE *resampler;
|
||||
int resampler_channels;
|
||||
sv_filter svf[2];
|
||||
HrtfFilter hrtf;
|
||||
} MCPXAPUVoiceFilter;
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
#ifndef SAMPLERATE_H
|
||||
#define SAMPLERATE_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct SRC_STATE SRC_STATE;
|
||||
|
||||
typedef long (*src_callback_t)(void *cb_data, float **data);
|
||||
|
||||
enum {
|
||||
SRC_SINC_FASTEST = 0,
|
||||
};
|
||||
|
||||
SRC_STATE *src_callback_new(src_callback_t cb, int converter_type, int channels,
|
||||
int *error, void *cb_data);
|
||||
long src_callback_read(SRC_STATE *state, double ratio, long frames, float *data);
|
||||
int src_reset(SRC_STATE *state);
|
||||
const char *src_strerror(int error);
|
||||
|
||||
void src_float_to_short_array(const float *in, short *out, int len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SAMPLERATE_H */
|
||||
Reference in New Issue
Block a user