summaryrefslogtreecommitdiffstats
path: root/modules/audio_remote_submix/audio_hw.cpp
diff options
context:
space:
mode:
authorStewart Miles <smiles@google.com>2014-05-01 09:03:27 -0700
committerStewart Miles <smiles@google.com>2014-05-14 09:59:59 -0700
commit7072684fbfc73b0dd92dc010f418b3919221f1d8 (patch)
treefe6d917b39ce3104a2d090213a473856945e6abe /modules/audio_remote_submix/audio_hw.cpp
parent568e66ff9e0d6be92ba994e8054f78a0fb8726e2 (diff)
downloadhardware_libhardware-7072684fbfc73b0dd92dc010f418b3919221f1d8.zip
hardware_libhardware-7072684fbfc73b0dd92dc010f418b3919221f1d8.tar.gz
hardware_libhardware-7072684fbfc73b0dd92dc010f418b3919221f1d8.tar.bz2
Allow submix module streams to use any NBAIO format.
This modifies the submix module to allow the internal buffer to be any NBAIO format. This is useful when sending audio of arbitrary formats between processes. AudioFlinger continues to enforce format restrictions as specified by the device specific audio_policy.conf so the submix streams will have the same restrictions as before. Bug: 11273000 Change-Id: I2cd4ff383c5c75e491ab22bc1949c835f04e230f
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;