С подачи 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 кГц, имеют полное право на жизнь:

Код:
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
Но на 24 битах ждет разочарование:
Код:
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
Теперь подключим карту, заведомо аппаратно поддерживающую 16/24 бит и 48/96 кГц:

Код:
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
И, как можно убедиться, список поддерживаемых форматов довольно длинный:
Код:
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
При этом стандартное приложение "Музыка" при воспроизведении 24.wav, вероятно, действительно пытается "на лету" преобразовать в другой формат, о чем свидетельствуют жуткий скрежет каждые несколько тактов. Иные же приложения, jetAUDIO к примеру, воспроизводят 24.wav, на первый взгляд, точно так же, как и команда alsa_aplay из консоли.

---------- Сообщение добавлено 19.01.2014 в 02:57 ----------

Также стоит внимательно рассмотреть функции и структуры данных библиотеки, отвечающие непосредственно за создание аудиопотока:

Код:
/* 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);
При этом предпочтительные параметры конфигурации могут быть назначены при вызове из audio.primary.exDroid.so, как это реализовано в нашем решении для a13 Андроид зеркало на Allwinner A13 в части частоты дискретизации, что дает возможность корректировать обращения к аудиосистеме со стороны стандартных приложений Android, если они содержат в себе излишние ограничения.

---------- Сообщение добавлено 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);
, так и прибегая к "умной" модели выбора
Код:
        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);
для последующей обработки и корректировки для совместимости и выбранным в данный момент оборудованием (встроенный SoC или внешний USB DAC), если потребуется, на уровне библиотеки libtinyalsa.so:

Код:
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(&params);
        param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT,
                       pcm_format_to_alsa(config->format));
        param_set_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
                       SNDRV_PCM_SUBFORMAT_STD);
        param_set_min(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
        param_set_int(&params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
                      pcm_format_to_bits(config->format));
        param_set_int(&params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
                      pcm_format_to_bits(config->format) * config->channels);
        param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS,
                      config->channels);
        param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
        param_set_int(&params, SNDRV_PCM_HW_PARAM_RATE, config->rate);
            
        param_dump(&params);
        
        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(&params, SNDRV_PCM_HW_PARAM_ACCESS,
                       SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
        else
            param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
                       SNDRV_PCM_ACCESS_RW_INTERLEAVED);
 
        if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) {
            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);



Полезные темы: