summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHongwei Wang <hwwang@google.com>2018-05-11 11:55:44 -0700
committerandroid-build-merger <android-build-merger@google.com>2018-05-11 11:55:44 -0700
commitafcd393d476abf71ddf573b6ffd76d0ca3699c56 (patch)
treef912977ea8fd5aef18456bb9cd5630e945d9d995
parentea46c2bf14415eac9894a916e3f11d82a263c580 (diff)
parent8a1a1c4d8a583ce31e4c7adfe01d14dce6fd9f94 (diff)
downloadcar-afcd393d476abf71ddf573b6ffd76d0ca3699c56.tar.gz
Simulates audio mixer
am: 8a1a1c4d8a Change-Id: I724fa521a68d71f61e13b9e5c161e57087c69591
-rw-r--r--emulator/audio/driver/audio_hw.c3
-rw-r--r--emulator/audio/driver/ext_pcm.c109
-rw-r--r--emulator/audio/driver/ext_pcm.h18
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 <errno.h>
#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <log/log.h>
+#include <cutils/str_parms.h>
#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 <pthread.h>
+
+#include <cutils/hashmap.h>
#include <tinyalsa/asoundlib.h>
+// 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);