• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "audio_hw_generic"
18 
19 #include <errno.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 
24 #include <log/log.h>
25 #include <cutils/str_parms.h>
26 
27 #include "ext_pcm.h"
28 
29 static pthread_mutex_t ext_pcm_init_lock = PTHREAD_MUTEX_INITIALIZER;
30 static struct ext_pcm *shared_ext_pcm = NULL;
31 
32 // Sleep 10ms between each mixing, this interval value is arbitrary chosen
33 #define MIXER_INTERVAL_MS 10
34 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
35 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
36 
37 /* copied from libcutils/str_parms.c */
str_eq(void * key_a,void * key_b)38 static bool str_eq(void *key_a, void *key_b) {
39   return !strcmp((const char *)key_a, (const char *)key_b);
40 }
41 
42 /**
43  * use djb hash unless we find it inadequate.
44  * copied from libcutils/str_parms.c
45  */
46 #ifdef __clang__
47 __attribute__((no_sanitize("integer")))
48 #endif
str_hash_fn(void * str)49 static int str_hash_fn(void *str) {
50   uint32_t hash = 5381;
51   char *p;
52   for (p = str; p && *p; p++) {
53     hash = ((hash << 5) + hash) + *p;
54   }
55   return (int)hash;
56 }
57 
mixer_thread_mix(__unused void * key,void * value,void * context)58 static bool mixer_thread_mix(__unused void *key, void *value, void *context) {
59   struct ext_mixer_pipeline *pipeline_out = (struct ext_mixer_pipeline *)context;
60   struct ext_mixer_pipeline *pipeline_in = (struct ext_mixer_pipeline *)value;
61   pipeline_out->position = MAX(pipeline_out->position, pipeline_in->position);
62   for (int i = 0; i < pipeline_out->position; i++) {
63     float mixed = pipeline_out->buffer[i] + pipeline_in->buffer[i];
64     if (mixed > INT16_MAX) pipeline_out->buffer[i] = INT16_MAX;
65     else if (mixed < INT16_MIN) pipeline_out->buffer[i] = INT16_MIN;
66     else pipeline_out->buffer[i] = (int16_t)mixed;
67   }
68   memset(pipeline_in, 0, sizeof(struct ext_mixer_pipeline));
69   return true;
70 }
71 
mixer_thread_loop(void * context)72 static void *mixer_thread_loop(void *context) {
73   ALOGD("%s: __enter__", __func__);
74   struct ext_pcm *ext_pcm = (struct ext_pcm *)context;
75   do {
76     pthread_mutex_lock(&ext_pcm->mixer_lock);
77     ext_pcm->mixer_pipeline.position = 0;
78     // Combine the output from every pipeline into one output buffer
79     hashmapForEach(ext_pcm->mixer_pipeline_map, mixer_thread_mix,
80         &ext_pcm->mixer_pipeline);
81     if (ext_pcm->mixer_pipeline.position > 0) {
82       pcm_write(ext_pcm->pcm, (void *)ext_pcm->mixer_pipeline.buffer,
83           ext_pcm->mixer_pipeline.position * sizeof(int16_t));
84     }
85     memset(&ext_pcm->mixer_pipeline, 0, sizeof(struct ext_mixer_pipeline));
86     pthread_mutex_unlock(&ext_pcm->mixer_lock);
87     usleep(MIXER_INTERVAL_MS * 1000);
88   } while (1);
89 }
90 
mixer_pipeline_write(struct ext_pcm * ext_pcm,const char * bus_address,const void * data,unsigned int count)91 static int mixer_pipeline_write(struct ext_pcm *ext_pcm, const char *bus_address,
92                                 const void *data, unsigned int count) {
93   pthread_mutex_lock(&ext_pcm->mixer_lock);
94   struct ext_mixer_pipeline *pipeline = hashmapGet(
95       ext_pcm->mixer_pipeline_map, bus_address);
96   if (!pipeline) {
97     pipeline = calloc(1, sizeof(struct ext_mixer_pipeline));
98     hashmapPut(ext_pcm->mixer_pipeline_map, bus_address, pipeline);
99   }
100   unsigned int byteCount = MIN(count,
101       (MIXER_BUFFER_SIZE - pipeline->position) * sizeof(int16_t));
102   unsigned int int16Count = byteCount / sizeof(int16_t);
103   if (int16Count > 0) {
104     memcpy(&pipeline->buffer[pipeline->position], data, byteCount);
105     pipeline->position += int16Count;
106   }
107   pthread_mutex_unlock(&ext_pcm->mixer_lock);
108   return 0;
109 }
110 
ext_pcm_open(unsigned int card,unsigned int device,unsigned int flags,struct pcm_config * config)111 struct ext_pcm *ext_pcm_open(unsigned int card, unsigned int device,
112                              unsigned int flags, struct pcm_config *config) {
113   pthread_mutex_lock(&ext_pcm_init_lock);
114   if (shared_ext_pcm == NULL) {
115     shared_ext_pcm = calloc(1, sizeof(struct ext_pcm));
116     pthread_mutex_init(&shared_ext_pcm->lock, (const pthread_mutexattr_t *) NULL);
117     shared_ext_pcm->pcm = pcm_open(card, device, flags, config);
118     pthread_mutex_init(&shared_ext_pcm->mixer_lock, (const pthread_mutexattr_t *)NULL);
119     pthread_create(&shared_ext_pcm->mixer_thread, (const pthread_attr_t *)NULL,
120             mixer_thread_loop, shared_ext_pcm);
121     shared_ext_pcm->mixer_pipeline_map = hashmapCreate(8, str_hash_fn, str_eq);
122   }
123   pthread_mutex_unlock(&ext_pcm_init_lock);
124 
125   pthread_mutex_lock(&shared_ext_pcm->lock);
126   shared_ext_pcm->ref_count += 1;
127   pthread_mutex_unlock(&shared_ext_pcm->lock);
128 
129   return shared_ext_pcm;
130 }
131 
mixer_free_pipeline(__unused void * key,void * value,void * context)132 static bool mixer_free_pipeline(__unused void *key, void *value, void *context) {
133   struct ext_mixer_pipeline *pipeline = (struct ext_mixer_pipeline *)value;
134   free(pipeline);
135   return true;
136 }
137 
ext_pcm_close(struct ext_pcm * ext_pcm)138 int ext_pcm_close(struct ext_pcm *ext_pcm) {
139   if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
140     return -EINVAL;
141   }
142 
143   pthread_mutex_lock(&ext_pcm->lock);
144   ext_pcm->ref_count -= 1;
145   pthread_mutex_unlock(&ext_pcm->lock);
146 
147   pthread_mutex_lock(&ext_pcm_init_lock);
148   if (ext_pcm->ref_count <= 0) {
149     pthread_mutex_destroy(&ext_pcm->lock);
150     pcm_close(ext_pcm->pcm);
151     pthread_mutex_destroy(&ext_pcm->mixer_lock);
152     hashmapForEach(ext_pcm->mixer_pipeline_map, mixer_free_pipeline,
153         (void *)NULL);
154     hashmapFree(ext_pcm->mixer_pipeline_map);
155     pthread_kill(ext_pcm->mixer_thread, SIGINT);
156     free(ext_pcm);
157     shared_ext_pcm = NULL;
158   }
159   pthread_mutex_unlock(&ext_pcm_init_lock);
160   return 0;
161 }
162 
ext_pcm_is_ready(struct ext_pcm * ext_pcm)163 int ext_pcm_is_ready(struct ext_pcm *ext_pcm) {
164   if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
165     return 0;
166   }
167 
168   return pcm_is_ready(ext_pcm->pcm);
169 }
170 
ext_pcm_write(struct ext_pcm * ext_pcm,const char * address,const void * data,unsigned int count)171 int ext_pcm_write(struct ext_pcm *ext_pcm, const char *address,
172                   const void *data, unsigned int count) {
173   if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
174     return -EINVAL;
175   }
176 
177   return mixer_pipeline_write(ext_pcm, address, data, count);
178 }
179 
ext_pcm_get_error(struct ext_pcm * ext_pcm)180 const char *ext_pcm_get_error(struct ext_pcm *ext_pcm) {
181   if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
182     return NULL;
183   }
184 
185   return pcm_get_error(ext_pcm->pcm);
186 }
187 
ext_pcm_frames_to_bytes(struct ext_pcm * ext_pcm,unsigned int frames)188 unsigned int ext_pcm_frames_to_bytes(struct ext_pcm *ext_pcm,
189                                      unsigned int frames) {
190   if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
191     return -EINVAL;
192   }
193 
194   return pcm_frames_to_bytes(ext_pcm->pcm, frames);
195 }
196