From 8a1a1c4d8a583ce31e4c7adfe01d14dce6fd9f94 Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Thu, 10 May 2018 10:53:35 -0700 Subject: Simulates audio mixer Bug: 79496296 Test: Play music + navigation in KitchenSink, gcar_emu_x86-userdebug Change-Id: I335183e2428e655d1cd81141d49326b457669383 --- emulator/audio/driver/audio_hw.c | 3 +- emulator/audio/driver/ext_pcm.c | 109 +++++++++++++++++++++++++++++++++++++-- emulator/audio/driver/ext_pcm.h | 18 ++++++- 3 files changed, 123 insertions(+), 7 deletions(-) diff --git a/emulator/audio/driver/audio_hw.c b/emulator/audio/driver/audio_hw.c index ade4f0e..7737955 100644 --- a/emulator/audio/driver/audio_hw.c +++ b/emulator/audio/driver/audio_hw.c @@ -258,7 +258,8 @@ static void *out_write_worker(void *args) { } int frames = audio_vbuffer_read(&out->buffer, buffer, buffer_frames); pthread_mutex_unlock(&out->lock); - int write_error = ext_pcm_write(ext_pcm, buffer, ext_pcm_frames_to_bytes(ext_pcm, frames)); + int write_error = ext_pcm_write(ext_pcm, out->bus_address, + buffer, ext_pcm_frames_to_bytes(ext_pcm, frames)); if (write_error) { ALOGE("pcm_write failed %s address %s", ext_pcm_get_error(ext_pcm), out->bus_address); restart = true; diff --git a/emulator/audio/driver/ext_pcm.c b/emulator/audio/driver/ext_pcm.c index 8fad329..b1519f5 100644 --- a/emulator/audio/driver/ext_pcm.c +++ b/emulator/audio/driver/ext_pcm.c @@ -14,14 +14,100 @@ * limitations under the License. */ +#define LOG_TAG "audio_hw_generic" + #include #include +#include +#include + +#include +#include #include "ext_pcm.h" static pthread_mutex_t ext_pcm_init_lock = PTHREAD_MUTEX_INITIALIZER; static struct ext_pcm *shared_ext_pcm = NULL; +// Sleep 10ms between each mixing, this interval value is arbitrary chosen +#define MIXER_INTERVAL_MS 10 +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +/* copied from libcutils/str_parms.c */ +static bool str_eq(void *key_a, void *key_b) { + return !strcmp((const char *)key_a, (const char *)key_b); +} + +/** + * use djb hash unless we find it inadequate. + * copied from libcutils/str_parms.c + */ +#ifdef __clang__ +__attribute__((no_sanitize("integer"))) +#endif +static int str_hash_fn(void *str) { + uint32_t hash = 5381; + char *p; + for (p = str; p && *p; p++) { + hash = ((hash << 5) + hash) + *p; + } + return (int)hash; +} + +static bool mixer_thread_mix(__unused void *key, void *value, void *context) { + struct ext_mixer_pipeline *pipeline_out = (struct ext_mixer_pipeline *)context; + struct ext_mixer_pipeline *pipeline_in = (struct ext_mixer_pipeline *)value; + pipeline_out->position = MAX(pipeline_out->position, pipeline_in->position); + for (int i = 0; i < pipeline_out->position; i++) { + float mixed = pipeline_out->buffer[i] + pipeline_in->buffer[i]; + if (mixed > INT16_MAX) pipeline_out->buffer[i] = INT16_MAX; + else if (mixed < INT16_MIN) pipeline_out->buffer[i] = INT16_MIN; + else pipeline_out->buffer[i] = (int16_t)mixed; + } + memset(pipeline_in, 0, sizeof(struct ext_mixer_pipeline)); + return true; +} + +static void *mixer_thread_loop(void *context) { + ALOGD("%s: __enter__", __func__); + struct ext_pcm *ext_pcm = (struct ext_pcm *)context; + do { + pthread_mutex_lock(&ext_pcm->mixer_lock); + ext_pcm->mixer_pipeline.position = 0; + // Combine the output from every pipeline into one output buffer + hashmapForEach(ext_pcm->mixer_pipeline_map, mixer_thread_mix, + &ext_pcm->mixer_pipeline); + if (ext_pcm->mixer_pipeline.position > 0) { + pcm_write(ext_pcm->pcm, (void *)ext_pcm->mixer_pipeline.buffer, + ext_pcm->mixer_pipeline.position * sizeof(int16_t)); + } + memset(&ext_pcm->mixer_pipeline, 0, sizeof(struct ext_mixer_pipeline)); + pthread_mutex_unlock(&ext_pcm->mixer_lock); + usleep(MIXER_INTERVAL_MS * 1000); + } while (1); +} + +static int mixer_pipeline_write(struct ext_pcm *ext_pcm, const char *bus_address, + const void *data, unsigned int count) { + pthread_mutex_lock(&ext_pcm->mixer_lock); + struct ext_mixer_pipeline *pipeline = hashmapGet( + ext_pcm->mixer_pipeline_map, bus_address); + if (!pipeline) { + pipeline = calloc(1, sizeof(struct ext_mixer_pipeline)); + hashmapPut(ext_pcm->mixer_pipeline_map, bus_address, pipeline); + } + unsigned int byteCount = MIN(count, + (MIXER_BUFFER_SIZE - pipeline->position) * sizeof(int16_t)); + unsigned int int16Count = byteCount / sizeof(int16_t); + if (int16Count > 0) { + memcpy(&pipeline->buffer[pipeline->position], data, byteCount); + pipeline->position += int16Count; + } + pthread_mutex_unlock(&ext_pcm->mixer_lock); + return 0; +} + struct ext_pcm *ext_pcm_open(unsigned int card, unsigned int device, unsigned int flags, struct pcm_config *config) { pthread_mutex_lock(&ext_pcm_init_lock); @@ -29,6 +115,10 @@ struct ext_pcm *ext_pcm_open(unsigned int card, unsigned int device, shared_ext_pcm = calloc(1, sizeof(struct ext_pcm)); pthread_mutex_init(&shared_ext_pcm->lock, (const pthread_mutexattr_t *) NULL); shared_ext_pcm->pcm = pcm_open(card, device, flags, config); + pthread_mutex_init(&shared_ext_pcm->mixer_lock, (const pthread_mutexattr_t *)NULL); + pthread_create(&shared_ext_pcm->mixer_thread, (const pthread_attr_t *)NULL, + mixer_thread_loop, shared_ext_pcm); + shared_ext_pcm->mixer_pipeline_map = hashmapCreate(8, str_hash_fn, str_eq); } pthread_mutex_unlock(&ext_pcm_init_lock); @@ -39,6 +129,12 @@ struct ext_pcm *ext_pcm_open(unsigned int card, unsigned int device, return shared_ext_pcm; } +static bool mixer_free_pipeline(__unused void *key, void *value, void *context) { + struct ext_mixer_pipeline *pipeline = (struct ext_mixer_pipeline *)value; + free(pipeline); + return true; +} + int ext_pcm_close(struct ext_pcm *ext_pcm) { if (ext_pcm == NULL || ext_pcm->pcm == NULL) { return -EINVAL; @@ -52,6 +148,11 @@ int ext_pcm_close(struct ext_pcm *ext_pcm) { if (ext_pcm->ref_count <= 0) { pthread_mutex_destroy(&ext_pcm->lock); pcm_close(ext_pcm->pcm); + pthread_mutex_destroy(&ext_pcm->mixer_lock); + hashmapForEach(ext_pcm->mixer_pipeline_map, mixer_free_pipeline, + (void *)NULL); + hashmapFree(ext_pcm->mixer_pipeline_map); + pthread_kill(ext_pcm->mixer_thread, SIGINT); free(ext_pcm); shared_ext_pcm = NULL; } @@ -67,13 +168,13 @@ int ext_pcm_is_ready(struct ext_pcm *ext_pcm) { return pcm_is_ready(ext_pcm->pcm); } -int ext_pcm_write(struct ext_pcm *ext_pcm, const void *data, - unsigned int count) { +int ext_pcm_write(struct ext_pcm *ext_pcm, const char *address, + const void *data, unsigned int count) { if (ext_pcm == NULL || ext_pcm->pcm == NULL) { - return 0; + return -EINVAL; } - return pcm_write(ext_pcm->pcm, data, count); + return mixer_pipeline_write(ext_pcm, address, data, count); } const char *ext_pcm_get_error(struct ext_pcm *ext_pcm) { diff --git a/emulator/audio/driver/ext_pcm.h b/emulator/audio/driver/ext_pcm.h index 19045ff..0bc8df5 100644 --- a/emulator/audio/driver/ext_pcm.h +++ b/emulator/audio/driver/ext_pcm.h @@ -18,20 +18,34 @@ #define EXT_PCM_H #include + +#include #include +// Holds up to 4KB buffer for each mixer pipeline, this value is arbitrary chosen +#define MIXER_BUFFER_SIZE (1024 * 4) + +struct ext_mixer_pipeline { + int16_t buffer[MIXER_BUFFER_SIZE]; + unsigned int position; +}; + struct ext_pcm { struct pcm *pcm; pthread_mutex_t lock; unsigned int ref_count; + pthread_mutex_t mixer_lock; + struct ext_mixer_pipeline mixer_pipeline; + pthread_t mixer_thread; + Hashmap *mixer_pipeline_map; }; struct ext_pcm *ext_pcm_open(unsigned int card, unsigned int device, unsigned int flags, struct pcm_config *config); int ext_pcm_close(struct ext_pcm *ext_pcm); int ext_pcm_is_ready(struct ext_pcm *ext_pcm); -int ext_pcm_write(struct ext_pcm *ext_pcm, const void *data, - unsigned int count); +int ext_pcm_write(struct ext_pcm *ext_pcm, const char *bus_address, + const void *data, unsigned int count); const char *ext_pcm_get_error(struct ext_pcm *ext_pcm); unsigned int ext_pcm_frames_to_bytes(struct ext_pcm *ext_pcm, unsigned int frames); -- cgit v1.2.3