С подачи 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 http://magnitola.org/usb-audio-dac-a...ml#post2317264 в части частоты дискретизации, что дает возможность корректировать обращения к аудиосистеме со стороны стандартных приложений 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);

