С подачи Rover'а, озадачились вопросами качества воспроизведения с Android-устройствах в роли источников звука. В частности, насколько жестко ограничение Google по 16 bit/44.1 kHz/ 2 channels, о котором утверждают разработчики. А также насколько критично данное ограничение для мобильных устройств, возможности процессоров которых также далеко не безграничны.
Пресловутое ограничение, если такое и имеется в последних версиях Андроида, в большей степени связано с отсутствием необходимости закладывать в музыкальные приложения протоколы воспроизведения аудиофайлов с параметрами, превосходящими мультимедийные возможности SoC. Тем не менее, так как ядро Linux разрабатывалось для более широкого спектра устройств, базовая часть аппаратной поддержки звуковых карт, поддерживаемых ALSA, обеспечивается по умолчанию уже на этом уровне, но эти возможности попросту не используются оболочкой.
Это можно продемонстрировать на следующем примере. Скачаем тестовые сэмплы в формате .wav для 16 и 24-битовых стандартов соответственно. Например, из следующей подборки: http://www.nordicaudiolabs.com/dart/ . Алгоритм "даунсэмплинга" в данный момент нам неинтересен, достаточно быть уверенными в параметрах тестируемого материала.
Возможности встроенного звукового адаптера, как и ожидалось, ограничены 16 битами. Чего не скажешь про частоту дискретизации, все-таки целых 48 кГц, а не 44,1 кГц, имеют полное право на жизнь:
Но на 24 битах ждет разочарование:Код:1|root@android:/mnt/extsd/test-samles # alsa_aplay -v -Dhw:0,0 16-hpt.wav Playing WAVE '16-hpt.wav' : Signed 16 bit Little Endian, Rate 48000 Hz, Stereo Hardware PCM card 0 'audiocodec' device 0 subdevice 0 Its setup is: stream : PLAYBACK access : RW_INTERLEAVED format : S16_LE subformat : STD channels : 2 rate : 48000 exact rate : 48000 (48000/1) msbits : 16 buffer_size : 8192 period_size : 2048 period_time : 42666 tstamp_mode : NONE period_step : 1 avail_min : 2048 period_event : 0 start_threshold : 8192 stop_threshold : 8192 silence_threshold: 0 silence_size : 0 boundary : 1073741824 appl_ptr : 0 hw_ptr : 0
Теперь подключим карту, заведомо аппаратно поддерживающую 16/24 бит и 48/96 кГц:Код:1|root@android:/mnt/extsd/test-samles # alsa_aplay -vv -Dhw:0,0 24.wav Playing WAVE '24.wav' : Signed 24 bit Little Endian in 3bytes, Rate 48000 Hz, Stereo aplay: set_params:1054: Sample format non available Available formats: - S16_LE
Код:root@android:/mnt/extsd/test-samles # alsa_aplay -v -Dhw:1,0 16-hpt.wav Playing WAVE '16-hpt.wav' : Signed 16 bit Little Endian, Rate 48000 Hz, Stereo Hardware PCM card 1 'SB Audigy 2 NX' device 0 subdevice 0 Its setup is: stream : PLAYBACK access : RW_INTERLEAVED format : S16_LE subformat : STD channels : 2 rate : 48000 exact rate : 48000 (48000/1) msbits : 16 buffer_size : 24000 period_size : 6000 period_time : 125000 tstamp_mode : NONE period_step : 1 avail_min : 6000 period_event : 0 start_threshold : 24000 stop_threshold : 24000 silence_threshold: 0 silence_size : 0 boundary : 1572864000 appl_ptr : 0 hw_ptr : 0И, как можно убедиться, список поддерживаемых форматов довольно длинный:Код:1|root@android:/mnt/extsd/test-samles # alsa_aplay -v -Dhw:1,0 24.wav Playing WAVE '24.wav' : Signed 24 bit Little Endian in 3bytes, Rate 48000 Hz, Stereo Hardware PCM card 1 'SB Audigy 2 NX' device 0 subdevice 0 Its setup is: stream : PLAYBACK access : RW_INTERLEAVED format : S24_3LE subformat : STD channels : 2 rate : 48000 exact rate : 48000 (48000/1) msbits : 24 buffer_size : 24000 period_size : 6000 period_time : 125000 tstamp_mode : NONE period_step : 1 avail_min : 6000 period_event : 0 start_threshold : 24000 stop_threshold : 24000 silence_threshold: 0 silence_size : 0 boundary : 1572864000 appl_ptr : 0 hw_ptr : 0
При этом стандартное приложение "Музыка" при воспроизведении 24.wav, вероятно, действительно пытается "на лету" преобразовать в другой формат, о чем свидетельствуют жуткий скрежет каждые несколько тактов. Иные же приложения, jetAUDIO к примеру, воспроизводят 24.wav, на первый взгляд, точно так же, как и команда alsa_aplay из консоли.Код:root@android:/ # cat /proc/asound/card1/stream0 Creative Technology Ltd SB Audigy 2 NX at usb-sw_hcd_host0-1, high speed : USB Audio Playback: Status: Stop Interface 1 Altset 3 Format: S16_LE Channels: 2 Endpoint: 1 OUT (ASYNC) Rates: 48000 Data packet interval: 1000 us Interface 1 Altset 4 Format: S24_3LE Channels: 2 Endpoint: 1 OUT (ASYNC) Rates: 48000 Data packet interval: 1000 us Interface 1 Altset 5 Format: S16_LE Channels: 2 Endpoint: 1 OUT (ASYNC) Rates: 96000 Data packet interval: 1000 us Interface 1 Altset 6 Format: S24_3LE Channels: 2 Endpoint: 1 OUT (ASYNC) Rates: 96000 Data packet interval: 1000 us Interface 1 Altset 7 Format: S16_LE Channels: 4 Endpoint: 1 OUT (ASYNC) Rates: 48000 Data packet interval: 1000 us Interface 1 Altset 8 Format: S24_3LE Channels: 4 Endpoint: 1 OUT (ASYNC) Rates: 48000 Data packet interval: 1000 us Interface 1 Altset 9 Format: S16_LE Channels: 4 Endpoint: 1 OUT (ASYNC) Rates: 96000 Data packet interval: 1000 us Interface 1 Altset 10 Format: S24_3LE Channels: 4 Endpoint: 1 OUT (ASYNC) Rates: 96000 Data packet interval: 125 us Interface 1 Altset 11 Format: S16_LE Channels: 6 Endpoint: 1 OUT (ASYNC) Rates: 48000 Data packet interval: 1000 us Interface 1 Altset 12 Format: S24_3LE Channels: 6 Endpoint: 1 OUT (ASYNC) Rates: 48000 Data packet interval: 1000 us Interface 1 Altset 13 Format: S16_LE Channels: 6 Endpoint: 1 OUT (ASYNC) Rates: 96000 Data packet interval: 125 us Interface 1 Altset 14 Format: S24_3LE Channels: 6 Endpoint: 1 OUT (ASYNC) Rates: 96000 Data packet interval: 125 us Interface 1 Altset 15 Format: S16_LE Channels: 8 Endpoint: 1 OUT (ASYNC) Rates: 48000 Data packet interval: 1000 us Interface 1 Altset 16 Format: S24_3LE Channels: 8 Endpoint: 1 OUT (ASYNC) Rates: 48000 Data packet interval: 125 us Interface 1 Altset 17 Format: S16_LE Channels: 8 Endpoint: 1 OUT (ASYNC) Rates: 96000 Data packet interval: 125 us Interface 1 Altset 18 Format: S24_3LE Channels: 8 Endpoint: 1 OUT (ASYNC) Rates: 96000 Data packet interval: 125 us Capture: Status: Stop Interface 2 Altset 5 Format: S16_LE Channels: 2 Endpoint: 2 IN (NONE) Rates: 48000 Data packet interval: 1000 us Interface 2 Altset 6 Format: S24_3LE Channels: 2 Endpoint: 2 IN (NONE) Rates: 48000 Data packet interval: 1000 us Interface 2 Altset 7 Format: S16_LE Channels: 2 Endpoint: 2 IN (NONE) Rates: 96000 Data packet interval: 1000 us Interface 2 Altset 8 Format: S24_3LE Channels: 2 Endpoint: 2 IN (NONE) Rates: 96000 Data packet interval: 1000 us
---------- Сообщение добавлено 19.01.2014 в 02:57 ----------
Также стоит внимательно рассмотреть функции и структуры данных библиотеки, отвечающие непосредственно за создание аудиопотока:
При этом предпочтительные параметры конфигурации могут быть назначены при вызове из audio.primary.exDroid.so, как это реализовано в нашем решении для a13 Андроид зеркало на Allwinner A13 в части частоты дискретизации, что дает возможность корректировать обращения к аудиосистеме со стороны стандартных приложений Android, если они содержат в себе излишние ограничения.Код:/* Configuration for a stream */ struct pcm_config { unsigned int channels_min; unsigned int channels_max; unsigned int rate_max; unsigned int rate_min; unsigned int channels; unsigned int rate; unsigned int period_size; unsigned int period_count; enum pcm_format format; /* Values to use for the ALSA start, stop and silence thresholds. Setting * any one of these values to 0 will cause the default tinyalsa values to be * used instead. Tinyalsa defaults are as follows. * * start_threshold : period_count * period_size * stop_threshold : period_count * period_size * silence_threshold : 0 */ unsigned int start_threshold; unsigned int stop_threshold; unsigned int silence_threshold; int avail_min; unsigned int in_init_channels;//keep the record init channels }; struct pcm *pcm_open_req(unsigned int card, unsigned int device, unsigned int flags, struct pcm_config *config, int requested_rate);
---------- Сообщение добавлено 19.01.2014 в 15:02 ----------
Предопределив желаемую частоту дискретизации для библиотеки audio.primary.exDroid.so через параметры конфигурации системы audio.usb.out.freq, и перенаправив все вызовы на собственную функцию pcm_open_req вместо pmp_open, мы получили возможность передавать эту настройку через переменную requested_rate
как задавая эти ограничения жесткоstruct pcm *pcm_open_req(unsigned int card, unsigned int device,
unsigned int flags, struct pcm_config *config, int requested_rate);
, так и прибегая к "умной" модели выбораКод:/* android default out sampling rate*/ #define DEFAULT_OUT_SAMPLING_RATE SAMPLING_RATE_44K <...> LOGD("start_output_stream: card:%d, port:%d, rate:%d", card, port, out->config.rate); out->pcm = pcm_open_req(card, port, PCM_OUT | PCM_MMAP | PCM_NOIRQ, &out->config, DEFAULT_OUT_SAMPLING_RATE);
для последующей обработки и корректировки для совместимости и выбранным в данный момент оборудованием (встроенный SoC или внешний USB DAC), если потребуется, на уровне библиотеки libtinyalsa.so:Код:int in_ajust_rate = in->requested_rate; // out/in stream should be both 44.1K serial if (!(in->requested_rate % SAMPLING_RATE_11K)) { // OK in_ajust_rate = in->requested_rate; } else { in_ajust_rate = SAMPLING_RATE_11K * in->requested_rate / SAMPLING_RATE_8K; if (in_ajust_rate > SAMPLING_RATE_44K) { in_ajust_rate = SAMPLING_RATE_44K; } LOGV("out/in stream should be both 44.1K serial, force capture rate: %d", in_ajust_rate); } in->pcm = pcm_open_req(0, PORT_CODEC, PCM_IN, &in->config, in_ajust_rate);
Код:for (index = 0; index < size_rate; index++) { if (pcm_rate[index] == requested_rate) { break; } } if (index == size_rate) { if (requested_rate < pcm_rate[0]) { config->rate = pcm_rate[0]; } else { config->rate = pcm_rate[index - 1]; } } for (cnt = 0; cnt < size_rate; cnt++) { config->rate = pcm_rate[(index + cnt) % size_rate]; LOGV("pcm_open_req channels: %d, try rate: %d", config->channels, config->rate); param_init(¶ms); param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT, pcm_format_to_alsa(config->format)); param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT, SNDRV_PCM_SUBFORMAT_STD); param_set_min(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size); param_set_int(¶ms, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, pcm_format_to_bits(config->format)); param_set_int(¶ms, SNDRV_PCM_HW_PARAM_FRAME_BITS, pcm_format_to_bits(config->format) * config->channels); param_set_int(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS, config->channels); param_set_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count); param_set_int(¶ms, SNDRV_PCM_HW_PARAM_RATE, config->rate); param_dump(¶ms); if (flags & PCM_NOIRQ) { if (!(flags & PCM_MMAP)) { oops(pcm, -EINVAL, "noirq only currently supported with mmap()."); goto fail; } params.flags |= SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP; pcm->noirq_frames_per_msec = config->rate / 1000; } if (flags & PCM_MMAP) param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS, SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); else param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS, SNDRV_PCM_ACCESS_RW_INTERLEAVED); if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) { LOGV("cannot set hw params"); } else { LOGV("pcm_open_req OK config->rate: %d", config->rate); break; } } if (cnt == size_rate) { cnt = 0; config->channels = 3 - config->channels; pcm->config.channels = config->channels; for (index = 0; index < size_rate; index++) { if (pcm_rate[index] == requested_rate) { break; } } if (index == size_rate) { if (requested_rate < pcm_rate[0]) { config->rate = pcm_rate[0]; } else { config->rate = pcm_rate[index - 1]; } } for (cnt = 0; cnt < size_rate; cnt++) { config->rate = pcm_rate[(index + cnt) % size_rate]; LOGV("pcm_open_req channels: %d, try rate: %d", config->channels, config->rate);
Полезные темы:







Социальные закладки