summaryrefslogtreecommitdiffstats
path: root/modules/audio_remote_submix/audio_hw.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/audio_remote_submix/audio_hw.cpp')
-rw-r--r--modules/audio_remote_submix/audio_hw.cpp254
1 files changed, 211 insertions, 43 deletions
diff --git a/modules/audio_remote_submix/audio_hw.cpp b/modules/audio_remote_submix/audio_hw.cpp
index d7e422b..82aadb5 100644
--- a/modules/audio_remote_submix/audio_hw.cpp
+++ b/modules/audio_remote_submix/audio_hw.cpp
@@ -67,18 +67,36 @@ namespace android {
// See NBAIO_Format frameworks/av/include/media/nbaio/NBAIO.h.
#define DEFAULT_FORMAT AUDIO_FORMAT_PCM_16_BIT
+// Set *result_variable_ptr to true if value_to_find is present in the array array_to_search,
+// otherwise set *result_variable_ptr to false.
+#define SUBMIX_VALUE_IN_SET(value_to_find, array_to_search, result_variable_ptr) \
+ { \
+ size_t i; \
+ *(result_variable_ptr) = false; \
+ for (i = 0; i < sizeof(array_to_search) / sizeof((array_to_search)[0]); i++) { \
+ if ((value_to_find) == (array_to_search)[i]) { \
+ *(result_variable_ptr) = true; \
+ break; \
+ } \
+ } \
+ }
+
// Configuration of the submix pipe.
struct submix_config {
- audio_format_t format;
- audio_channel_mask_t channel_mask;
- unsigned int sample_rate; // Sample rate for the device in Hz.
+ // Channel mask field in this data structure is set to either input_channel_mask or
+ // output_channel_mask depending upon the last stream to be opened on this device.
+ struct audio_config common;
+ // Input stream and output stream channel masks. This is required since input and output
+ // channel bitfields are not equivalent.
+ audio_channel_mask_t input_channel_mask;
+ audio_channel_mask_t output_channel_mask;
size_t period_size; // Size of the audio pipe is period_size * period_count in frames.
};
struct submix_audio_device {
struct audio_hw_device device;
- bool output_standby;
bool input_standby;
+ bool output_standby;
submix_config config;
// Pipe variables: they handle the ring buffer that "pipes" audio:
// - from the submix virtual audio output == what needs to be played
@@ -112,6 +130,69 @@ struct submix_stream_in {
int64_t read_counter_frames;
};
+// Determine whether the specified sample rate is supported by the submix module.
+static bool sample_rate_supported(const uint32_t sample_rate)
+{
+ // Set of sample rates supported by Format_from_SR_C() frameworks/av/media/libnbaio/NAIO.cpp.
+ static const unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
+ };
+ bool return_value;
+ SUBMIX_VALUE_IN_SET(sample_rate, supported_sample_rates, &return_value);
+ return return_value;
+}
+
+// Determine whether the specified sample rate is supported, if it is return the specified sample
+// rate, otherwise return the default sample rate for the submix module.
+static uint32_t get_supported_sample_rate(uint32_t sample_rate)
+{
+ return sample_rate_supported(sample_rate) ? sample_rate : DEFAULT_SAMPLE_RATE_HZ;
+}
+
+// Determine whether the specified channel in mask is supported by the submix module.
+static bool channel_in_mask_supported(const audio_channel_mask_t channel_in_mask)
+{
+ // Set of channel in masks supported by Format_from_SR_C()
+ // frameworks/av/media/libnbaio/NAIO.cpp.
+ static const audio_channel_mask_t supported_channel_in_masks[] = {
+ AUDIO_CHANNEL_IN_MONO, AUDIO_CHANNEL_IN_STEREO,
+ };
+ bool return_value;
+ SUBMIX_VALUE_IN_SET(channel_in_mask, supported_channel_in_masks, &return_value);
+ return return_value;
+}
+
+// Determine whether the specified channel in mask is supported, if it is return the specified
+// channel in mask, otherwise return the default channel in mask for the submix module.
+static audio_channel_mask_t get_supported_channel_in_mask(
+ const audio_channel_mask_t channel_in_mask)
+{
+ return channel_in_mask_supported(channel_in_mask) ? channel_in_mask :
+ static_cast<audio_channel_mask_t>(AUDIO_CHANNEL_IN_STEREO);
+}
+
+// Determine whether the specified channel out mask is supported by the submix module.
+static bool channel_out_mask_supported(const audio_channel_mask_t channel_out_mask)
+{
+ // Set of channel out masks supported by Format_from_SR_C()
+ // frameworks/av/media/libnbaio/NAIO.cpp.
+ static const audio_channel_mask_t supported_channel_out_masks[] = {
+ AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_STEREO,
+ };
+ bool return_value;
+ SUBMIX_VALUE_IN_SET(channel_out_mask, supported_channel_out_masks, &return_value);
+ return return_value;
+}
+
+// Determine whether the specified channel out mask is supported, if it is return the specified
+// channel out mask, otherwise return the default channel out mask for the submix module.
+static audio_channel_mask_t get_supported_channel_out_mask(
+ const audio_channel_mask_t channel_out_mask)
+{
+ return channel_out_mask_supported(channel_out_mask) ? channel_out_mask :
+ static_cast<audio_channel_mask_t>(AUDIO_CHANNEL_OUT_STEREO);
+}
+
// Get a pointer to submix_stream_out given an audio_stream_out that is embedded within the
// structure.
static struct submix_stream_out * audio_stream_out_get_submix_stream_out(
@@ -172,26 +253,107 @@ uint32_t get_channel_count_from_mask(const audio_channel_mask_t channel_mask) {
return 0;
}
+// Convert a input channel mask to output channel mask where a mapping is available, returns 0
+// otherwise.
+static audio_channel_mask_t audio_channel_in_mask_to_out(
+ const audio_channel_mask_t channel_in_mask)
+{
+ switch (channel_in_mask) {
+ case AUDIO_CHANNEL_IN_MONO:
+ return AUDIO_CHANNEL_OUT_MONO;
+ case AUDIO_CHANNEL_IN_STEREO:
+ return AUDIO_CHANNEL_OUT_STEREO;
+ default:
+ return 0;
+ }
+}
+
+// Compare an audio_config with input channel mask and an audio_config with output channel mask
+// returning false if they do *not* match, true otherwise.
+static bool audio_config_compare(const audio_config * const input_config,
+ const audio_config * const output_config)
+{
+ audio_channel_mask_t channel_mask = audio_channel_in_mask_to_out(input_config->channel_mask);
+ if (channel_mask != output_config->channel_mask) {
+ ALOGE("audio_config_compare() channel mask mismatch %x (%x) vs. %x",
+ channel_mask, input_config->channel_mask, output_config->channel_mask);
+ return false;
+ }
+ if (input_config->sample_rate != output_config->sample_rate) {
+ ALOGE("audio_config_compare() sample rate mismatch %ul vs. %ul",
+ input_config->sample_rate, output_config->sample_rate);
+ return false;
+ }
+ if (input_config->format != output_config->format) {
+ ALOGE("audio_config_compare() format mismatch %x vs. %x",
+ input_config->format, output_config->format);
+ return false;
+ }
+ // This purposely ignores offload_info as it's not required for the submix device.
+ return true;
+}
+
+// Sanitize the user specified audio config for a submix input / output stream.
+static void submix_sanitize_config(struct audio_config * const config, const bool is_input_format)
+{
+ config->channel_mask = is_input_format ? get_supported_channel_in_mask(config->channel_mask) :
+ get_supported_channel_out_mask(config->channel_mask);
+ config->sample_rate = get_supported_sample_rate(config->sample_rate);
+ config->format = DEFAULT_FORMAT;
+}
+
+// Verify a submix input or output stream can be opened.
+static bool submix_open_validate(const struct submix_audio_device * const rsxadev,
+ pthread_mutex_t * const lock,
+ const struct audio_config * const config,
+ const bool opening_input)
+{
+ bool pipe_open;
+ audio_config pipe_config;
+
+ // Query the device for the current audio config and whether input and output streams are open.
+ pthread_mutex_lock(lock);
+ pipe_open = rsxadev->rsxSink.get() != NULL || rsxadev->rsxSource.get() != NULL;
+ memcpy(&pipe_config, &rsxadev->config.common, sizeof(pipe_config));
+ pthread_mutex_unlock(lock);
+
+ // If the pipe is open, verify the existing audio config the pipe matches the user
+ // specified config.
+ if (pipe_open) {
+ const audio_config * const input_config = opening_input ? config : &pipe_config;
+ const audio_config * const output_config = opening_input ? &pipe_config : config;
+ // Get the channel mask of the open device.
+ pipe_config.channel_mask =
+ opening_input ? rsxadev->config.output_channel_mask :
+ rsxadev->config.input_channel_mask;
+ if (!audio_config_compare(input_config, output_config)) {
+ ALOGE("submix_open_validate(): Unsupported format.");
+ return -EINVAL;
+ }
+ }
+ return true;
+}
+
/* audio HAL functions */
static uint32_t out_get_sample_rate(const struct audio_stream *stream)
{
const struct submix_stream_out * const out = audio_stream_get_submix_stream_out(
const_cast<struct audio_stream *>(stream));
- const uint32_t out_rate = out->dev->config.sample_rate;
+ const uint32_t out_rate = out->dev->config.common.sample_rate;
SUBMIX_ALOGV("out_get_sample_rate() returns %u", out_rate);
return out_rate;
}
static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
{
- if ((rate != 44100) && (rate != 48000)) {
+ if (!sample_rate_supported(rate)) {
ALOGE("out_set_sample_rate(rate=%u) rate unsupported", rate);
return -ENOSYS;
}
struct submix_stream_out * const out = audio_stream_get_submix_stream_out(stream);
SUBMIX_ALOGV("out_set_sample_rate(rate=%u)", rate);
- out->dev->config.sample_rate = rate;
+ out->dev->config.common.sample_rate = rate;
return 0;
}
@@ -202,7 +364,7 @@ static size_t out_get_buffer_size(const struct audio_stream *stream)
const struct submix_config * const config = &out->dev->config;
const size_t buffer_size = config->period_size * audio_stream_frame_size(stream);
SUBMIX_ALOGV("out_get_buffer_size() returns %zu bytes, %zu frames",
- buffer_size, config->buffer_period_size_frames);
+ buffer_size, config->period_size);
return buffer_size;
}
@@ -210,7 +372,7 @@ static audio_channel_mask_t out_get_channels(const struct audio_stream *stream)
{
const struct submix_stream_out * const out = audio_stream_get_submix_stream_out(
const_cast<struct audio_stream *>(stream));
- uint32_t channel_mask = out->dev->config.channel_mask;
+ uint32_t channel_mask = out->dev->config.output_channel_mask;
SUBMIX_ALOGV("out_get_channels() returns %08x", channel_mask);
return channel_mask;
}
@@ -219,7 +381,7 @@ static audio_format_t out_get_format(const struct audio_stream *stream)
{
const struct submix_stream_out * const out = audio_stream_get_submix_stream_out(
const_cast<struct audio_stream *>(stream));
- const audio_format_t format = out->dev->config.format;
+ const audio_format_t format = out->dev->config.common.format;
SUBMIX_ALOGV("out_get_format() returns %x", format);
return format;
}
@@ -227,7 +389,7 @@ static audio_format_t out_get_format(const struct audio_stream *stream)
static int out_set_format(struct audio_stream *stream, audio_format_t format)
{
const struct submix_stream_out * const out = audio_stream_get_submix_stream_out(stream);
- if (format != out->dev->config.format) {
+ if (format != out->dev->config.common.format) {
ALOGE("out_set_format(format=%x) format unsupported", format);
return -ENOSYS;
}
@@ -294,9 +456,9 @@ static uint32_t out_get_latency(const struct audio_stream_out *stream)
const struct submix_stream_out * const out = audio_stream_out_get_submix_stream_out(
const_cast<struct audio_stream_out *>(stream));
const struct submix_config * const config = &out->dev->config;
- const uint32_t latency_ms = (MAX_PIPE_DEPTH_IN_FRAMES * 1000) / config->sample_rate;
+ const uint32_t latency_ms = (MAX_PIPE_DEPTH_IN_FRAMES * 1000) / config->common.sample_rate;
SUBMIX_ALOGV("out_get_latency() returns %u ms, size in frames %zu, sample rate %u", latency_ms,
- config->buffer_size_frames, config->common.sample_rate);
+ MAX_PIPE_DEPTH_IN_FRAMES, config->common.sample_rate);
return latency_ms;
}
@@ -410,14 +572,18 @@ static uint32_t in_get_sample_rate(const struct audio_stream *stream)
{
const struct submix_stream_in * const in = audio_stream_get_submix_stream_in(
const_cast<struct audio_stream*>(stream));
- SUBMIX_ALOGV("in_get_sample_rate() returns %u", in->dev->config.sample_rate);
- return in->dev->config.sample_rate;
+ SUBMIX_ALOGV("in_get_sample_rate() returns %u", in->dev->config.common.sample_rate);
+ return in->dev->config.common.sample_rate;
}
static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
{
const struct submix_stream_in * const in = audio_stream_get_submix_stream_in(stream);
- in->dev->config.sample_rate = rate;
+ if (!sample_rate_supported(rate)) {
+ ALOGE("in_set_sample_rate(rate=%u) rate unsupported", rate);
+ return -ENOSYS;
+ }
+ in->dev->config.common.sample_rate = rate;
SUBMIX_ALOGV("in_set_sample_rate() set %u", rate);
return 0;
}
@@ -433,15 +599,18 @@ static size_t in_get_buffer_size(const struct audio_stream *stream)
static audio_channel_mask_t in_get_channels(const struct audio_stream *stream)
{
- (void)stream;
- return AUDIO_CHANNEL_IN_STEREO;
+ const struct submix_stream_in * const in = audio_stream_get_submix_stream_in(
+ const_cast<struct audio_stream*>(stream));
+ const audio_channel_mask_t channel_mask = in->dev->config.input_channel_mask;
+ SUBMIX_ALOGV("in_get_channels() returns %x", channel_mask);
+ return channel_mask;
}
static audio_format_t in_get_format(const struct audio_stream *stream)
{
const struct submix_stream_in * const in = audio_stream_get_submix_stream_in(
- const_cast<struct audio_stream*>(stream));
- const audio_format_t format = in->dev->config.format;
+ const_cast<struct audio_stream*>(stream));
+ const audio_format_t format = in->dev->config.common.format;
SUBMIX_ALOGV("in_get_format() returns %x", format);
return format;
}
@@ -449,7 +618,7 @@ static audio_format_t in_get_format(const struct audio_stream *stream)
static int in_set_format(struct audio_stream *stream, audio_format_t format)
{
const struct submix_stream_in * const in = audio_stream_get_submix_stream_in(stream);
- if (format != in->dev->config.format) {
+ if (format != in->dev->config.common.format) {
ALOGE("in_set_format(format=%x) format unsupported", format);
return -ENOSYS;
}
@@ -642,6 +811,13 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
(void)devices;
(void)flags;
+ // Make sure it's possible to open the device given the current audio config.
+ submix_sanitize_config(config, false);
+ if (!submix_open_validate(rsxadev, &rsxadev->lock, config, false)) {
+ ALOGE("adev_open_output_stream(): Unable to open output stream.");
+ return -EINVAL;
+ }
+
out = (struct submix_stream_out *)calloc(1, sizeof(struct submix_stream_out));
if (!out) {
ret = -ENOMEM;
@@ -669,16 +845,8 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
out->stream.get_render_position = out_get_render_position;
out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
- config->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
- rsxadev->config.channel_mask = config->channel_mask;
-
- if ((config->sample_rate != 48000) && (config->sample_rate != 44100)) {
- config->sample_rate = DEFAULT_SAMPLE_RATE_HZ;
- }
- rsxadev->config.sample_rate = config->sample_rate;
-
- config->format = AUDIO_FORMAT_PCM_16_BIT;
- rsxadev->config.format = config->format;
+ memcpy(&rsxadev->config.common, config, sizeof(rsxadev->config.common));
+ rsxadev->config.output_channel_mask = config->channel_mask;
rsxadev->config.period_size = PERIOD_SIZE_IN_FRAMES;
@@ -691,8 +859,9 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
// initialize pipe
{
ALOGV(" initializing pipe");
- const NBAIO_Format format = Format_from_SR_C(config->sample_rate,
- get_channel_count_from_mask(config->channel_mask), config->format);
+ const NBAIO_Format format = Format_from_SR_C(rsxadev->config.common.sample_rate,
+ get_channel_count_from_mask(rsxadev->config.common.channel_mask),
+ rsxadev->config.common.format);
const NBAIO_Format offers[1] = {format};
size_t numCounterOffers = 0;
// creating a MonoPipe with optional blocking set to true.
@@ -839,6 +1008,13 @@ static int adev_open_input_stream(struct audio_hw_device *dev,
(void)handle;
(void)devices;
+ // Make sure it's possible to open the device given the current audio config.
+ submix_sanitize_config(config, true);
+ if (!submix_open_validate(rsxadev, &rsxadev->lock, config, true)) {
+ ALOGE("adev_open_input_stream(): Unable to open input stream.");
+ return -EINVAL;
+ }
+
in = (struct submix_stream_in *)calloc(1, sizeof(struct submix_stream_in));
if (!in) {
ret = -ENOMEM;
@@ -864,16 +1040,8 @@ static int adev_open_input_stream(struct audio_hw_device *dev,
in->stream.read = in_read;
in->stream.get_input_frames_lost = in_get_input_frames_lost;
- config->channel_mask = AUDIO_CHANNEL_IN_STEREO;
- rsxadev->config.channel_mask = config->channel_mask;
-
- if ((config->sample_rate != 48000) && (config->sample_rate != 44100)) {
- config->sample_rate = DEFAULT_SAMPLE_RATE_HZ;
- }
- rsxadev->config.sample_rate = config->sample_rate;
-
- config->format = AUDIO_FORMAT_PCM_16_BIT;
- rsxadev->config.format = config->format;
+ memcpy(&rsxadev->config.common, config, sizeof(rsxadev->config.common));
+ rsxadev->config.input_channel_mask = config->channel_mask;
rsxadev->config.period_size = PERIOD_SIZE_IN_FRAMES;