diff options
author | Hongwei Wang <hwwang@google.com> | 2018-04-10 14:14:32 -0700 |
---|---|---|
committer | Hongwei Wang <hwwang@google.com> | 2018-05-10 14:36:42 -0700 |
commit | fc91e00f0a533419695084ae08cc2217c5711a3b (patch) | |
tree | d467b65815d29d73c4cced7c2509b2c551d5b74a /emulator | |
parent | 3b214bb09477b50626312ff4cba773fdc201fc77 (diff) | |
download | device_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.bp | 6 | ||||
-rw-r--r-- | emulator/audio/driver/audio_hw.c | 251 | ||||
-rw-r--r-- | emulator/audio/driver/audio_hw.h | 90 | ||||
-rw-r--r-- | emulator/audio/driver/audio_vbuffer.c | 137 | ||||
-rw-r--r-- | emulator/audio/driver/audio_vbuffer.h | 47 | ||||
-rw-r--r-- | emulator/audio/driver/ext_pcm.c | 94 | ||||
-rw-r--r-- | emulator/audio/driver/ext_pcm.h | 39 |
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 |