Skip to content

Commit

Permalink
Refactor conversion buffer, handle input
Browse files Browse the repository at this point in the history
  • Loading branch information
padenot committed Aug 22, 2023
1 parent d11bdab commit 1e46027
Showing 1 changed file with 125 additions and 30 deletions.
155 changes: 125 additions & 30 deletions src/cubeb_opensl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ struct cubeb_stream {
std::unique_ptr<cubeb_stream_params> output_params;
// A non-empty buffer means that f32 -> int16 conversion need to happen
std::vector<float> conversion_buffer_output;
std::vector<float> conversion_buffer_input;
};

/* Forward declaration. */
Expand Down Expand Up @@ -260,6 +261,36 @@ recorder_marker_callback(SLRecordItf caller, void * pContext, SLuint32 event)
}
}

// Returns a buffer suitable to write output data to.
void *
get_output_buffer(cubeb_stream * stm, void * output_buffer,
uint32_t sample_count)
{
if (stm->conversion_buffer_output.empty()) {
return output_buffer;
}
if (stm->conversion_buffer_output.size() < sample_count) {
stm->conversion_buffer_output.resize(sample_count);
}
return stm->conversion_buffer_output.data();
}

void *
release_output_buffer(cubeb_stream * stm, void * original_output_buffer,
uint32_t sample_count)
{
if (stm->conversion_buffer_output.empty()) {
return original_output_buffer;
}
int16_t * int16_buf = reinterpret_cast<int16_t *>(original_output_buffer);
for (uint32_t i = 0; i < sample_count; i++) {
float v = stm->conversion_buffer_output[i] * 32768.0f;
float clamped = std::max(-32768.0f, std::min(32767.0f, v));
int16_buf[i] = static_cast<int16_t>(clamped);
}
return original_output_buffer;
}

static void
bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
{
Expand All @@ -276,9 +307,10 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
return;
}

uint8_t * buf = reinterpret_cast<uint8_t *>(stm->queuebuf[stm->queuebuf_idx]);
uint8_t * buf_back = buf;
uint32_t sample_count = 0;
void * buf = stm->queuebuf[stm->queuebuf_idx];
void * buf_back = buf;
uint32_t sample_count =
stm->output_params->channels * stm->queuebuf_len / stm->framesize;
written = 0;
int r = pthread_mutex_lock(&stm->mutex);
assert(r == 0);
Expand All @@ -287,27 +319,13 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
r = pthread_mutex_unlock(&stm->mutex);
assert(r == 0);
if (!draining && !shutdown) {
if (!stm->conversion_buffer_output.empty()) {
sample_count =
stm->output_params->channels * stm->queuebuf_len / stm->framesize;
if (stm->conversion_buffer_output.size() < sample_count) {
stm->conversion_buffer_output.resize(sample_count);
}
buf = reinterpret_cast<uint8_t *>(stm->conversion_buffer_output.data());
}

buf = get_output_buffer(stm, buf, sample_count);

written = cubeb_resampler_fill(stm->resampler, nullptr, nullptr, buf,
stm->queuebuf_len / stm->framesize);

if (!stm->conversion_buffer_output.empty()) {
int16_t * buf_int16 = reinterpret_cast<int16_t *>(buf_back);
for (uint32_t i = 0; i < sample_count; i++) {
float v = stm->conversion_buffer_output[i] * 32768.0f;
float clamped = std::max(-32768.0f, std::min(32767.0f, v));
buf_int16[i] = static_cast<int16_t>(clamped);
}
buf = buf_back;
}
buf = release_output_buffer(stm, buf_back, sample_count);

ALOGV("bufferqueue_callback: resampler fill returned %ld frames", written);
if (written < 0 ||
Expand All @@ -326,7 +344,7 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
// Keep sending silent data even in draining mode to prevent the audio
// back-end from being stopped automatically by OpenSL/ES.
assert(static_cast<uint32_t>(stm->queuebuf_len) >= written * stm->framesize);
memset(buf + written * stm->framesize, 0,
memset(reinterpret_cast<uint8_t *>(buf) + written * stm->framesize, 0,
stm->queuebuf_len - written * stm->framesize);
res = (*stm->bufq)->Enqueue(stm->bufq, buf, stm->queuebuf_len);
assert(res == SL_RESULT_SUCCESS);
Expand Down Expand Up @@ -399,6 +417,26 @@ opensl_enqueue_recorder(cubeb_stream * stm, void ** last_filled_buffer)
return CUBEB_OK;
}

// If necessary, convert and returns an input buffer.
// Otherwise, just returns the pointer that has been passed in.
void *
convert_input_buffer_if_needed(cubeb_stream * stm, void * input_buffer,
uint32_t sample_count)
{
// Perform conversion if needed
if (stm->conversion_buffer_input.empty()) {
return input_buffer;
}
if (stm->conversion_buffer_input.size() < sample_count) {
stm->conversion_buffer_input.resize(sample_count);
}
int16_t * int16_buf = reinterpret_cast<int16_t *>(input_buffer);
for (uint32_t i = 0; i < sample_count; i++) {
stm->conversion_buffer_input[i] = int16_buf[i] / 32768.f;
}
return stm->conversion_buffer_input.data();
}

// input data callback
void
recorder_callback(SLAndroidSimpleBufferQueueItf bq, void * context)
Expand Down Expand Up @@ -429,8 +467,14 @@ recorder_callback(SLAndroidSimpleBufferQueueItf bq, void * context)
r = opensl_enqueue_recorder(stm, &input_buffer);
assert(r == CUBEB_OK);
assert(input_buffer);
// Fill resampler with last input

long input_frame_count = stm->input_buffer_length / stm->input_frame_size;
uint32_t sample_count = input_frame_count * stm->input_params->channels;

input_buffer =
convert_input_buffer_if_needed(stm, input_buffer, sample_count);

// Fill resampler with last input
long got = cubeb_resampler_fill(stm->resampler, input_buffer,
&input_frame_count, nullptr, 0);
// Error case
Expand Down Expand Up @@ -524,6 +568,7 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr)
r = pthread_mutex_lock(&stm->mutex);
assert(r == 0);
output_buffer = stm->queuebuf[stm->queuebuf_idx];
void * output_buffer_bck = output_buffer;
// Advance the output buffer queue index
stm->queuebuf_idx = (stm->queuebuf_idx + 1) % stm->queuebuf_capacity;
r = pthread_mutex_unlock(&stm->mutex);
Expand All @@ -543,18 +588,27 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr)
// Get input.
void * input_buffer = array_queue_pop(stm->input_queue);
long input_frame_count = stm->input_buffer_length / stm->input_frame_size;
long sample_count = input_frame_count * stm->input_params->channels;
long frames_needed = stm->queuebuf_len / stm->framesize;

