diff options
author | Paul Adenot <[email protected]> | 2023-08-25 10:30:50 -0400 |
---|---|---|
committer | Paul Adenot <[email protected]> | 2023-08-25 20:09:58 +0200 |
commit | ed468c2a472c362b7fb9c80c25ed35ca987f0d76 (patch) | |
tree | 8591bf127fff895c469e226486965e196a3ed8ce | |
parent | cd4effa91a990c3bfa7ffbb7fd0aa0438b82d6ed (diff) | |
download | cubeb-ed468c2a472c362b7fb9c80c25ed35ca987f0d76.tar.gz cubeb-ed468c2a472c362b7fb9c80c25ed35ca987f0d76.zip |
Refactor conversion and format setting logic
-rw-r--r-- | src/cubeb_opensl.cpp | 264 |
1 files changed, 153 insertions, 111 deletions
diff --git a/src/cubeb_opensl.cpp b/src/cubeb_opensl.cpp index 7445b05..c5a2543 100644 --- a/src/cubeb_opensl.cpp +++ b/src/cubeb_opensl.cpp @@ -260,6 +260,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) { @@ -276,9 +306,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_original_ptr = 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); @@ -287,31 +318,18 @@ 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_original_ptr, sample_count); ALOGV("bufferqueue_callback: resampler fill returned %ld frames", written); if (written < 0 || written * stm->framesize > static_cast<uint32_t>(stm->queuebuf_len)) { + ALOGV("bufferqueue_callback: error, shutting down", written); r = pthread_mutex_lock(&stm->mutex); assert(r == 0); opensl_set_shutdown(stm, 1); @@ -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); @@ -890,10 +908,49 @@ opensl_set_format(SLDataFormat_PCM * format, cubeb_stream_params * params) case CUBEB_SAMPLE_FLOAT32BE: format->endianness = SL_BYTEORDER_BIGENDIAN; break; + default: + assert(false && "unhandled value"); } return CUBEB_OK; } +template <typename Function> +int +initialize_with_format(cubeb_stream * stm, cubeb_stream_params * params, + Function func) +{ + void * format = nullptr; + bool using_floats = false; + uint32_t * format_sample_rate; +#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; + } + + func(format, format_sample_rate, using_floats); + + return CUBEB_OK; +} + static int opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params) { @@ -1105,102 +1162,86 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) stm->lastPositionTimeStamp = 0; stm->lastCompensativePosition = -1; - 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("Output stream configured for using float, but not supported: a " - "conversion will be performed"); - stm->conversion_buffer_output.resize(1); - } - - if (!using_floats) { - stm->framesize = params->channels * sizeof(int16_t); - } else { - stm->framesize = params->channels * sizeof(float); - } - - SLDataLocator_BufferQueue loc_bufq; - loc_bufq.locatorType = SL_DATALOCATOR_BUFFERQUEUE; - loc_bufq.numBuffers = NBUFS; - SLDataSource source; - source.pLocator = &loc_bufq; - source.pFormat = format; - - SLDataLocator_OutputMix loc_outmix; - loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; - loc_outmix.outputMix = stm->context->outmixObj; - SLDataSink sink; - sink.pLocator = &loc_outmix; - sink.pFormat = nullptr; + int rv = initialize_with_format( + stm, params, + [=](void * format, uint32_t * format_sample_rate, bool using_floats) { + SLDataLocator_BufferQueue loc_bufq; + loc_bufq.locatorType = SL_DATALOCATOR_BUFFERQUEUE; + loc_bufq.numBuffers = NBUFS; + SLDataSource source; + source.pLocator = &loc_bufq; + source.pFormat = format; + SLDataLocator_OutputMix loc_outmix; + loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; + loc_outmix.outputMix = stm->context->outmixObj; + + SLDataSink sink; + sink.pLocator = &loc_outmix; + sink.pFormat = nullptr; #if defined(__ANDROID__) - const SLInterfaceID ids[] = {stm->context->SL_IID_BUFFERQUEUE, - stm->context->SL_IID_VOLUME, - stm->context->SL_IID_ANDROIDCONFIGURATION}; - const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; + const SLInterfaceID ids[] = {stm->context->SL_IID_BUFFERQUEUE, + stm->context->SL_IID_VOLUME, + stm->context->SL_IID_ANDROIDCONFIGURATION}; + const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, + SL_BOOLEAN_TRUE}; #else - const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE, ctx->SL_IID_VOLUME}; - const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; + const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE, + ctx->SL_IID_VOLUME}; + const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; #endif - assert(NELEMS(ids) == NELEMS(req)); - - uint32_t preferred_sampling_rate = stm->user_output_rate; - SLresult res = SL_RESULT_CONTENT_UNSUPPORTED; - if (preferred_sampling_rate) { - res = (*stm->context->eng) - ->CreateAudioPlayer(stm->context->eng, &stm->playerObj, &source, - &sink, NELEMS(ids), ids, req); - } - - // Sample rate not supported? Try again with primary sample rate! - if (res == SL_RESULT_CONTENT_UNSUPPORTED && - preferred_sampling_rate != DEFAULT_SAMPLE_RATE) { - preferred_sampling_rate = DEFAULT_SAMPLE_RATE; - *format_sample_rate = preferred_sampling_rate * 1000; - res = (*stm->context->eng) - ->CreateAudioPlayer(stm->context->eng, &stm->playerObj, &source, - &sink, NELEMS(ids), ids, req); + assert(NELEMS(ids) == NELEMS(req)); + + uint32_t preferred_sampling_rate = stm->user_output_rate; + SLresult res = SL_RESULT_CONTENT_UNSUPPORTED; + if (preferred_sampling_rate) { + res = (*stm->context->eng) + ->CreateAudioPlayer(stm->context->eng, &stm->playerObj, + &source, &sink, NELEMS(ids), ids, req); + } + + // Sample rate not supported? Try again with primary sample rate! + if (res == SL_RESULT_CONTENT_UNSUPPORTED && + preferred_sampling_rate != DEFAULT_SAMPLE_RATE) { + preferred_sampling_rate = DEFAULT_SAMPLE_RATE; + *format_sample_rate = preferred_sampling_rate * 1000; + res = (*stm->context->eng) + ->CreateAudioPlayer(stm->context->eng, &stm->playerObj, + &source, &sink, NELEMS(ids), ids, req); + } + + if (res != SL_RESULT_SUCCESS) { + LOG("Failed to create audio player. Error code: %lu", res); + return CUBEB_ERROR; + } + stm->output_configured_rate = preferred_sampling_rate; + + // 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_output.resize(1); + } + + if (!using_floats) { + stm->framesize = params->channels * sizeof(int16_t); + } else { + stm->framesize = params->channels * sizeof(float); + } + return CUBEB_OK; + }); + + if (rv != CUBEB_OK) { + LOG("Couldn't set format on sink or source"); + return rv; } - if (res != SL_RESULT_SUCCESS) { - LOG("Failed to create audio player. Error code: %lu", res); - return CUBEB_ERROR; - } - - stm->output_configured_rate = preferred_sampling_rate; stm->bytespersec = stm->output_configured_rate * stm->framesize; stm->queuebuf_len = stm->framesize * stm->buffer_size_frames; @@ -1215,6 +1256,7 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) SLAndroidConfigurationItf playerConfig = nullptr; + SLresult res; if (get_android_version() >= ANDROID_VERSION_N_MR1) { res = (*stm->playerObj) ->GetInterface(stm->playerObj, |