blob: 054c77a63053f0aa3dccbde0fd950546ac3603b6 [file] [log] [blame]
Paul McLean98ca8d42015-01-13 09:41:36 -08001/*
2 * Copyright (C) 2014 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 "alsa_device_profile"
18/*#define LOG_NDEBUG 0*/
19/*#define LOG_PCM_PARAMS 0*/
20
21#include <errno.h>
22#include <inttypes.h>
23#include <stdint.h>
24#include <stdlib.h>
25
26#include <log/log.h>
27
28#include "include/alsa_device_profile.h"
29#include "include/alsa_format.h"
30#include "include/alsa_logging.h"
31
32#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
33
34/*TODO - Evaluate if this value should/can be retrieved from a device-specific property */
35#define BUFF_DURATION_MS 5
36
37#define DEFAULT_PERIOD_SIZE 1024
38
39static const char * const format_string_map[] = {
40 "AUDIO_FORMAT_PCM_16_BIT", /* "PCM_FORMAT_S16_LE", */
41 "AUDIO_FORMAT_PCM_32_BIT", /* "PCM_FORMAT_S32_LE", */
42 "AUDIO_FORMAT_PCM_8_BIT", /* "PCM_FORMAT_S8", */
43 "AUDIO_FORMAT_PCM_8_24_BIT", /* "PCM_FORMAT_S24_LE", */
44 "AUDIO_FORMAT_PCM_24_BIT_PACKED"/* "PCM_FORMAT_S24_3LE" */
45};
46
Paul McLean98ca8d42015-01-13 09:41:36 -080047extern int8_t const pcm_format_value_map[50];
48
Andy Hungb2906702015-05-06 09:58:05 -070049/* Sort these in terms of preference (best first).
50 192 kHz is not first because it requires significant resources for possibly worse
51 quality and driver instability (depends on device).
52 The order here determines the default sample rate for the device.
53 AudioPolicyManager may not respect this ordering when picking sample rates.
54 Update MAX_PROFILE_SAMPLE_RATES after changing the array size.
55
56 TODO: remove 32000, 22050, 12000, 11025? Each sample rate check
57 requires opening the device which may cause pops. */
Paul McLean98ca8d42015-01-13 09:41:36 -080058static const unsigned std_sample_rates[] =
Andy Hungb2906702015-05-06 09:58:05 -070059 {96000, 88200, 192000, 176400, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000};
Paul McLean98ca8d42015-01-13 09:41:36 -080060
61static void profile_reset(alsa_device_profile* profile)
62{
63 profile->card = profile->device = -1;
64
Andy Hungb2906702015-05-06 09:58:05 -070065 /* terminate the attribute arrays with invalid values */
66 profile->formats[0] = PCM_FORMAT_INVALID;
67 profile->sample_rates[0] = 0;
68 profile->channel_counts[0] = 0;
Paul McLean98ca8d42015-01-13 09:41:36 -080069
70 profile->min_period_size = profile->max_period_size = 0;
71 profile->min_channel_count = profile->max_channel_count = DEFAULT_CHANNEL_COUNT;
72
73 profile->is_valid = false;
74}
75
76void profile_init(alsa_device_profile* profile, int direction)
77{
78 profile->direction = direction;
79 profile_reset(profile);
80}
81
82bool profile_is_initialized(alsa_device_profile* profile)
83{
84 return profile->card >= 0 && profile->device >= 0;
85}
86
87bool profile_is_valid(alsa_device_profile* profile) {
88 return profile->is_valid;
89}
90
91bool profile_is_cached_for(alsa_device_profile* profile, int card, int device) {
92 return card == profile->card && device == profile->device;
93}
94
95void profile_decache(alsa_device_profile* profile) {
96 profile_reset(profile);
97}
98
99/*
100 * Returns the supplied value rounded up to the next even multiple of 16
101 */
102static unsigned int round_to_16_mult(unsigned int size)
103{
Paul McLean52dc1132015-04-04 13:28:27 -0700104 return (size + 15) & ~15; /* 0xFFFFFFF0; */
Paul McLean98ca8d42015-01-13 09:41:36 -0800105}
106
107/*
108 * Returns the system defined minimum period size based on the supplied sample rate.
109 */
110unsigned profile_calc_min_period_size(alsa_device_profile* profile, unsigned sample_rate)
111{
112 ALOGV("profile_calc_min_period_size(%p, rate:%d)", profile, sample_rate);
113 if (profile == NULL) {
114 return DEFAULT_PERIOD_SIZE;
115 } else {
116 unsigned num_sample_frames = (sample_rate * BUFF_DURATION_MS) / 1000;
117 if (num_sample_frames < profile->min_period_size) {
118 num_sample_frames = profile->min_period_size;
119 }
120 return round_to_16_mult(num_sample_frames) * 2;
121 }
122}
123
124unsigned int profile_get_period_size(alsa_device_profile* profile, unsigned sample_rate)
125{
Paul McLean98ca8d42015-01-13 09:41:36 -0800126 unsigned int period_size = profile_calc_min_period_size(profile, sample_rate);
127 ALOGV("profile_get_period_size(rate:%d) = %d", sample_rate, period_size);
128 return period_size;
129}
130
131/*
132 * Sample Rate
133 */
134unsigned profile_get_default_sample_rate(alsa_device_profile* profile)
135{
136 /*
137 * TODO this won't be right in general. we should store a preferred rate as we are scanning.
138 * But right now it will return the highest rate, which may be correct.
139 */
140 return profile_is_valid(profile) ? profile->sample_rates[0] : DEFAULT_SAMPLE_RATE;
141}
142
143bool profile_is_sample_rate_valid(alsa_device_profile* profile, unsigned rate)
144{
145 if (profile_is_valid(profile)) {
146 size_t index;
147 for (index = 0; profile->sample_rates[index] != 0; index++) {
148 if (profile->sample_rates[index] == rate) {
149 return true;
150 }
151 }
152
153 return false;
154 } else {
155 return rate == DEFAULT_SAMPLE_RATE;
156 }
157}
158
159/*
160 * Format
161 */
162enum pcm_format profile_get_default_format(alsa_device_profile* profile)
163{
164 /*
165 * TODO this won't be right in general. we should store a preferred format as we are scanning.
166 */
167 return profile_is_valid(profile) ? profile->formats[0] : DEFAULT_SAMPLE_FORMAT;
168}
169
170bool profile_is_format_valid(alsa_device_profile* profile, enum pcm_format fmt) {
171 if (profile_is_valid(profile)) {
172 size_t index;
173 for (index = 0; profile->formats[index] != PCM_FORMAT_INVALID; index++) {
174 if (profile->formats[index] == fmt) {
175 return true;
176 }
177 }
178
179 return false;
180 } else {
181 return fmt == DEFAULT_SAMPLE_FORMAT;
182 }
183}
184
185/*
186 * Channels
187 */
188unsigned profile_get_default_channel_count(alsa_device_profile* profile)
189{
190 return profile_is_valid(profile) ? profile->channel_counts[0] : DEFAULT_CHANNEL_COUNT;
191}
192
193bool profile_is_channel_count_valid(alsa_device_profile* profile, unsigned count)
194{
195 if (profile_is_initialized(profile)) {
196 return count >= profile->min_channel_count && count <= profile->max_channel_count;
197 } else {
198 return count == DEFAULT_CHANNEL_COUNT;
199 }
200}
201
202static bool profile_test_sample_rate(alsa_device_profile* profile, unsigned rate)
203{
204 struct pcm_config config = profile->default_config;
205 config.rate = rate;
206
207 bool works = false; /* let's be pessimistic */
208 struct pcm * pcm = pcm_open(profile->card, profile->device,
209 profile->direction, &config);
210
211 if (pcm != NULL) {
212 works = pcm_is_ready(pcm);
213 pcm_close(pcm);
214 }
215
216 return works;
217}
218
219static unsigned profile_enum_sample_rates(alsa_device_profile* profile, unsigned min, unsigned max)
220{
221 unsigned num_entries = 0;
222 unsigned index;
223
224 for (index = 0; index < ARRAY_SIZE(std_sample_rates) &&
225 num_entries < ARRAY_SIZE(profile->sample_rates) - 1;
226 index++) {
227 if (std_sample_rates[index] >= min && std_sample_rates[index] <= max
228 && profile_test_sample_rate(profile, std_sample_rates[index])) {
229 profile->sample_rates[num_entries++] = std_sample_rates[index];
230 }
231 }
Andy Hungb2906702015-05-06 09:58:05 -0700232 profile->sample_rates[num_entries] = 0; /* terminate */
Paul McLean98ca8d42015-01-13 09:41:36 -0800233 return num_entries; /* return # of supported rates */
234}
235
236static unsigned profile_enum_sample_formats(alsa_device_profile* profile, struct pcm_mask * mask)
237{
238 const int num_slots = ARRAY_SIZE(mask->bits);
239 const int bits_per_slot = sizeof(mask->bits[0]) * 8;
240
241 const int table_size = ARRAY_SIZE(pcm_format_value_map);
242
243 int slot_index, bit_index, table_index;
244 table_index = 0;
245 int num_written = 0;
246 for (slot_index = 0; slot_index < num_slots && table_index < table_size;
247 slot_index++) {
248 unsigned bit_mask = 1;
249 for (bit_index = 0;
250 bit_index < bits_per_slot && table_index < table_size;
251 bit_index++) {
252 if ((mask->bits[slot_index] & bit_mask) != 0) {
253 enum pcm_format format = pcm_format_value_map[table_index];
254 /* Never return invalid (unrecognized) or 8-bit */
255 if (format != PCM_FORMAT_INVALID && format != PCM_FORMAT_S8) {
256 profile->formats[num_written++] = format;
257 if (num_written == ARRAY_SIZE(profile->formats) - 1) {
258 /* leave at least one PCM_FORMAT_INVALID at the end */
Andy Hungb2906702015-05-06 09:58:05 -0700259 goto end;
Paul McLean98ca8d42015-01-13 09:41:36 -0800260 }
261 }
262 }
263 bit_mask <<= 1;
264 table_index++;
265 }
266 }
Andy Hungb2906702015-05-06 09:58:05 -0700267end:
268 profile->formats[num_written] = PCM_FORMAT_INVALID;
Paul McLean98ca8d42015-01-13 09:41:36 -0800269 return num_written;
270}
271
272static unsigned profile_enum_channel_counts(alsa_device_profile* profile, unsigned min,
273 unsigned max)
274{
Andy Hunge7d87cb2015-06-12 11:26:54 -0700275 /* modify alsa_device_profile.h if you change the std_channel_counts[] array. */
276 static const unsigned std_channel_counts[] = {8, 7, 6, 5, 4, 3, 2, 1};
Paul McLean98ca8d42015-01-13 09:41:36 -0800277
278 unsigned num_counts = 0;
279 unsigned index;
280 /* TODO write a profile_test_channel_count() */
281 /* Ensure there is at least one invalid channel count to terminate the channel counts array */
282 for (index = 0; index < ARRAY_SIZE(std_channel_counts) &&
283 num_counts < ARRAY_SIZE(profile->channel_counts) - 1;
284 index++) {
285 /* TODO Do we want a channel counts test? */
286 if (std_channel_counts[index] >= min && std_channel_counts[index] <= max /* &&
287 profile_test_channel_count(profile, channel_counts[index])*/) {
288 profile->channel_counts[num_counts++] = std_channel_counts[index];
289 }
290 }
Andy Hungdce0eca2015-09-28 16:31:54 -0700291 // if we have no match with the standard counts, we use the largest (preferred) std count.
292 if (num_counts == 0) {
293 ALOGW("usb device does not match std channel counts, setting to %d",
294 std_channel_counts[0]);
295 profile->channel_counts[num_counts++] = std_channel_counts[0];
296 }
Andy Hungb2906702015-05-06 09:58:05 -0700297 profile->channel_counts[num_counts] = 0;
Paul McLean98ca8d42015-01-13 09:41:36 -0800298 return num_counts; /* return # of supported counts */
299}
300
301/*
302 * Reads and decodes configuration info from the specified ALSA card/device.
303 */
304static int read_alsa_device_config(alsa_device_profile * profile, struct pcm_config * config)
305{
306 ALOGV("usb:audio_hw - read_alsa_device_config(c:%d d:%d t:0x%X)",
307 profile->card, profile->device, profile->direction);
308
309 if (profile->card < 0 || profile->device < 0) {
310 return -EINVAL;
311 }
312
313 struct pcm_params * alsa_hw_params =
314 pcm_params_get(profile->card, profile->device, profile->direction);
315 if (alsa_hw_params == NULL) {
316 return -EINVAL;
317 }
318
319 profile->min_period_size = pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_SIZE);
320 profile->max_period_size = pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_SIZE);
321
322 profile->min_channel_count = pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS);
323 profile->max_channel_count = pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS);
324
325 int ret = 0;
326
327 /*
328 * This Logging will be useful when testing new USB devices.
329 */
330#ifdef LOG_PCM_PARAMS
331 log_pcm_params(alsa_hw_params);
332#endif
333
334 config->channels = pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS);
335 config->rate = pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE);
336 config->period_size = profile_calc_min_period_size(profile, config->rate);
337 config->period_count = pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIODS);
338 config->format = get_pcm_format_for_mask(pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT));
339#ifdef LOG_PCM_PARAMS
340 log_pcm_config(config, "read_alsa_device_config");
341#endif
342 if (config->format == PCM_FORMAT_INVALID) {
343 ret = -EINVAL;
344 }
345
346 pcm_params_free(alsa_hw_params);
347
348 return ret;
349}
350
351bool profile_read_device_info(alsa_device_profile* profile)
352{
353 if (!profile_is_initialized(profile)) {
354 return false;
355 }
356
357 /* let's get some defaults */
358 read_alsa_device_config(profile, &profile->default_config);
359 ALOGV("default_config chans:%d rate:%d format:%d count:%d size:%d",
360 profile->default_config.channels, profile->default_config.rate,
361 profile->default_config.format, profile->default_config.period_count,
362 profile->default_config.period_size);
363
364 struct pcm_params * alsa_hw_params = pcm_params_get(profile->card,
365 profile->device,
366 profile->direction);
367 if (alsa_hw_params == NULL) {
368 return false;
369 }
370
371 /* Formats */
372 struct pcm_mask * format_mask = pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT);
373 profile_enum_sample_formats(profile, format_mask);
374
375 /* Channels */
376 profile_enum_channel_counts(
377 profile, pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS),
378 pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS));
379
380 /* Sample Rates */
381 profile_enum_sample_rates(
382 profile, pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE),
383 pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE));
384
385 profile->is_valid = true;
386
387 return true;
388}
389
390char * profile_get_sample_rate_strs(alsa_device_profile* profile)
391{
Paul McLean52dc1132015-04-04 13:28:27 -0700392 /* if we assume that rate strings are about 5 characters (48000 is 5), plus ~1 for a
393 * delimiter "|" this buffer has room for about 22 rate strings which seems like
394 * way too much, but it's a stack variable so only temporary.
395 */
Paul McLean98ca8d42015-01-13 09:41:36 -0800396 char buffer[128];
397 buffer[0] = '\0';
Paul McLean52dc1132015-04-04 13:28:27 -0700398 size_t buffSize = ARRAY_SIZE(buffer);
399 size_t curStrLen = 0;
Paul McLean98ca8d42015-01-13 09:41:36 -0800400
401 char numBuffer[32];
402
Paul McLean52dc1132015-04-04 13:28:27 -0700403 size_t numEntries = 0;
404 size_t index;
Paul McLean98ca8d42015-01-13 09:41:36 -0800405 for (index = 0; profile->sample_rates[index] != 0; index++) {
Paul McLean98ca8d42015-01-13 09:41:36 -0800406 snprintf(numBuffer, sizeof(numBuffer), "%u", profile->sample_rates[index]);
Paul McLean52dc1132015-04-04 13:28:27 -0700407 // account for both the null, and potentially the bar.
408 if (buffSize - curStrLen < strlen(numBuffer) + (numEntries != 0 ? 2 : 1)) {
409 /* we don't have room for another, so bail at this point rather than
410 * return a malformed rate string
411 */
412 break;
413 }
414 if (numEntries++ != 0) {
415 strlcat(buffer, "|", buffSize);
416 }
417 curStrLen = strlcat(buffer, numBuffer, buffSize);
Paul McLean98ca8d42015-01-13 09:41:36 -0800418 }
419
420 return strdup(buffer);
421}
422
423char * profile_get_format_strs(alsa_device_profile* profile)
424{
Paul McLean52dc1132015-04-04 13:28:27 -0700425 /* if we assume that format strings are about 24 characters (AUDIO_FORMAT_PCM_16_BIT is 23),
426 * plus ~1 for a delimiter "|" this buffer has room for about 10 format strings which seems
427 * like way too much, but it's a stack variable so only temporary.
428 */
429 char buffer[256];
Paul McLean98ca8d42015-01-13 09:41:36 -0800430 buffer[0] = '\0';
Paul McLean52dc1132015-04-04 13:28:27 -0700431 size_t buffSize = ARRAY_SIZE(buffer);
432 size_t curStrLen = 0;
Paul McLean98ca8d42015-01-13 09:41:36 -0800433
Paul McLean52dc1132015-04-04 13:28:27 -0700434 size_t numEntries = 0;
435 size_t index = 0;
Paul McLean98ca8d42015-01-13 09:41:36 -0800436 for (index = 0; profile->formats[index] != PCM_FORMAT_INVALID; index++) {
Paul McLean52dc1132015-04-04 13:28:27 -0700437 // account for both the null, and potentially the bar.
438 if (buffSize - curStrLen < strlen(format_string_map[profile->formats[index]])
439 + (numEntries != 0 ? 2 : 1)) {
440 /* we don't have room for another, so bail at this point rather than
441 * return a malformed rate string
442 */
443 break;
Paul McLean98ca8d42015-01-13 09:41:36 -0800444 }
Paul McLean52dc1132015-04-04 13:28:27 -0700445 if (numEntries++ != 0) {
446 strlcat(buffer, "|", buffSize);
447 }
448 curStrLen = strlcat(buffer, format_string_map[profile->formats[index]], buffSize);
Paul McLean98ca8d42015-01-13 09:41:36 -0800449 }
450
451 return strdup(buffer);
452}
453
454char * profile_get_channel_count_strs(alsa_device_profile* profile)
455{
Andy Hunge7d87cb2015-06-12 11:26:54 -0700456 // FIXME implicit fixed channel count assumption here (FCC_8).
457 // we use only the canonical even number channel position masks.
Paul McLean98ca8d42015-01-13 09:41:36 -0800458 static const char * const out_chans_strs[] = {
459 /* 0 */"AUDIO_CHANNEL_NONE", /* will never be taken as this is a terminator */
460 /* 1 */"AUDIO_CHANNEL_OUT_MONO",
461 /* 2 */"AUDIO_CHANNEL_OUT_STEREO",
462 /* 3 */ /* "AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_FRONT_CENTER" */ NULL,
463 /* 4 */"AUDIO_CHANNEL_OUT_QUAD",
464 /* 5 */ /* "AUDIO_CHANNEL_OUT_QUAD|AUDIO_CHANNEL_OUT_FRONT_CENTER" */ NULL,
465 /* 6 */"AUDIO_CHANNEL_OUT_5POINT1",
466 /* 7 */ /* "AUDIO_CHANNEL_OUT_5POINT1|AUDIO_CHANNEL_OUT_BACK_CENTER" */ NULL,
Andy Hunge7d87cb2015-06-12 11:26:54 -0700467 /* 8 */"AUDIO_CHANNEL_OUT_7POINT1",
Paul McLean98ca8d42015-01-13 09:41:36 -0800468 /* channel counts greater than this not considered */
469 };
470
471 static const char * const in_chans_strs[] = {
472 /* 0 */"AUDIO_CHANNEL_NONE", /* will never be taken as this is a terminator */
473 /* 1 */"AUDIO_CHANNEL_IN_MONO",
474 /* 2 */"AUDIO_CHANNEL_IN_STEREO",
475 /* channel counts greater than this not considered */
476 };
477
Andy Hung3f822c52015-04-21 13:31:16 -0700478 static const char * const index_chans_strs[] = {
479 /* 0 */"AUDIO_CHANNEL_NONE", /* will never be taken as this is a terminator */
480 /* 1 */"AUDIO_CHANNEL_INDEX_MASK_1",
481 /* 2 */"AUDIO_CHANNEL_INDEX_MASK_2",
482 /* 3 */"AUDIO_CHANNEL_INDEX_MASK_3",
483 /* 4 */"AUDIO_CHANNEL_INDEX_MASK_4",
484 /* 5 */"AUDIO_CHANNEL_INDEX_MASK_5",
485 /* 6 */"AUDIO_CHANNEL_INDEX_MASK_6",
486 /* 7 */"AUDIO_CHANNEL_INDEX_MASK_7",
487 /* 8 */"AUDIO_CHANNEL_INDEX_MASK_8",
488 };
489
Paul McLean98ca8d42015-01-13 09:41:36 -0800490 const bool isOutProfile = profile->direction == PCM_OUT;
491
Paul McLean52dc1132015-04-04 13:28:27 -0700492 const char * const * const chans_strs = isOutProfile ? out_chans_strs : in_chans_strs;
493 const size_t chans_strs_size =
494 isOutProfile ? ARRAY_SIZE(out_chans_strs) : ARRAY_SIZE(in_chans_strs);
Paul McLean98ca8d42015-01-13 09:41:36 -0800495
Paul McLean52dc1132015-04-04 13:28:27 -0700496 /*
Andy Hung3f822c52015-04-21 13:31:16 -0700497 * If we assume each channel string is 26 chars ("AUDIO_CHANNEL_INDEX_MASK_8" is 26) + 1 for,
498 * the "|" delimiter, then we allocate room for 16 strings.
Paul McLean52dc1132015-04-04 13:28:27 -0700499 */
Andy Hung3f822c52015-04-21 13:31:16 -0700500 char buffer[27 * 16 + 1]; /* caution, may need to be expanded */
Paul McLean98ca8d42015-01-13 09:41:36 -0800501 buffer[0] = '\0';
Paul McLean52dc1132015-04-04 13:28:27 -0700502 size_t buffSize = ARRAY_SIZE(buffer);
503 size_t curStrLen = 0;
504
Paul McLean98ca8d42015-01-13 09:41:36 -0800505 /* We currently support MONO and STEREO, and always report STEREO but some (many)
506 * USB Audio Devices may only announce support for MONO (a headset mic for example), or
507 * The total number of output channels. SO, if the device itself doesn't explicitly
508 * support STEREO, append to the channel config strings we are generating.
Andy Hunge7d87cb2015-06-12 11:26:54 -0700509 *
510 * The MONO and STEREO positional channel masks are provided for legacy compatibility.
511 * For multichannel (n > 2) we only expose channel index masks.
Paul McLean98ca8d42015-01-13 09:41:36 -0800512 */
Paul McLean52dc1132015-04-04 13:28:27 -0700513 // Always support stereo
514 curStrLen = strlcat(buffer, chans_strs[2], buffSize);
515
516 size_t index;
Paul McLean98ca8d42015-01-13 09:41:36 -0800517 unsigned channel_count;
Paul McLean52dc1132015-04-04 13:28:27 -0700518 for (index = 0;
Andy Hung3f822c52015-04-21 13:31:16 -0700519 (channel_count = profile->channel_counts[index]) != 0;
Paul McLean52dc1132015-04-04 13:28:27 -0700520 index++) {
Andy Hunge7d87cb2015-06-12 11:26:54 -0700521
522 /* we only show positional information for mono (stereo handled already) */
Andy Hung3f822c52015-04-21 13:31:16 -0700523 if (channel_count < chans_strs_size
524 && chans_strs[channel_count] != NULL
Andy Hunge7d87cb2015-06-12 11:26:54 -0700525 && channel_count < 2 /* positional only for fewer than 2 channels */) {
Paul McLean52dc1132015-04-04 13:28:27 -0700526 // account for the '|' and the '\0'
527 if (buffSize - curStrLen < strlen(chans_strs[channel_count]) + 2) {
528 /* we don't have room for another, so bail at this point rather than
529 * return a malformed rate string
530 */
531 break;
Paul McLean98ca8d42015-01-13 09:41:36 -0800532 }
Paul McLean98ca8d42015-01-13 09:41:36 -0800533
Paul McLean52dc1132015-04-04 13:28:27 -0700534 strlcat(buffer, "|", buffSize);
535 curStrLen = strlcat(buffer, chans_strs[channel_count], buffSize);
Paul McLean98ca8d42015-01-13 09:41:36 -0800536 }
Andy Hung3f822c52015-04-21 13:31:16 -0700537
Andy Hungdca2ec02015-05-05 23:20:25 -0700538 // handle channel index masks for both input and output
539 // +2 to account for the '|' and the '\0'
540 if (buffSize - curStrLen < strlen(index_chans_strs[channel_count]) + 2) {
541 /* we don't have room for another, so bail at this point rather than
542 * return a malformed rate string
543 */
544 break;
545 }
546
547 strlcat(buffer, "|", buffSize);
548 curStrLen = strlcat(buffer, index_chans_strs[channel_count], buffSize);
Paul McLean98ca8d42015-01-13 09:41:36 -0800549 }
550
551 return strdup(buffer);
552}