if (!input_buffer) {
LOG("Input hole set silent input buffer");
input_buffer = stm->input_silent_buffer;
}

input_buffer =
convert_input_buffer_if_needed(stm, input_buffer, sample_count);

output_buffer = get_output_buffer(stm, output_buffer, sample_count);

long written = 0;
// Trigger user callback through resampler
written =
cubeb_resampler_fill(stm->resampler, input_buffer, &input_frame_count,
output_buffer, frames_needed);

output_buffer = release_output_buffer(stm, output_buffer_bck, sample_count);

LOG("Fill: written %ld, frames_needed %ld, input array size %zu", written,
frames_needed, array_queue_get_size(stm->input_queue));

Expand Down Expand Up @@ -786,8 +840,8 @@ opensl_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
{
assert(ctx && max_channels);
/* The android mixer handles up to two channels, see
http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67
*/
http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67
*/
*max_channels = 2;

return CUBEB_OK;
Expand Down Expand Up @@ -904,18 +958,59 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
lDataLocatorOut.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
lDataLocatorOut.numBuffers = NBUFS;

SLDataFormat_PCM lDataFormat;
int r = opensl_set_format(&lDataFormat, params);
if (r != CUBEB_OK) {
return CUBEB_ERROR_INVALID_FORMAT;
void * format = nullptr;
SLuint32 * format_sample_rate = nullptr;
bool using_floats = false;

#if defined(__ANDROID__) && (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP)
SLAndroidDataFormat_PCM_EX pcm_ext_format;
if (get_android_version() >= ANDROID_VERSION_LOLLIPOP) {
if (opensl_set_format_ext(&pcm_ext_format, params) != CUBEB_OK) {
LOG("opensl_set_format_ext: error, exiting");
return CUBEB_ERROR_INVALID_FORMAT;
}
format = &pcm_ext_format;
format_sample_rate = &pcm_ext_format.sampleRate;
using_floats =
pcm_ext_format.representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT;
}
#endif

SLDataFormat_PCM pcm_format;
if (!format) {
if (opensl_set_format(&pcm_format, params) != CUBEB_OK) {
LOG("opensl_set_format: error, exiting");
return CUBEB_ERROR_INVALID_FORMAT;
}
format = &pcm_format;
format_sample_rate = &pcm_format.samplesPerSec;
}

// It's always possible to use int16 regardless of the Android version.
// However if compiling for older Android version, it's possible to request
// f32 audio, but Android only supports int16, in which case a conversion need
// to happen.
if ((params->format == CUBEB_SAMPLE_FLOAT32NE ||
params->format == CUBEB_SAMPLE_FLOAT32BE) &&
!using_floats) {
// setup conversion from f32 to int16
LOG("Input stream configured for using float, but not supported: a "
"conversion will be performed");
stm->conversion_buffer_input.resize(1);
}

if (!using_floats) {
stm->framesize = params->channels * sizeof(int16_t);
} else {
stm->framesize = params->channels * sizeof(float);
}

/* For now set device rate to params rate. */
stm->input_device_rate = params->rate;

SLDataSink lDataSink;
lDataSink.pLocator = &lDataLocatorOut;
lDataSink.pFormat = &lDataFormat;
lDataSink.pFormat = format;

SLDataLocator_IODevice lDataLocatorIn;
lDataLocatorIn.locatorType = SL_DATALOCATOR_IODEVICE;
Expand Down Expand Up @@ -951,7 +1046,7 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
// The default rate expected to be supported from all android devices.
stm->input_device_rate = DEFAULT_SAMPLE_RATE;
}
lDataFormat.samplesPerSec = stm->input_device_rate * 1000;
*format_sample_rate = stm->input_device_rate * 1000;
res = (*stm->context->eng)
->CreateAudioRecorder(stm->context->eng, &stm->recorderObj,
&lDataSource, &lDataSink,
Expand Down

0 comments on commit 1e46027

Please sign in to comment.