Use libsamplerate to handle pitch for MojoAL
This commit is contained in:
parent
be7b46f7d9
commit
33459e852c
@ -370,6 +370,15 @@ endif()
|
|||||||
if(NOT SERVER_ONLY)
|
if(NOT SERVER_ONLY)
|
||||||
# OpenAL
|
# OpenAL
|
||||||
if (USE_MOJOAL)
|
if (USE_MOJOAL)
|
||||||
|
find_library(LIBSAMPLERATE_LIBRARY NAMES samplerate libsamplerate)
|
||||||
|
find_path(LIBSAMPLERATE_INCLUDEDIR NAMES samplerate.h PATHS)
|
||||||
|
if (NOT LIBSAMPLERATE_LIBRARY OR NOT LIBSAMPLERATE_INCLUDEDIR)
|
||||||
|
message(FATAL_ERROR "libsamplerate not found. "
|
||||||
|
"libsamplerate is required to handle pitch for MojoAL.")
|
||||||
|
else()
|
||||||
|
include_directories("${LIBSAMPLERATE_INCLUDEDIR}")
|
||||||
|
MESSAGE(STATUS "Use system libsamplerate: ${LIBSAMPLERATE_LIBRARY}")
|
||||||
|
endif()
|
||||||
add_definitions(-DAL_LIBTYPE_STATIC)
|
add_definitions(-DAL_LIBTYPE_STATIC)
|
||||||
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/mojoal")
|
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/mojoal")
|
||||||
include_directories(BEFORE "${PROJECT_SOURCE_DIR}/lib/mojoal")
|
include_directories(BEFORE "${PROJECT_SOURCE_DIR}/lib/mojoal")
|
||||||
@ -642,7 +651,7 @@ endif()
|
|||||||
|
|
||||||
if(NOT SERVER_ONLY)
|
if(NOT SERVER_ONLY)
|
||||||
if (USE_MOJOAL)
|
if (USE_MOJOAL)
|
||||||
target_link_libraries(supertuxkart mojoal)
|
target_link_libraries(supertuxkart mojoal ${LIBSAMPLERATE_LIBRARY})
|
||||||
else()
|
else()
|
||||||
target_link_libraries(supertuxkart ${OPENAL_LIBRARY})
|
target_link_libraries(supertuxkart ${OPENAL_LIBRARY})
|
||||||
endif()
|
endif()
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/alc.h"
|
#include "AL/alc.h"
|
||||||
#include "SDL.h"
|
#include "SDL.h"
|
||||||
|
#include "samplerate.h"
|
||||||
|
|
||||||
#ifdef __SSE__ /* if you are on x86 or x86-64, we assume you have SSE1 by now. */
|
#ifdef __SSE__ /* if you are on x86 or x86-64, we assume you have SSE1 by now. */
|
||||||
#define NEED_SCALAR_FALLBACK 0
|
#define NEED_SCALAR_FALLBACK 0
|
||||||
@ -444,7 +445,7 @@ SIMDALIGNEDSTRUCT ALsource
|
|||||||
ALfloat cone_outer_angle;
|
ALfloat cone_outer_angle;
|
||||||
ALfloat cone_outer_gain;
|
ALfloat cone_outer_gain;
|
||||||
ALbuffer *buffer;
|
ALbuffer *buffer;
|
||||||
SDL_AudioStream *stream; /* for resampling. */
|
SRC_STATE *src_state; /* for resampling. */
|
||||||
BufferQueue buffer_queue;
|
BufferQueue buffer_queue;
|
||||||
BufferQueue buffer_queue_processed;
|
BufferQueue buffer_queue_processed;
|
||||||
ALsizei offset; /* offset in bytes for converted stream! */
|
ALsizei offset; /* offset in bytes for converted stream! */
|
||||||
@ -1171,33 +1172,58 @@ static ALboolean mix_source_buffer(ALCcontext *ctx, ALsource *src, BufferQueueIt
|
|||||||
const int bufferframesize = (int) (buffer->channels * sizeof (float));
|
const int bufferframesize = (int) (buffer->channels * sizeof (float));
|
||||||
const int deviceframesize = ctx->device->framesize;
|
const int deviceframesize = ctx->device->framesize;
|
||||||
const int framesneeded = *len / deviceframesize;
|
const int framesneeded = *len / deviceframesize;
|
||||||
|
const float pitch = src->pitch;
|
||||||
|
|
||||||
SDL_assert(src->offset < buffer->len);
|
SDL_assert(src->offset < buffer->len);
|
||||||
|
|
||||||
if (src->stream) { /* resampling? */
|
/*
|
||||||
int mixframes, mixlen, remainingmixframes;
|
SRC_SINC_BEST_QUALITY
|
||||||
while ( (((mixlen = SDL_AudioStreamAvailable(src->stream)) / bufferframesize) < framesneeded) && (src->offset < buffer->len) ) {
|
SRC_SINC_MEDIUM_QUALITY
|
||||||
const int framesput = (buffer->len - src->offset) / bufferframesize;
|
SRC_SINC_FASTEST
|
||||||
const int bytesput = SDL_min(framesput, 1024) * bufferframesize;
|
SRC_ZERO_ORDER_HOLD
|
||||||
FIXME("dynamically adjust frames here?"); /* we hardcode 1024 samples when opening the audio device, too. */
|
SRC_LINEAR
|
||||||
SDL_AudioStreamPut(src->stream, data, bytesput);
|
*/
|
||||||
src->offset += bytesput;
|
const int converter = SRC_LINEAR;
|
||||||
data += bytesput / sizeof (float);
|
int src_error = 0;
|
||||||
}
|
if ((pitch != 1.0f || ctx->device->frequency != buffer->frequency) && src->src_state == NULL) {
|
||||||
|
src->src_state = src_new(converter, buffer->channels, &src_error);
|
||||||
mixframes = SDL_min(mixlen / bufferframesize, framesneeded);
|
if (src->src_state == NULL)
|
||||||
remainingmixframes = mixframes;
|
fprintf(stderr, "src_new error: %s\n", src_strerror(src_error));
|
||||||
while (remainingmixframes > 0) {
|
}
|
||||||
float mixbuf[256];
|
else if (pitch == 1.0f && ctx->device->frequency == buffer->frequency && src->src_state != NULL) {
|
||||||
const int mixbuflen = sizeof (mixbuf);
|
src_delete(src->src_state);
|
||||||
const int mixbufframes = mixbuflen / bufferframesize;
|
src->src_state = NULL;
|
||||||
const int getframes = SDL_min(remainingmixframes, mixbufframes);
|
}
|
||||||
SDL_AudioStreamGet(src->stream, mixbuf, getframes * bufferframesize);
|
else if (src->src_state != NULL && src_get_channels(src->src_state) != buffer->channels) {
|
||||||
mix_buffer(buffer, src->panning, mixbuf, *stream, getframes);
|
src_delete(src->src_state);
|
||||||
*len -= getframes * deviceframesize;
|
src->src_state = src_new(converter, buffer->channels, &src_error);
|
||||||
*stream += getframes * ctx->device->channels;
|
if (src->src_state == NULL)
|
||||||
remainingmixframes -= getframes;
|
fprintf(stderr, "src_new error: %s\n", src_strerror(src_error));
|
||||||
}
|
}
|
||||||
|
if (src->src_state) { /* resampling? */
|
||||||
|
int used_frame = 0;
|
||||||
|
do {
|
||||||
|
SRC_DATA src_data = {};
|
||||||
|
src_data.input_frames = (buffer->len - src->offset) / buffer->channels / sizeof (float);
|
||||||
|
src_data.output_frames = framesneeded - used_frame;
|
||||||
|
if (src_data.output_frames <= 0 || src->offset >= buffer->len)
|
||||||
|
break;
|
||||||
|
src_data.data_in = data;
|
||||||
|
float *data_out = (float *) alloca((framesneeded - used_frame) * buffer->channels * sizeof (float));
|
||||||
|
src_data.data_out = data_out;
|
||||||
|
src_data.src_ratio = (double) ctx->device->frequency / (double) buffer->frequency / pitch;
|
||||||
|
int ret = src_process(src->src_state, &src_data);
|
||||||
|
if (ret != 0) {
|
||||||
|
fprintf(stderr, "src_process error: %s\n", src_strerror(ret));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mix_buffer(buffer, src->panning, src_data.data_out, *stream, src_data.output_frames_gen);
|
||||||
|
*len -= src_data.output_frames_gen * deviceframesize;
|
||||||
|
*stream += src_data.output_frames_gen * ctx->device->channels;
|
||||||
|
src->offset += src_data.input_frames_used * buffer->channels * sizeof (float);
|
||||||
|
used_frame += src_data.output_frames_gen;
|
||||||
|
data += src_data.input_frames_used * buffer->channels;
|
||||||
|
} while (*len > 0);
|
||||||
} else {
|
} else {
|
||||||
const int framesavail = (buffer->len - src->offset) / bufferframesize;
|
const int framesavail = (buffer->len - src->offset) / bufferframesize;
|
||||||
const int mixframes = SDL_min(framesneeded, framesavail);
|
const int mixframes = SDL_min(framesneeded, framesavail);
|
||||||
@ -2035,7 +2061,8 @@ static void _alcDestroyContext(ALCcontext *ctx)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_FreeAudioStream(src->stream);
|
if (src->src_state)
|
||||||
|
src_delete(src->src_state);
|
||||||
source_release_buffer_queue(ctx, src);
|
source_release_buffer_queue(ctx, src);
|
||||||
if (--sb->used == 0) {
|
if (--sb->used == 0) {
|
||||||
break;
|
break;
|
||||||
@ -3415,9 +3442,9 @@ static void _alDeleteSources(const ALsizei n, const ALuint *names)
|
|||||||
(void) SDL_AtomicDecRef(&source->buffer->refcount);
|
(void) SDL_AtomicDecRef(&source->buffer->refcount);
|
||||||
source->buffer = NULL;
|
source->buffer = NULL;
|
||||||
}
|
}
|
||||||
if (source->stream) {
|
if (source->src_state) {
|
||||||
SDL_FreeAudioStream(source->stream);
|
src_delete(source->src_state);
|
||||||
source->stream = NULL;
|
source->src_state = NULL;
|
||||||
}
|
}
|
||||||
block->used--;
|
block->used--;
|
||||||
}
|
}
|
||||||
@ -3448,7 +3475,16 @@ static void _alSourcefv(const ALuint name, const ALenum param, const ALfloat *va
|
|||||||
case AL_REFERENCE_DISTANCE: src->reference_distance = *values; break;
|
case AL_REFERENCE_DISTANCE: src->reference_distance = *values; break;
|
||||||
case AL_ROLLOFF_FACTOR: src->rolloff_factor = *values; break;
|
case AL_ROLLOFF_FACTOR: src->rolloff_factor = *values; break;
|
||||||
case AL_MAX_DISTANCE: src->max_distance = *values; break;
|
case AL_MAX_DISTANCE: src->max_distance = *values; break;
|
||||||
case AL_PITCH: src->pitch = *values; break;
|
case AL_PITCH:
|
||||||
|
{
|
||||||
|
float pitch = *values;
|
||||||
|
if (pitch < 0.5f)
|
||||||
|
pitch = 0.5f;
|
||||||
|
else if (pitch > 2.0f)
|
||||||
|
pitch = 2.0f;
|
||||||
|
src->pitch = pitch;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case AL_CONE_INNER_ANGLE: src->cone_inner_angle = *values; break;
|
case AL_CONE_INNER_ANGLE: src->cone_inner_angle = *values; break;
|
||||||
case AL_CONE_OUTER_ANGLE: src->cone_outer_angle = *values; break;
|
case AL_CONE_OUTER_ANGLE: src->cone_outer_angle = *values; break;
|
||||||
case AL_CONE_OUTER_GAIN: src->cone_outer_gain = *values; break;
|
case AL_CONE_OUTER_GAIN: src->cone_outer_gain = *values; break;
|
||||||
@ -3517,18 +3553,6 @@ static void set_source_static_buffer(ALCcontext *ctx, ALsource *src, const ALuin
|
|||||||
set_al_error(ctx, AL_INVALID_VALUE);
|
set_al_error(ctx, AL_INVALID_VALUE);
|
||||||
} else {
|
} else {
|
||||||
const ALboolean must_lock = SDL_AtomicGet(&src->mixer_accessible) ? AL_TRUE : AL_FALSE;
|
const ALboolean must_lock = SDL_AtomicGet(&src->mixer_accessible) ? AL_TRUE : AL_FALSE;
|
||||||
SDL_AudioStream *stream = NULL;
|
|
||||||
SDL_AudioStream *freestream = NULL;
|
|
||||||
/* We only use the stream for resampling, not for channel conversion. */
|
|
||||||
FIXME("keep the existing stream if formats match?");
|
|
||||||
if (buffer && (ctx->device->frequency != buffer->frequency)) {
|
|
||||||
stream = SDL_NewAudioStream(AUDIO_F32SYS, buffer->channels, buffer->frequency, AUDIO_F32SYS, buffer->channels, ctx->device->frequency);
|
|
||||||
if (!stream) {
|
|
||||||
set_al_error(ctx, AL_OUT_OF_MEMORY);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
FIXME("need a way to prealloc space in the stream, so the mixer doesn't have to malloc");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* this can happen if you alSource(AL_BUFFER) while the exact source is in the middle of mixing */
|
/* this can happen if you alSource(AL_BUFFER) while the exact source is in the middle of mixing */
|
||||||
FIXME("Double-check this lock; we shouldn't be able to reach this if the source is playing.");
|
FIXME("Double-check this lock; we shouldn't be able to reach this if the source is playing.");
|
||||||
@ -3552,18 +3576,13 @@ static void set_source_static_buffer(ALCcontext *ctx, ALsource *src, const ALuin
|
|||||||
|
|
||||||
source_release_buffer_queue(ctx, src);
|
source_release_buffer_queue(ctx, src);
|
||||||
|
|
||||||
if (src->stream != stream) {
|
if (src->src_state) {
|
||||||
freestream = src->stream; /* free this after unlocking. */
|
src_reset(src->src_state);
|
||||||
src->stream = stream;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (must_lock) {
|
if (must_lock) {
|
||||||
SDL_UnlockMutex(ctx->source_lock);
|
SDL_UnlockMutex(ctx->source_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (freestream) {
|
|
||||||
SDL_FreeAudioStream(freestream);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4052,7 +4071,6 @@ static void _alSourceQueueBuffers(const ALuint name, const ALsizei nb, const ALu
|
|||||||
ALint queue_channels = 0;
|
ALint queue_channels = 0;
|
||||||
ALsizei queue_frequency = 0;
|
ALsizei queue_frequency = 0;
|
||||||
ALboolean failed = AL_FALSE;
|
ALboolean failed = AL_FALSE;
|
||||||
SDL_AudioStream *stream = NULL;
|
|
||||||
|
|
||||||
if (!src) {
|
if (!src) {
|
||||||
return;
|
return;
|
||||||
@ -4129,16 +4147,6 @@ static void _alSourceQueueBuffers(const ALuint name, const ALsizei nb, const ALu
|
|||||||
|
|
||||||
if (!src->queue_frequency) {
|
if (!src->queue_frequency) {
|
||||||
SDL_assert(!src->queue_channels);
|
SDL_assert(!src->queue_channels);
|
||||||
SDL_assert(!src->stream);
|
|
||||||
/* We only use the stream for resampling, not for channel conversion. */
|
|
||||||
if (ctx->device->frequency != queue_frequency) {
|
|
||||||
stream = SDL_NewAudioStream(AUDIO_F32SYS, queue_channels, queue_frequency, AUDIO_F32SYS, queue_channels, ctx->device->frequency);
|
|
||||||
if (!stream) {
|
|
||||||
set_al_error(ctx, AL_OUT_OF_MEMORY);
|
|
||||||
failed = AL_TRUE;
|
|
||||||
}
|
|
||||||
FIXME("need a way to prealloc space in the stream, so the mixer doesn't have to malloc");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (failed) {
|
if (failed) {
|
||||||
@ -4155,9 +4163,6 @@ static void _alSourceQueueBuffers(const ALuint name, const ALsizei nb, const ALu
|
|||||||
queueend->next = ctx->device->playback.buffer_queue_pool;
|
queueend->next = ctx->device->playback.buffer_queue_pool;
|
||||||
ctx->device->playback.buffer_queue_pool = queue;
|
ctx->device->playback.buffer_queue_pool = queue;
|
||||||
}
|
}
|
||||||
if (stream) {
|
|
||||||
SDL_FreeAudioStream(stream);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4169,7 +4174,6 @@ static void _alSourceQueueBuffers(const ALuint name, const ALsizei nb, const ALu
|
|||||||
if (!src->queue_channels) {
|
if (!src->queue_channels) {
|
||||||
src->queue_channels = queue_channels;
|
src->queue_channels = queue_channels;
|
||||||
src->queue_frequency = queue_frequency;
|
src->queue_frequency = queue_frequency;
|
||||||
src->stream = stream;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* so we're going to put these on a linked list called just_queued,
|
/* so we're going to put these on a linked list called just_queued,
|
||||||
|
Loading…
Reference in New Issue
Block a user