summaryrefslogtreecommitdiffstats
path: root/emulator
diff options
context:
space:
mode:
authorHongwei Wang <hwwang@google.com>2018-04-10 14:14:32 -0700
committerHongwei Wang <hwwang@google.com>2018-05-10 14:36:42 -0700
commitfc91e00f0a533419695084ae08cc2217c5711a3b (patch)
treed467b65815d29d73c4cced7c2509b2c551d5b74a /emulator
parent3b214bb09477b50626312ff4cba773fdc201fc77 (diff)
downloaddevice_generic_car-fc91e00f0a533419695084ae08cc2217c5711a3b.tar.gz
device_generic_car-fc91e00f0a533419695084ae08cc2217c5711a3b.tar.bz2
device_generic_car-fc91e00f0a533419695084ae08cc2217c5711a3b.zip
Simulates hardware gain control in audio driver
Change log - audio_vbuffer in its own source and header files - a pcm is shared via ext_pcm by multiple audio streams Known issue - Audio is distorted if multiple audio streams try to play simultaneously. This is due to lack of proper audio mixing and will be fixed in a future CL Bug: 79496296 Test: manually Change-Id: I14392e13ae49131a044efdda97ea23946287ad97
Diffstat (limited to 'emulator')
-rw-r--r--emulator/audio/driver/Android.bp6
-rw-r--r--emulator/audio/driver/audio_hw.c251
-rw-r--r--emulator/audio/driver/audio_hw.h90
-rw-r--r--emulator/audio/driver/audio_vbuffer.c137
-rw-r--r--emulator/audio/driver/audio_vbuffer.h47
-rw-r--r--emulator/audio/driver/ext_pcm.c94
-rw-r--r--emulator/audio/driver/ext_pcm.h39
7 files changed, 467 insertions, 197 deletions
diff --git a/emulator/audio/driver/Android.bp b/emulator/audio/driver/Android.bp
index 24aa96d..b0c2c34 100644
--- a/emulator/audio/driver/Android.bp
+++ b/emulator/audio/driver/Android.bp
@@ -21,7 +21,11 @@ cc_library_shared {
name: "audio.primary.caremu",
relative_install_path: "hw",
- srcs: ["audio_hw.c"],
+ srcs: [
+ "audio_hw.c",
+ "audio_vbuffer.c",
+ "ext_pcm.c",
+ ],
include_dirs: ["external/tinyalsa/include"],
diff --git a/emulator/audio/driver/audio_hw.c b/emulator/audio/driver/audio_hw.c
index 83d4858..ade4f0e 100644
--- a/emulator/audio/driver/audio_hw.c
+++ b/emulator/audio/driver/audio_hw.c
@@ -24,7 +24,7 @@
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
-#include <pthread.h>
+#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/time.h>
@@ -33,13 +33,13 @@
#include <unistd.h>
#include <log/log.h>
-#include <cutils/hashmap.h>
#include <cutils/str_parms.h>
#include <hardware/hardware.h>
#include <system/audio.h>
-#include <hardware/audio.h>
-#include <tinyalsa/asoundlib.h>
+
+#include "audio_hw.h"
+#include "ext_pcm.h"
#define PCM_CARD 0
#define PCM_DEVICE 0
@@ -50,183 +50,8 @@
#define IN_PERIOD_MS 15
#define IN_PERIOD_COUNT 4
-struct generic_audio_device {
- struct audio_hw_device device; // Constant after init
- pthread_mutex_t lock;
- bool mic_mute; // Proteced by this->lock
- struct mixer *mixer; // Proteced by this->lock
- Hashmap *out_bus_stream_map; // Extended field. Constant after init
-};
-
static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state);
-typedef struct audio_vbuffer {
- pthread_mutex_t lock;
- uint8_t *data;
- size_t frame_size;
- size_t frame_count;
- size_t head;
- size_t tail;
- size_t live;
-} audio_vbuffer_t;
-
-static int audio_vbuffer_init(audio_vbuffer_t *audio_vbuffer, size_t frame_count,
- size_t frame_size) {
- if (!audio_vbuffer) {
- return -EINVAL;
- }
- audio_vbuffer->frame_size = frame_size;
- audio_vbuffer->frame_count = frame_count;
- size_t bytes = frame_count * frame_size;
- audio_vbuffer->data = calloc(bytes, 1);
- if (!audio_vbuffer->data) {
- return -ENOMEM;
- }
- audio_vbuffer->head = 0;
- audio_vbuffer->tail = 0;
- audio_vbuffer->live = 0;
- pthread_mutex_init(&audio_vbuffer->lock, (const pthread_mutexattr_t *) NULL);
- return 0;
-}
-
-static int audio_vbuffer_destroy(audio_vbuffer_t *audio_vbuffer) {
- if (!audio_vbuffer) {
- return -EINVAL;
- }
- free(audio_vbuffer->data);
- pthread_mutex_destroy(&audio_vbuffer->lock);
- return 0;
-}
-
-static int audio_vbuffer_live(audio_vbuffer_t *audio_vbuffer) {
- if (!audio_vbuffer) {
- return -EINVAL;
- }
- pthread_mutex_lock(&audio_vbuffer->lock);
- int live = audio_vbuffer->live;
- pthread_mutex_unlock(&audio_vbuffer->lock);
- return live;
-}
-
-static int audio_vbuffer_dead(audio_vbuffer_t *audio_vbuffer) {
- if (!audio_vbuffer) {
- return -EINVAL;
- }
- pthread_mutex_lock(&audio_vbuffer->lock);
- int dead = audio_vbuffer->frame_count - audio_vbuffer->live;
- pthread_mutex_unlock(&audio_vbuffer->lock);
- return dead;
-}
-
-#define MIN(a,b) (((a)<(b))?(a):(b))
-static size_t audio_vbuffer_write(audio_vbuffer_t *audio_vbuffer, const void *buffer,
- size_t frame_count) {
- size_t frames_written = 0;
- pthread_mutex_lock(&audio_vbuffer->lock);
-
- while (frame_count != 0) {
- int frames = 0;
- if (audio_vbuffer->live == 0 || audio_vbuffer->head > audio_vbuffer->tail) {
- frames = MIN(frame_count, audio_vbuffer->frame_count - audio_vbuffer->head);
- } else if (audio_vbuffer->head < audio_vbuffer->tail) {
- frames = MIN(frame_count, audio_vbuffer->tail - (audio_vbuffer->head));
- } else {
- ALOGD("%s audio_vbuffer is full", __func__);
- break;
- }
- memcpy(&audio_vbuffer->data[audio_vbuffer->head*audio_vbuffer->frame_size],
- &((uint8_t*)buffer)[frames_written*audio_vbuffer->frame_size],
- frames*audio_vbuffer->frame_size);
- audio_vbuffer->live += frames;
- frames_written += frames;
- frame_count -= frames;
- audio_vbuffer->head = (audio_vbuffer->head + frames) % audio_vbuffer->frame_count;
- }
-
- pthread_mutex_unlock(&audio_vbuffer->lock);
- return frames_written;
-}
-
-static size_t audio_vbuffer_read(audio_vbuffer_t *audio_vbuffer, void *buffer,
- size_t frame_count) {
- size_t frames_read = 0;
- pthread_mutex_lock(&audio_vbuffer->lock);
-
- while (frame_count != 0) {
- int frames = 0;
- if (audio_vbuffer->live == audio_vbuffer->frame_count ||
- audio_vbuffer->tail > audio_vbuffer->head) {
- frames = MIN(frame_count, audio_vbuffer->frame_count - audio_vbuffer->tail);
- } else if (audio_vbuffer->tail < audio_vbuffer->head) {
- frames = MIN(frame_count, audio_vbuffer->head - audio_vbuffer->tail);
- } else {
- break;
- }
- memcpy(&((uint8_t*)buffer)[frames_read*audio_vbuffer->frame_size],
- &audio_vbuffer->data[audio_vbuffer->tail*audio_vbuffer->frame_size],
- frames*audio_vbuffer->frame_size);
- audio_vbuffer->live -= frames;
- frames_read += frames;
- frame_count -= frames;
- audio_vbuffer->tail = (audio_vbuffer->tail + frames) % audio_vbuffer->frame_count;
- }
-
- pthread_mutex_unlock(&audio_vbuffer->lock);
- return frames_read;
-}
-
-struct generic_stream_out {
- struct audio_stream_out stream; // Constant after init
- pthread_mutex_t lock;
- struct generic_audio_device *dev; // Constant after init
- audio_devices_t device; // Protected by this->lock
- struct audio_config req_config; // Constant after init
- struct pcm_config pcm_config; // Constant after init
- audio_vbuffer_t buffer; // Constant after init
- const char *bus_address; // Extended field. Constant after init
-
- // Time & Position Keeping
- bool standby; // Protected by this->lock
- uint64_t underrun_position; // Protected by this->lock
- struct timespec underrun_time; // Protected by this->lock
- uint64_t last_write_time_us; // Protected by this->lock
- uint64_t frames_total_buffered; // Protected by this->lock
- uint64_t frames_written; // Protected by this->lock
- uint64_t frames_rendered; // Protected by this->lock
-
- // Worker
- pthread_t worker_thread; // Constant after init
- pthread_cond_t worker_wake; // Protected by this->lock
- bool worker_standby; // Protected by this->lock
- bool worker_exit; // Protected by this->lock
-};
-
-struct generic_stream_in {
- struct audio_stream_in stream; // Constant after init
- pthread_mutex_t lock;
- struct generic_audio_device *dev; // Constant after init
- audio_devices_t device; // Protected by this->lock
- struct audio_config req_config; // Constant after init
- struct pcm *pcm; // Protected by this->lock
- struct pcm_config pcm_config; // Constant after init
- int16_t *stereo_to_mono_buf; // Protected by this->lock
- size_t stereo_to_mono_buf_size; // Protected by this->lock
- audio_vbuffer_t buffer; // Protected by this->lock
- const char *bus_address; // Extended field. Constant after init
-
- // Time & Position Keeping
- bool standby; // Protected by this->lock
- int64_t standby_position; // Protected by this->lock
- struct timespec standby_exit_time;// Protected by this->lock
- int64_t standby_frames_read; // Protected by this->lock
-
- // Worker
- pthread_t worker_thread; // Constant after init
- pthread_cond_t worker_wake; // Protected by this->lock
- bool worker_standby; // Protected by this->lock
- bool worker_exit; // Protected by this->lock
-};
-
static struct pcm_config pcm_config_out = {
.channels = 2,
.rate = 0,
@@ -290,6 +115,7 @@ static int out_dump(const struct audio_stream *stream, int fd) {
"\t\tchannel mask: %08x\n"
"\t\tformat: %d\n"
"\t\tdevice: %08x\n"
+ "\t\tamplitude ratio: %f\n"
"\t\taudio dev: %p\n\n",
out->bus_address,
out_get_sample_rate(stream),
@@ -297,6 +123,7 @@ static int out_dump(const struct audio_stream *stream, int fd) {
out_get_channels(stream),
out_get_format(stream),
out->device,
+ out->amplitude_ratio,
out->dev);
pthread_mutex_unlock(&out->lock);
return 0;
@@ -369,7 +196,7 @@ static int out_set_volume(struct audio_stream_out *stream,
static void *out_write_worker(void *args) {
struct generic_stream_out *out = (struct generic_stream_out *)args;
- struct pcm *pcm = NULL;
+ struct ext_pcm *ext_pcm = NULL;
uint8_t *buffer = NULL;
int buffer_frames;
int buffer_size;
@@ -379,9 +206,9 @@ static void *out_write_worker(void *args) {
pthread_mutex_lock(&out->lock);
while (out->worker_standby || restart) {
restart = false;
- if (pcm) {
- pcm_close(pcm); // Frees pcm
- pcm = NULL;
+ if (ext_pcm) {
+ ext_pcm_close(ext_pcm); // Frees pcm
+ ext_pcm = NULL;
free(buffer);
buffer=NULL;
}
@@ -407,12 +234,12 @@ static void *out_write_worker(void *args) {
break;
}
- if (!pcm) {
- pcm = pcm_open(PCM_CARD, PCM_DEVICE,
+ if (!ext_pcm) {
+ ext_pcm = ext_pcm_open(PCM_CARD, PCM_DEVICE,
PCM_OUT | PCM_MONOTONIC, &out->pcm_config);
- if (!pcm_is_ready(pcm)) {
+ if (!ext_pcm_is_ready(ext_pcm)) {
ALOGE("pcm_open(out) failed: %s: address %s channels %d format %d rate %d",
- pcm_get_error(pcm),
+ ext_pcm_get_error(ext_pcm),
out->bus_address,
out->pcm_config.channels,
out->pcm_config.format,
@@ -421,7 +248,7 @@ static void *out_write_worker(void *args) {
break;
}
buffer_frames = out->pcm_config.period_size;
- buffer_size = pcm_frames_to_bytes(pcm, buffer_frames);
+ buffer_size = ext_pcm_frames_to_bytes(ext_pcm, buffer_frames);
buffer = malloc(buffer_size);
if (!buffer) {
ALOGE("could not allocate write buffer");
@@ -431,9 +258,9 @@ 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 = pcm_write(pcm, buffer, pcm_frames_to_bytes(pcm, frames));
+ int write_error = ext_pcm_write(ext_pcm, buffer, ext_pcm_frames_to_bytes(ext_pcm, frames));
if (write_error) {
- ALOGE("pcm_write failed %s address %s", pcm_get_error(pcm), out->bus_address);
+ ALOGE("pcm_write failed %s address %s", ext_pcm_get_error(ext_pcm), out->bus_address);
restart = true;
} else {
ALOGD("pcm_write succeed address %s", out->bus_address);
@@ -484,6 +311,18 @@ static void get_current_output_position(struct generic_stream_out *out,
}
}
+// Applies gain naively, assume AUDIO_FORMAT_PCM_16_BIT
+static void out_apply_gain(struct generic_stream_out *out, const void *buffer, size_t bytes) {
+ int16_t *int16_buffer = (int16_t *)buffer;
+ size_t int16_size = bytes / sizeof(int16_t);
+ for (int i = 0; i < int16_size; i++) {
+ float multiplied = int16_buffer[i] * out->amplitude_ratio;
+ if (multiplied > INT16_MAX) int16_buffer[i] = INT16_MAX;
+ else if (multiplied < INT16_MIN) int16_buffer[i] = INT16_MIN;
+ else int16_buffer[i] = (int16_t)multiplied;
+ }
+}
+
static ssize_t out_write(struct audio_stream_out *stream, const void *buffer, size_t bytes) {
struct generic_stream_out *out = (struct generic_stream_out *)stream;
const size_t frames = bytes / audio_stream_out_frame_size(stream);
@@ -507,6 +346,7 @@ static ssize_t out_write(struct audio_stream_out *stream, const void *buffer, si
out->frames_total_buffered = 0;
}
+ out_apply_gain(out, buffer, bytes);
size_t frames_written = audio_vbuffer_write(&out->buffer, buffer, frames);
pthread_cond_signal(&out->worker_wake);
@@ -926,7 +766,7 @@ static void *in_read_worker(void *args) {
break;
}
buffer_frames = in->pcm_config.period_size;
- buffer_size = pcm_frames_to_bytes(pcm, buffer_frames);
+ buffer_size = ext_pcm_frames_to_bytes(pcm, buffer_frames);
buffer = malloc(buffer_size);
if (!buffer) {
ALOGE("could not allocate worker read buffer");
@@ -935,7 +775,7 @@ static void *in_read_worker(void *args) {
}
}
pthread_mutex_unlock(&in->lock);
- int ret = pcm_read(pcm, buffer, pcm_frames_to_bytes(pcm, buffer_frames));
+ int ret = pcm_read(pcm, buffer, ext_pcm_frames_to_bytes(pcm, buffer_frames));
if (ret != 0) {
ALOGW("pcm_read failed %s", pcm_get_error(pcm));
restart = true;
@@ -1144,6 +984,13 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
out->bus_address = calloc(strlen(address) + 1, sizeof(char));
strncpy(out->bus_address, address, strlen(address));
hashmapPut(adev->out_bus_stream_map, out->bus_address, out);
+ /* TODO: read struct audio_gain from audio_policy_configuration */
+ out->gain_stage = (struct audio_gain) {
+ .min_value = -8400,
+ .max_value = 4000,
+ .step_value = 100,
+ };
+ out->amplitude_ratio = 1.0;
}
*stream_out = &out->stream;
ALOGD("%s bus:%s", __func__, out->bus_address);
@@ -1341,17 +1188,29 @@ static int adev_dump(const audio_hw_device_t *dev, int fd) {
static int adev_set_audio_port_config(struct audio_hw_device *dev,
const struct audio_port_config *config) {
+ int ret = 0;
struct generic_audio_device *adev = (struct generic_audio_device *)dev;
const char *bus_address = config->ext.device.address;
struct generic_stream_out *out = hashmapGet(adev->out_bus_stream_map, bus_address);
if (out) {
- // TODO set the actual audio port gain on physical bus
- ALOGD("%s: set audio gain: %d on bus_address:%s",
- __func__, config->gain.values[0], bus_address);
+ pthread_mutex_lock(&out->lock);
+ int gainIndex = (config->gain.values[0] - out->gain_stage.min_value) /
+ out->gain_stage.step_value;
+ int totalSteps = (out->gain_stage.max_value - out->gain_stage.min_value) /
+ out->gain_stage.step_value;
+ int minDb = out->gain_stage.min_value / 100;
+ int maxDb = out->gain_stage.max_value / 100;
+ // curve: 10^((minDb + (maxDb - minDb) * gainIndex / totalSteps) / 20)
+ out->amplitude_ratio = pow(10,
+ (minDb + (maxDb - minDb) * (gainIndex / (float)totalSteps)) / 20);
+ pthread_mutex_unlock(&out->lock);
+ ALOGD("%s: set audio gain: %f on %s",
+ __func__, out->amplitude_ratio, bus_address);
} else {
ALOGE("%s: can not find output stream by bus_address:%s", __func__, bus_address);
+ ret = -EINVAL;
}
- return 0;
+ return ret;
}
static int adev_create_audio_patch(struct audio_hw_device *dev,
diff --git a/emulator/audio/driver/audio_hw.h b/emulator/audio/driver/audio_hw.h
new file mode 100644
index 0000000..9ad7990
--- /dev/null
+++ b/emulator/audio/driver/audio_hw.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AUDIO_HW_H
+#define AUDIO_HW_H
+
+#include <pthread.h>
+
+#include <cutils/hashmap.h>
+#include <hardware/audio.h>
+#include <tinyalsa/asoundlib.h>
+
+#include "audio_vbuffer.h"
+
+struct generic_audio_device {
+ struct audio_hw_device device; // Constant after init
+ pthread_mutex_t lock;
+ bool mic_mute; // Proteced by this->lock
+ struct mixer *mixer; // Proteced by this->lock
+ Hashmap *out_bus_stream_map; // Extended field. Constant after init
+};
+
+struct generic_stream_out {
+ struct audio_stream_out stream; // Constant after init
+ pthread_mutex_t lock;
+ struct generic_audio_device *dev; // Constant after init
+ audio_devices_t device; // Protected by this->lock
+ struct audio_config req_config; // Constant after init
+ struct pcm_config pcm_config; // Constant after init
+ audio_vbuffer_t buffer; // Constant after init
+ const char *bus_address; // Extended field. Constant after init
+ struct audio_gain gain_stage; // Constant after init
+ float amplitude_ratio; // Protected by this->lock
+
+ // Time & Position Keeping
+ bool standby; // Protected by this->lock
+ uint64_t underrun_position; // Protected by this->lock
+ struct timespec underrun_time; // Protected by this->lock
+ uint64_t last_write_time_us; // Protected by this->lock
+ uint64_t frames_total_buffered; // Protected by this->lock
+ uint64_t frames_written; // Protected by this->lock
+ uint64_t frames_rendered; // Protected by this->lock
+
+ // Worker
+ pthread_t worker_thread; // Constant after init
+ pthread_cond_t worker_wake; // Protected by this->lock
+ bool worker_standby; // Protected by this->lock
+ bool worker_exit; // Protected by this->lock
+};
+
+struct generic_stream_in {
+ struct audio_stream_in stream; // Constant after init
+ pthread_mutex_t lock;
+ struct generic_audio_device *dev; // Constant after init
+ audio_devices_t device; // Protected by this->lock
+ struct audio_config req_config; // Constant after init
+ struct pcm *pcm; // Protected by this->lock
+ struct pcm_config pcm_config; // Constant after init
+ int16_t *stereo_to_mono_buf; // Protected by this->lock
+ size_t stereo_to_mono_buf_size; // Protected by this->lock
+ audio_vbuffer_t buffer; // Protected by this->lock
+ const char *bus_address; // Extended field. Constant after init
+
+ // Time & Position Keeping
+ bool standby; // Protected by this->lock
+ int64_t standby_position; // Protected by this->lock
+ struct timespec standby_exit_time; // Protected by this->lock
+ int64_t standby_frames_read; // Protected by this->lock
+
+ // Worker
+ pthread_t worker_thread; // Constant after init
+ pthread_cond_t worker_wake; // Protected by this->lock
+ bool worker_standby; // Protected by this->lock
+ bool worker_exit; // Protected by this->lock
+};
+
+#endif // AUDIO_HW_H
diff --git a/emulator/audio/driver/audio_vbuffer.c b/emulator/audio/driver/audio_vbuffer.c
new file mode 100644
index 0000000..79be545
--- /dev/null
+++ b/emulator/audio/driver/audio_vbuffer.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "audio_hw_generic"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <log/log.h>
+
+#include "audio_vbuffer.h"
+
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+int audio_vbuffer_init(audio_vbuffer_t *audio_vbuffer, size_t frame_count,
+ size_t frame_size) {
+ if (!audio_vbuffer) {
+ return -EINVAL;
+ }
+ audio_vbuffer->frame_size = frame_size;
+ audio_vbuffer->frame_count = frame_count;
+ size_t bytes = frame_count * frame_size;
+ audio_vbuffer->data = calloc(bytes, 1);
+ if (!audio_vbuffer->data) {
+ return -ENOMEM;
+ }
+ audio_vbuffer->head = 0;
+ audio_vbuffer->tail = 0;
+ audio_vbuffer->live = 0;
+ pthread_mutex_init(&audio_vbuffer->lock, (const pthread_mutexattr_t *)NULL);
+ return 0;
+}
+
+int audio_vbuffer_destroy(audio_vbuffer_t *audio_vbuffer) {
+ if (!audio_vbuffer) {
+ return -EINVAL;
+ }
+ free(audio_vbuffer->data);
+ pthread_mutex_destroy(&audio_vbuffer->lock);
+ return 0;
+}
+
+int audio_vbuffer_live(audio_vbuffer_t *audio_vbuffer) {
+ if (!audio_vbuffer) {
+ return -EINVAL;
+ }
+ pthread_mutex_lock(&audio_vbuffer->lock);
+ int live = audio_vbuffer->live;
+ pthread_mutex_unlock(&audio_vbuffer->lock);
+ return live;
+}
+
+int audio_vbuffer_dead(audio_vbuffer_t *audio_vbuffer) {
+ if (!audio_vbuffer) {
+ return -EINVAL;
+ }
+ pthread_mutex_lock(&audio_vbuffer->lock);
+ int dead = audio_vbuffer->frame_count - audio_vbuffer->live;
+ pthread_mutex_unlock(&audio_vbuffer->lock);
+ return dead;
+}
+
+size_t audio_vbuffer_write(audio_vbuffer_t *audio_vbuffer, const void *buffer,
+ size_t frame_count) {
+ size_t frames_written = 0;
+ pthread_mutex_lock(&audio_vbuffer->lock);
+
+ while (frame_count != 0) {
+ int frames = 0;
+ if (audio_vbuffer->live == 0 || audio_vbuffer->head > audio_vbuffer->tail) {
+ frames =
+ MIN(frame_count, audio_vbuffer->frame_count - audio_vbuffer->head);
+ } else if (audio_vbuffer->head < audio_vbuffer->tail) {
+ frames = MIN(frame_count, audio_vbuffer->tail - (audio_vbuffer->head));
+ } else {
+ ALOGD("%s audio_vbuffer is full", __func__);
+ break;
+ }
+ memcpy(
+ &audio_vbuffer->data[audio_vbuffer->head * audio_vbuffer->frame_size],
+ &((uint8_t *)buffer)[frames_written * audio_vbuffer->frame_size],
+ frames * audio_vbuffer->frame_size);
+ audio_vbuffer->live += frames;
+ frames_written += frames;
+ frame_count -= frames;
+ audio_vbuffer->head =
+ (audio_vbuffer->head + frames) % audio_vbuffer->frame_count;
+ }
+
+ pthread_mutex_unlock(&audio_vbuffer->lock);
+ return frames_written;
+}
+
+size_t audio_vbuffer_read(audio_vbuffer_t *audio_vbuffer, void *buffer,
+ size_t frame_count) {
+ size_t frames_read = 0;
+ pthread_mutex_lock(&audio_vbuffer->lock);
+
+ while (frame_count != 0) {
+ int frames = 0;
+ if (audio_vbuffer->live == audio_vbuffer->frame_count ||
+ audio_vbuffer->tail > audio_vbuffer->head) {
+ frames =
+ MIN(frame_count, audio_vbuffer->frame_count - audio_vbuffer->tail);
+ } else if (audio_vbuffer->tail < audio_vbuffer->head) {
+ frames = MIN(frame_count, audio_vbuffer->head - audio_vbuffer->tail);
+ } else {
+ break;
+ }
+ memcpy(
+ &((uint8_t *)buffer)[frames_read * audio_vbuffer->frame_size],
+ &audio_vbuffer->data[audio_vbuffer->tail * audio_vbuffer->frame_size],
+ frames * audio_vbuffer->frame_size);
+ audio_vbuffer->live -= frames;
+ frames_read += frames;
+ frame_count -= frames;
+ audio_vbuffer->tail =
+ (audio_vbuffer->tail + frames) % audio_vbuffer->frame_count;
+ }
+
+ pthread_mutex_unlock(&audio_vbuffer->lock);
+ return frames_read;
+}
diff --git a/emulator/audio/driver/audio_vbuffer.h b/emulator/audio/driver/audio_vbuffer.h
new file mode 100644
index 0000000..24bf986
--- /dev/null
+++ b/emulator/audio/driver/audio_vbuffer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AUDIO_VBUFFER_H
+#define AUDIO_VBUFFER_H
+
+#include <pthread.h>
+
+typedef struct audio_vbuffer {
+ pthread_mutex_t lock;
+ uint8_t *data;
+ size_t frame_size;
+ size_t frame_count;
+ size_t head;
+ size_t tail;
+ size_t live;
+} audio_vbuffer_t;
+
+int audio_vbuffer_init(audio_vbuffer_t *audio_vbuffer, size_t frame_count,
+ size_t frame_size);
+
+int audio_vbuffer_destroy(audio_vbuffer_t *audio_vbuffer);
+
+int audio_vbuffer_live(audio_vbuffer_t *audio_vbuffer);
+
+int audio_vbuffer_dead(audio_vbuffer_t *audio_vbuffer);
+
+size_t audio_vbuffer_write(audio_vbuffer_t *audio_vbuffer, const void *buffer,
+ size_t frame_count);
+
+size_t audio_vbuffer_read(audio_vbuffer_t *audio_vbuffer, void *buffer,
+ size_t frame_count);
+
+#endif // AUDIO_VBUFFER_H
diff --git a/emulator/audio/driver/ext_pcm.c b/emulator/audio/driver/ext_pcm.c
new file mode 100644
index 0000000..8fad329
--- /dev/null
+++ b/emulator/audio/driver/ext_pcm.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "ext_pcm.h"
+
+static pthread_mutex_t ext_pcm_init_lock = PTHREAD_MUTEX_INITIALIZER;
+static struct ext_pcm *shared_ext_pcm = NULL;
+
+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);
+ if (shared_ext_pcm == NULL) {
+ 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_unlock(&ext_pcm_init_lock);
+
+ pthread_mutex_lock(&shared_ext_pcm->lock);
+ shared_ext_pcm->ref_count += 1;
+ pthread_mutex_unlock(&shared_ext_pcm->lock);
+
+ return shared_ext_pcm;
+}
+
+int ext_pcm_close(struct ext_pcm *ext_pcm) {
+ if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
+ return -EINVAL;
+ }
+
+ pthread_mutex_lock(&ext_pcm->lock);
+ ext_pcm->ref_count -= 1;
+ pthread_mutex_unlock(&ext_pcm->lock);
+
+ pthread_mutex_lock(&ext_pcm_init_lock);
+ if (ext_pcm->ref_count <= 0) {
+ pthread_mutex_destroy(&ext_pcm->lock);
+ pcm_close(ext_pcm->pcm);
+ free(ext_pcm);
+ shared_ext_pcm = NULL;
+ }
+ pthread_mutex_unlock(&ext_pcm_init_lock);
+ return 0;
+}
+
+int ext_pcm_is_ready(struct ext_pcm *ext_pcm) {
+ if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
+ return 0;
+ }
+
+ return pcm_is_ready(ext_pcm->pcm);
+}
+
+int ext_pcm_write(struct ext_pcm *ext_pcm, const void *data,
+ unsigned int count) {
+ if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
+ return 0;
+ }
+
+ return pcm_write(ext_pcm->pcm, data, count);
+}
+
+const char *ext_pcm_get_error(struct ext_pcm *ext_pcm) {
+ if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
+ return NULL;
+ }
+
+ return pcm_get_error(ext_pcm->pcm);
+}
+
+unsigned int ext_pcm_frames_to_bytes(struct ext_pcm *ext_pcm,
+ unsigned int frames) {
+ if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
+ return -EINVAL;
+ }
+
+ return pcm_frames_to_bytes(ext_pcm->pcm, frames);
+}
diff --git a/emulator/audio/driver/ext_pcm.h b/emulator/audio/driver/ext_pcm.h
new file mode 100644
index 0000000..19045ff
--- /dev/null
+++ b/emulator/audio/driver/ext_pcm.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef EXT_PCM_H
+#define EXT_PCM_H
+
+#include <pthread.h>
+#include <tinyalsa/asoundlib.h>
+
+struct ext_pcm {
+ struct pcm *pcm;
+ pthread_mutex_t lock;
+ unsigned int ref_count;
+};
+
+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);
+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);
+
+#endif // EXT_PCM_H