1 /*
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 #include <cutils/properties.h>
26 
27 #include <log/log.h>
28 
29 #include "include/alsa_device_profile.h"
30 #include "include/alsa_format.h"
31 #include "include/alsa_logging.h"
32 
33 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
34 
35 #define PERIOD_DURATION_US (5 * 1000)
36 
37 #define DEFAULT_PERIOD_SIZE 1024
38 
39 static 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 
47 extern int8_t const pcm_format_value_map[50];
48 
49 /* 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. */
58 static const unsigned std_sample_rates[] =
59     {96000, 88200, 192000, 176400, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000};
60 
profile_reset(alsa_device_profile * profile)61 static void profile_reset(alsa_device_profile* profile)
62 {
63     profile->card = profile->device = -1;
64 
65     /* 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;
69 
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 
profile_init(alsa_device_profile * profile,int direction)76 void profile_init(alsa_device_profile* profile, int direction)
77 {
78     profile->direction = direction;
79     profile_reset(profile);
80 }
81 
profile_is_initialized(alsa_device_profile * profile)82 bool profile_is_initialized(alsa_device_profile* profile)
83 {
84     return profile->card >= 0 && profile->device >= 0;
85 }
86 
profile_is_valid(alsa_device_profile * profile)87 bool profile_is_valid(alsa_device_profile* profile) {
88     return profile->is_valid;
89 }
90 
profile_is_cached_for(alsa_device_profile * profile,int card,int device)91 bool profile_is_cached_for(alsa_device_profile* profile, int card, int device) {
92     return card == profile->card && device == profile->device;
93 }
94 
profile_decache(alsa_device_profile * profile)95 void 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  */
round_to_16_mult(unsigned int size)102 static unsigned int round_to_16_mult(unsigned int size)
103 {
104     return (size + 15) & ~15;   /* 0xFFFFFFF0; */
105 }
106 
107 /*
108  * Returns the system defined minimum period size based on the supplied sample rate.
109  */
profile_calc_min_period_size(alsa_device_profile * profile,unsigned sample_rate)110 unsigned 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 period_us = property_get_int32("ro.audio.usb.period_us", PERIOD_DURATION_US);
117         unsigned num_sample_frames = ((uint64_t)sample_rate * period_us) / 1000000;
118 
119         if (num_sample_frames < profile->min_period_size) {
120             num_sample_frames = profile->min_period_size;
121         }
122         return round_to_16_mult(num_sample_frames);
123     }
124 }
125 
profile_get_period_size(alsa_device_profile * profile,unsigned sample_rate)126 unsigned int profile_get_period_size(alsa_device_profile* profile, unsigned sample_rate)
127 {
128     unsigned int period_size = profile_calc_min_period_size(profile, sample_rate);
129     ALOGV("profile_get_period_size(rate:%d) = %d", sample_rate, period_size);
130     return period_size;
131 }
132 
133 /*
134  * Sample Rate
135  */
profile_get_default_sample_rate(alsa_device_profile * profile)136 unsigned profile_get_default_sample_rate(alsa_device_profile* profile)
137 {
138     /*
139      * TODO this won't be right in general. we should store a preferred rate as we are scanning.
140      * But right now it will return the highest rate, which may be correct.
141      */
142     return profile_is_valid(profile) ? profile->sample_rates[0] : DEFAULT_SAMPLE_RATE;
143 }
144 
profile_is_sample_rate_valid(alsa_device_profile * profile,unsigned rate)145 bool profile_is_sample_rate_valid(alsa_device_profile* profile, unsigned rate)
146 {
147     if (profile_is_valid(profile)) {
148         size_t index;
149         for (index = 0; profile->sample_rates[index] != 0; index++) {
150             if (profile->sample_rates[index] == rate) {
151                 return true;
152             }
153         }
154 
155         return false;
156     } else {
157         return rate == DEFAULT_SAMPLE_RATE;
158     }
159 }
160 
161 /*
162  * Format
163  */
profile_get_default_format(alsa_device_profile * profile)164 enum pcm_format profile_get_default_format(alsa_device_profile* profile)
165 {
166     /*
167      * TODO this won't be right in general. we should store a preferred format as we are scanning.
168      */
169     return profile_is_valid(profile) ? profile->formats[0] : DEFAULT_SAMPLE_FORMAT;
170 }
171 
profile_is_format_valid(alsa_device_profile * profile,enum pcm_format fmt)172 bool profile_is_format_valid(alsa_device_profile* profile, enum pcm_format fmt) {
173     if (profile_is_valid(profile)) {
174         size_t index;
175         for (index = 0; profile->formats[index] != PCM_FORMAT_INVALID; index++) {
176             if (profile->formats[index] == fmt) {
177                 return true;
178             }
179         }
180 
181         return false;
182     } else {
183         return fmt == DEFAULT_SAMPLE_FORMAT;
184     }
185 }
186 
187 /*
188  * Channels
189  */
profile_get_default_channel_count(alsa_device_profile * profile)190 unsigned profile_get_default_channel_count(alsa_device_profile* profile)
191 {
192     return profile_is_valid(profile) ? profile->channel_counts[0] : DEFAULT_CHANNEL_COUNT;
193 }
194 
profile_is_channel_count_valid(alsa_device_profile * profile,unsigned count)195 bool profile_is_channel_count_valid(alsa_device_profile* profile, unsigned count)
196 {
197     if (profile_is_initialized(profile)) {
198         return count >= profile->min_channel_count && count <= profile->max_channel_count;
199     } else {
200         return count == DEFAULT_CHANNEL_COUNT;
201     }
202 }
203 
profile_test_sample_rate(alsa_device_profile * profile,unsigned rate)204 static bool profile_test_sample_rate(alsa_device_profile* profile, unsigned rate)
205 {
206     struct pcm_config config = profile->default_config;
207     config.rate = rate;
208 
209     bool works = false; /* let's be pessimistic */
210     struct pcm * pcm = pcm_open(profile->card, profile->device,
211                                 profile->direction, &config);
212 
213     if (pcm != NULL) {
214         works = pcm_is_ready(pcm);
215         pcm_close(pcm);
216     }
217 
218     return works;
219 }
220 
profile_enum_sample_rates(alsa_device_profile * profile,unsigned min,unsigned max)221 static unsigned profile_enum_sample_rates(alsa_device_profile* profile, unsigned min, unsigned max)
222 {
223     unsigned num_entries = 0;
224     unsigned index;
225 
226     for (index = 0; index < ARRAY_SIZE(std_sample_rates) &&
227                     num_entries < ARRAY_SIZE(profile->sample_rates) - 1;
228          index++) {
229         if (std_sample_rates[index] >= min && std_sample_rates[index] <= max
230                 && profile_test_sample_rate(profile, std_sample_rates[index])) {
231             profile->sample_rates[num_entries++] = std_sample_rates[index];
232         }
233     }
234     profile->sample_rates[num_entries] = 0; /* terminate */
235     return num_entries; /* return # of supported rates */
236 }
237 
profile_enum_sample_formats(alsa_device_profile * profile,struct pcm_mask * mask)238 static unsigned profile_enum_sample_formats(alsa_device_profile* profile, struct pcm_mask * mask)
239 {
240     const int num_slots = ARRAY_SIZE(mask->bits);
241     const int bits_per_slot = sizeof(mask->bits[0]) * 8;
242 
243     const int table_size = ARRAY_SIZE(pcm_format_value_map);
244 
245     int slot_index, bit_index, table_index;
246     table_index = 0;
247     int num_written = 0;
248     for (slot_index = 0; slot_index < num_slots && table_index < table_size;
249             slot_index++) {
250         unsigned bit_mask = 1;
251         for (bit_index = 0;
252                 bit_index < bits_per_slot && table_index < table_size;
253                 bit_index++) {
254             if ((mask->bits[slot_index] & bit_mask) != 0) {
255                 enum pcm_format format = pcm_format_value_map[table_index];
256                 /* Never return invalid (unrecognized) or 8-bit */
257                 if (format != PCM_FORMAT_INVALID && format != PCM_FORMAT_S8) {
258                     profile->formats[num_written++] = format;
259                     if (num_written == ARRAY_SIZE(profile->formats) - 1) {
260                         /* leave at least one PCM_FORMAT_INVALID at the end */
261                         goto end;
262                     }
263                 }
264             }
265             bit_mask <<= 1;
266             table_index++;
267         }
268     }
269 end:
270     profile->formats[num_written] = PCM_FORMAT_INVALID;
271     return num_written;
272 }
273 
profile_enum_channel_counts(alsa_device_profile * profile,unsigned min,unsigned max)274 static unsigned profile_enum_channel_counts(alsa_device_profile* profile, unsigned min,
275         unsigned max)
276 {
277     /* modify alsa_device_profile.h if you change the std_channel_counts[] array. */
278     static const unsigned std_channel_counts[] = {8, 7, 6, 5, 4, 3, 2, 1};
279 
280     unsigned num_counts = 0;
281     unsigned index;
282     /* TODO write a profile_test_channel_count() */
283     /* Ensure there is at least one invalid channel count to terminate the channel counts array */
284     for (index = 0; index < ARRAY_SIZE(std_channel_counts) &&
285                     num_counts < ARRAY_SIZE(profile->channel_counts) - 1;
286          index++) {
287         /* TODO Do we want a channel counts test? */
288         if (std_channel_counts[index] >= min && std_channel_counts[index] <= max /* &&
289             profile_test_channel_count(profile, channel_counts[index])*/) {
290             profile->channel_counts[num_counts++] = std_channel_counts[index];
291         }
292     }
293     // if we have no match with the standard counts, we use the largest (preferred) std count.
294     if (num_counts == 0) {
295         ALOGW("usb device does not match std channel counts, setting to %d",
296                 std_channel_counts[0]);
297         profile->channel_counts[num_counts++] = std_channel_counts[0];
298     }
299     profile->channel_counts[num_counts] = 0;
300     return num_counts; /* return # of supported counts */
301 }
302 
303 /*
304  * Reads and decodes configuration info from the specified ALSA card/device.
305  */
read_alsa_device_config(alsa_device_profile * profile,struct pcm_config * config)306 static int read_alsa_device_config(alsa_device_profile * profile, struct pcm_config * config)
307 {
308     ALOGV("usb:audio_hw - read_alsa_device_config(c:%d d:%d t:0x%X)",
309           profile->card, profile->device, profile->direction);
310 
311     if (profile->card < 0 || profile->device < 0) {
312         return -EINVAL;
313     }
314 
315     struct pcm_params * alsa_hw_params =
316         pcm_params_get(profile->card, profile->device, profile->direction);
317     if (alsa_hw_params == NULL) {
318         return -EINVAL;
319     }
320 
321     profile->min_period_size = pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_SIZE);
322     profile->max_period_size = pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_SIZE);
323 
324     profile->min_channel_count = pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS);
325     profile->max_channel_count = pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS);
326 
327     int ret = 0;
328 
329     /*
330      * This Logging will be useful when testing new USB devices.
331      */
332 #ifdef LOG_PCM_PARAMS
333     log_pcm_params(alsa_hw_params);
334 #endif
335 
336     config->channels = pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS);
337     // For output devices, let's make sure we choose at least stereo
338     // (assuming the device supports it).
339     if (profile->direction == PCM_OUT &&
340         config->channels < 2 && pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS) >= 2) {
341         config->channels = 2;
342     }
343     config->rate = pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE);
344     // Prefer 48K or 44.1K
345     if (config->rate < 48000 &&
346         pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE) >= 48000) {
347         config->rate = 48000;
348     } else if (config->rate < 441000 &&
349                pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE) >= 44100) {
350         config->rate = 44100;
351     }
352     config->period_size = profile_calc_min_period_size(profile, config->rate);
353     config->period_count = pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIODS);
354     config->format = get_pcm_format_for_mask(pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT));
355 #ifdef LOG_PCM_PARAMS
356     log_pcm_config(config, "read_alsa_device_config");
357 #endif
358     if (config->format == PCM_FORMAT_INVALID) {
359         ret = -EINVAL;
360     }
361 
362     pcm_params_free(alsa_hw_params);
363 
364     return ret;
365 }
366 
profile_read_device_info(alsa_device_profile * profile)367 bool profile_read_device_info(alsa_device_profile* profile)
368 {
369     if (!profile_is_initialized(profile)) {
370         return false;
371     }
372 
373     /* let's get some defaults */
374     read_alsa_device_config(profile, &profile->default_config);
375     ALOGV("default_config chans:%d rate:%d format:%d count:%d size:%d",
376           profile->default_config.channels, profile->default_config.rate,
377           profile->default_config.format, profile->default_config.period_count,
378           profile->default_config.period_size);
379 
380     struct pcm_params * alsa_hw_params = pcm_params_get(profile->card,
381                                                         profile->device,
382                                                         profile->direction);
383     if (alsa_hw_params == NULL) {
384         return false;
385     }
386 
387     /* Formats */
388     struct pcm_mask * format_mask = pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT);
389     profile_enum_sample_formats(profile, format_mask);
390 
391     /* Channels */
392     profile_enum_channel_counts(
393             profile, pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS),
394             pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS));
395 
396     /* Sample Rates */
397     profile_enum_sample_rates(
398             profile, pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE),
399             pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE));
400 
401     profile->is_valid = true;
402 
403     return true;
404 }
405 
profile_get_sample_rate_strs(alsa_device_profile * profile)406 char * profile_get_sample_rate_strs(alsa_device_profile* profile)
407 {
408     /* if we assume that rate strings are about 5 characters (48000 is 5), plus ~1 for a
409      * delimiter "|" this buffer has room for about 22 rate strings which seems like
410      * way too much, but it's a stack variable so only temporary.
411      */
412     char buffer[128];
413     buffer[0] = '\0';
414     size_t buffSize = ARRAY_SIZE(buffer);
415     size_t curStrLen = 0;
416 
417     char numBuffer[32];
418 
419     size_t numEntries = 0;
420     size_t index;
421     for (index = 0; profile->sample_rates[index] != 0; index++) {
422         snprintf(numBuffer, sizeof(numBuffer), "%u", profile->sample_rates[index]);
423         // account for both the null, and potentially the bar.
424         if (buffSize - curStrLen < strlen(numBuffer) + (numEntries != 0 ? 2 : 1)) {
425             /* we don't have room for another, so bail at this point rather than
426              * return a malformed rate string
427              */
428             break;
429         }
430         if (numEntries++ != 0) {
431             strlcat(buffer, "|", buffSize);
432         }
433         curStrLen = strlcat(buffer, numBuffer, buffSize);
434     }
435 
436     return strdup(buffer);
437 }
438 
profile_get_format_strs(alsa_device_profile * profile)439 char * profile_get_format_strs(alsa_device_profile* profile)
440 {
441     /* if we assume that format strings are about 24 characters (AUDIO_FORMAT_PCM_16_BIT is 23),
442      * plus ~1 for a delimiter "|" this buffer has room for about 10 format strings which seems
443      *  like way too much, but it's a stack variable so only temporary.
444      */
445     char buffer[256];
446     buffer[0] = '\0';
447     size_t buffSize = ARRAY_SIZE(buffer);
448     size_t curStrLen = 0;
449 
450     size_t numEntries = 0;
451     size_t index = 0;
452     for (index = 0; profile->formats[index] != PCM_FORMAT_INVALID; index++) {
453         // account for both the null, and potentially the bar.
454         if (buffSize - curStrLen < strlen(format_string_map[profile->formats[index]])
455                                    + (numEntries != 0 ? 2 : 1)) {
456             /* we don't have room for another, so bail at this point rather than
457              * return a malformed rate string
458              */
459             break;
460         }
461         if (numEntries++ != 0) {
462             strlcat(buffer, "|", buffSize);
463         }
464         curStrLen = strlcat(buffer, format_string_map[profile->formats[index]], buffSize);
465     }
466 
467     return strdup(buffer);
468 }
469 
profile_get_channel_count_strs(alsa_device_profile * profile)470 char * profile_get_channel_count_strs(alsa_device_profile* profile)
471 {
472     // FIXME implicit fixed channel count assumption here (FCC_8).
473     // we use only the canonical even number channel position masks.
474     static const char * const out_chans_strs[] = {
475         /* 0 */"AUDIO_CHANNEL_NONE", /* will never be taken as this is a terminator */
476         /* 1 */"AUDIO_CHANNEL_OUT_MONO",
477         /* 2 */"AUDIO_CHANNEL_OUT_STEREO",
478         /* 3 */ /* "AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_FRONT_CENTER" */ NULL,
479         /* 4 */"AUDIO_CHANNEL_OUT_QUAD",
480         /* 5 */ /* "AUDIO_CHANNEL_OUT_QUAD|AUDIO_CHANNEL_OUT_FRONT_CENTER" */ NULL,
481         /* 6 */"AUDIO_CHANNEL_OUT_5POINT1",
482         /* 7 */ /* "AUDIO_CHANNEL_OUT_5POINT1|AUDIO_CHANNEL_OUT_BACK_CENTER" */ NULL,
483         /* 8 */"AUDIO_CHANNEL_OUT_7POINT1",
484         /* channel counts greater than this not considered */
485     };
486 
487     static const char * const in_chans_strs[] = {
488         /* 0 */"AUDIO_CHANNEL_NONE", /* will never be taken as this is a terminator */
489         /* 1 */"AUDIO_CHANNEL_IN_MONO",
490         /* 2 */"AUDIO_CHANNEL_IN_STEREO",
491         /* channel counts greater than this not considered */
492     };
493 
494     static const char * const index_chans_strs[] = {
495         /* 0 */"AUDIO_CHANNEL_NONE", /* will never be taken as this is a terminator */
496         /* 1 */"AUDIO_CHANNEL_INDEX_MASK_1",
497         /* 2 */"AUDIO_CHANNEL_INDEX_MASK_2",
498         /* 3 */"AUDIO_CHANNEL_INDEX_MASK_3",
499         /* 4 */"AUDIO_CHANNEL_INDEX_MASK_4",
500         /* 5 */"AUDIO_CHANNEL_INDEX_MASK_5",
501         /* 6 */"AUDIO_CHANNEL_INDEX_MASK_6",
502         /* 7 */"AUDIO_CHANNEL_INDEX_MASK_7",
503         /* 8 */"AUDIO_CHANNEL_INDEX_MASK_8",
504     };
505 
506     const bool isOutProfile = profile->direction == PCM_OUT;
507 
508     const char * const * const chans_strs = isOutProfile ? out_chans_strs : in_chans_strs;
509     const size_t chans_strs_size =
510             isOutProfile ? ARRAY_SIZE(out_chans_strs) : ARRAY_SIZE(in_chans_strs);
511 
512     /*
513      * If we assume each channel string is 26 chars ("AUDIO_CHANNEL_INDEX_MASK_8" is 26) + 1 for,
514      * the "|" delimiter, then we allocate room for 16 strings.
515      */
516     char buffer[27 * 16 + 1]; /* caution, may need to be expanded */
517     buffer[0] = '\0';
518     size_t buffSize = ARRAY_SIZE(buffer);
519     size_t curStrLen = 0;
520 
521     /* We currently support MONO and STEREO, and always report STEREO but some (many)
522      * USB Audio Devices may only announce support for MONO (a headset mic for example), or
523      * The total number of output channels. SO, if the device itself doesn't explicitly
524      * support STEREO, append to the channel config strings we are generating.
525      *
526      * The MONO and STEREO positional channel masks are provided for legacy compatibility.
527      * For multichannel (n > 2) we only expose channel index masks.
528      */
529     // Always support stereo
530     curStrLen = strlcat(buffer, chans_strs[2], buffSize);
531 
532     size_t index;
533     unsigned channel_count;
534     for (index = 0;
535          (channel_count = profile->channel_counts[index]) != 0;
536          index++) {
537 
538         /* we only show positional information for mono (stereo handled already) */
539         if (channel_count < chans_strs_size
540                 && chans_strs[channel_count] != NULL
541                 && channel_count < 2 /* positional only for fewer than 2 channels */) {
542             // account for the '|' and the '\0'
543             if (buffSize - curStrLen < strlen(chans_strs[channel_count]) + 2) {
544                 /* we don't have room for another, so bail at this point rather than
545                  * return a malformed rate string
546                  */
547                 break;
548             }
549 
550             strlcat(buffer, "|", buffSize);
551             curStrLen = strlcat(buffer, chans_strs[channel_count], buffSize);
552         }
553 
554         // handle channel index masks for both input and output
555         // +2 to account for the '|' and the '\0'
556          if (buffSize - curStrLen < strlen(index_chans_strs[channel_count]) + 2) {
557              /* we don't have room for another, so bail at this point rather than
558               * return a malformed rate string
559               */
560              break;
561          }
562 
563          strlcat(buffer, "|", buffSize);
564          curStrLen = strlcat(buffer, index_chans_strs[channel_count], buffSize);
565     }
566 
567     return strdup(buffer);
568 }
569