| # Alsa Conformance Test |
| |
| <!--* freshness: { owner: 'yuhsuan' reviewed: '2019-08-19' } *--> |
| |
| ALSA Conformance Test is a tool to verify the correctness and performance of |
| audio drivers. It can be used to verify the quality of audio bringup and prevent |
| regression. The tool has found many audio [issues](#bugs-we-found) on different |
| devices. |
| |
| [TOC] |
| |
| ## Prerequisites |
| Make sure these packages are on the host to compile the tool. |
| ```Shell |
| sudo apt-get update |
| sudo apt-get upgrade |
| sudo apt-get install git pkg-config build-essential libasound2-dev |
| ``` |
| |
| Make sure these packages are on the device under test(DUT) for testing. |
| ```Shell |
| sudo apt-get update |
| sudo apt-get upgrade |
| sudo apt-get install alsa-utils libasound2-dev |
| ``` |
| **NOTE:** ChromeOS has already had these packages. |
| |
| ## Build |
| The tool is a built-in binary in the ChromeOS test image. There are two ways to |
| build ALSA conformance test manually. |
| |
| ### Build in the ChromeOS chroot |
| ``` |
| (inside the cros chroot) emerge-${BOARD} audiotest |
| ``` |
| The binary will be in `/build/${BOARD}/usr/bin/alsa_conformance_test`. |
| |
| The command `cros deploy ${DUT_IP} audiotest` can deploy this tool to a device. |
| The binary will be in /usr/bin/. |
| |
| **NOTE:** The built-in binary is in `/usr/local/bin` where the developer root |
| is mounted at. The location is different from building by yourself. Make sure |
| that you use the correct binary before testing. |
| |
| ### Build in the git package |
| ``` |
| git clone https://chromium.googlesource.com/chromiumos/platform/audiotest |
| cd audiotest |
| make alsa_conformance_test |
| ``` |
| The default binary is in `./alsa_conformance_test/alsa_conformance_test`. |
| You can set the variable OUT to change the build directory. |
| For example `make alsa_conformance_test OUT=/path/to/builddir` changes the |
| output to `/path/to/builddir/alsa_conformance_test/alsa_conformance_test`. |
| |
| ## Run |
| ``` |
| alsa_conformance_test [OPTIONS] |
| ``` |
| Notice that some devices may not work before the correct UCM (Use Case Manager) |
| is set. In ChromeOS, the UCM should be set automatically when selecting audio |
| nodes from UI. Users can also find the UCM file for each supported sound card |
| in `/usr/share/alsa/ucm/` and set it manually by [alsaucm](https://www.systutorials.com/docs/linux/man/1-alsaucm/). |
| |
| **Example:** To enable the headphone specified on `kbl_r5514_5663_max`. |
| ``` |
| alsaucm -c kbl_r5514_5663_max set _verb HiFi set _enadev "Headphone" |
| ``` |
| |
| ### Options |
| + -h, --help |
| + Print this help and exit. |
| + -P, --playback_dev <device> |
| + PCM device for playback. (default: NULL) |
| + -C, --capture_dev <device> |
| + PCM device for capture. (default: NULL) |
| + -c, --channels <channels> |
| + Set channels. (default: 2) |
| + -f, --format <format> |
| + Set format. (default: S16_LE) |
| + -r, --rate <rate> |
| + Set rate. (default: 48000) |
| + -p, --period <period> |
| + Set period. If not set, the default value set in the driver will be used. |
| + -d, --durations <duration> |
| + Set durations(second). (default: 1.0) |
| + -B, --block_size <block_size> |
| + Set block size in frames of each read(for input devices) and |
| write(for output devices). (default: 240) |
| + For more details, please refer to the section about [Underrun and Overrun](#underrun-and-overrun). |
| + --debug |
| + Enable debug mode. (Not support multi-streams in this version) |
| + --strict |
| + Enable strict mode. It will set params to the fixed value. |
| + ALSA conformance test sets the rate and the period size nearest to the |
| target. The final value may not be the same as a user set in the params. |
| With this flag, the test will fail if these values are unexpected. |
| + --dev_info_only |
| + Show device information only without setting params and running I/O. |
| + --iterations |
| + Number of times to run the tests specified. (default: 1) |
| + --device_file: |
| + Device file path. It will load devices from the file. File format: |
| ``` |
| [name] [type] [channels] [format] [rate] [period] [block_size] [durations] # comment |
| [type] could be either `PLAYBACK` or `CAPTURE` # comment |
| eg: hw:0,0 PLAYBACK 2 S16_LE 48000 240 240 10 # Example |
| ``` |
| + --merge_threshold |
| + Set merge_threshold_t. (default: 0.0001) |
| + If the value is not zero, there will be a dryrun to set the |
| merge_threshold_sz as the median of frame diff. Points with |
| TIME_DIFF less than merge_threshold_t and SAMPLES_DIFF less |
| than merge_threshold_sz will be merged. |
| |
| ## Results |
| These are the functions that ALSA conformance test covers. |
| |
| ### Correctness of params |
| Sometimes audio device claims that it can support some params but it actually |
| does not. In this case, snd_pcm_hw_params fails. Therefore, verifying all |
| params can be set correctly is necessary. |
| ``` |
| ---------PRINT PARAMS--------- |
| PCM name: hw:0,0 |
| card: acpd7219m98357 [acpd7219m98357] |
| device: Playback da7219-hifi-0 [] |
| stream: PLAYBACK |
| access type: MMAP_INTERLEAVED |
| format: S16_LE |
| channels: 2 |
| rate: 48000 fps |
| period time: 5000 us |
| period size: 240 frames |
| buffer time: 160000 us |
| buffer size: 7680 frames |
| ``` |
| |
| ### Correctness of card name |
| A specific UCM configuration file is sometimes required for a USB audio device. |
| If the card name is the default USB card name, like 'USB Audio Device' |
| it will not be possible to add a UCM configuration file because UCM files are matched by card name. |
| ``` |
| Device Information |
| Name: hw:3,0 |
| Card: Device [USB Audio Device] |
| Device: USB Audio [USB Audio] |
| Stream: PLAYBACK |
| Format: ['S16_LE'] |
| Channels: [2] |
| Rate: [44100, 48000] |
| Period_size range: [45, 48000] |
| Buffer_size range: [90, 96000] |
| Mixer: |
| Mixer name: PCM |
| Mixer index: 0 |
| Mixer has_volume: 1 |
| Mixer db_range: [-9999999, 0] |
| Mixer volume_range: [0, 37] |
| ``` |
| |
| ### Correctness of usb audio mixer control |
| |
| If the device is a USB audio device, it is very important that the USB audio mixer control exposes reasonable values. Otherwise, applications may rely on incorrect values and lead to undefined behavior. |
| |
| #### Mixer name |
| The following are the requirements for the USB audio mixer control name for playback: |
| |
| * Headphone |
| * Headset |
| * Headset Earphone |
| * Speaker |
| * PCM |
| * Master |
| * Digital |
| * Speaker Volume |
| |
| The following are the requirements for the USB audio mixer control name for capture: |
| |
| * Capture |
| * Digital Capture |
| * Mic |
| * Microphone |
| * Headset |
| * Mic Volume |
| |
| #### Number of mixers |
| Device can only have one playback/capture mixer control system can recognize. Otherwise system will fail to know which control should be select in the system. |
| |
| #### Mixer volume control ability |
| If the mixer control does not have the ability to control volume, there is likely something wrong with it. The main function of a mixer control for a USB audio device is to control volume. |
| |
| #### Mixer volume db range |
| The volume db range must be within a reasonable range. If the volume db range is not reasonable, the system may use it as a basis for its actions, which could cause incorrect behavior. |
| |
| * The playback pass criteria are: 500 <= db_range_max - db_range_min <= 20000. |
| |
| #### Mixer playback volume range |
| The system will use the playback volume range to calculate the percentage of volume that should be increased with one volume up/down event. If the value is less than 10, it will cause incorrect behavior. The minimum value for the volume range is 10. |
| |
| #### Example |
| This headset is `Cyber Acoustics`, and the device info is |
| |
| ``` |
| Device Information |
| Name: hw:3,0 |
| Card: Device [USB Audio Device] |
| Device: USB Audio [USB Audio] |
| Stream: PLAYBACK |
| Format: ['S16_LE'] |
| Channels: [2] |
| Rate: [44100, 48000] |
| Period_size range: [45, 48000] |
| Buffer_size range: [90, 96000] |
| Mixer: |
| Mixer name: PCM |
| Mixer index: 0 |
| Mixer has_volume: 1 |
| Mixer db_range: [-9999999, 0] |
| Mixer volume_range: [0, 37] |
| ``` |
| |
| * number of mixer is 1 - pass |
| * The mixer name "PCM" is a valid name. - pass |
| * This mixer has volume control, which is indicated by the has_volume value of 1. - pass |
| * The mixer's db_range is 9999999, which is outside the acceptable range of 500-20000. - fail |
| * Mixer volume_range is 37, is >= 10 - pass |
| |
| ### Stability of rate |
| The number of samples consumed / time must be the same as the sampling rate. |
| For short-term, it can be checked every time when writing a block into the |
| device by calculating samples_diff / time_diff. |
| ``` |
| TIME_DIFF(s) HW_LEVEL READ RATE |
| 0.000780757 32 0 40985.863719 |
| 0.000791478 72 0 50538.359879 |
| 0.000834111 112 0 47955.248162 |
| 0.000838265 152 0 47717.607201 |
| ``` |
| |
| For long-term, the tool can record all data when hw_level changes. And then, |
| it will use linear regression to find out the measured rate. If measured rate |
| is not close to the sampling rate, or standard error from linear regression is |
| too large, the device doesn’t have enough stability of rate. |
| |
| ``` |
| rate: 48001.729135 |
| rate error: 7.050352 |
| ``` |
| |
| The [script](#script) only uses the long-term rate to determine pass or fail |
| because an unstable short-term rate may not affect the whole stability. |
| |
| |
| **Explaination of fields** |
| |
| + HW_LEVEL - Hardware level. The number of frames in a device buffer. |
| + READ - The cumulative number of frames has been read since the beginning. |
| |
| **Explaination of point merge** |
| ``` |
| TIME_DIFF(s) HW_LEVEL PLAYED DIFF RATE |
| 0.001000772 376 1064 48 47962.972585 |
| 0.001000089 328 1112 48 47995.728380 |
| 0.001000082 280 1160 48 47996.064323 |
| 0.001016092 232 1208 48 47239.816867 |
| 0.000983772 424 1256 48 48791.793220 |
| 0.000998709 400 1280 24 24031.024052 |
| 0.000082259 376 1304 24 291761.387812 [Merged] |
| ``` |
| |
| In the last two lines above, the device consumes 24 samples twice in a short time |
| instead of 48 samples in the fixed period and result in higher rate error. |
| To reduce the error, points with TIME_DIFF less than merge_threshold_t and SAMPLES_DIFF less than merge_threshold_sz will be merged. The merge_threshold_sz is determined automatically by the test and merge_threshold_t can be set by --merge_threshold option. |
| With the --merge_threshold set to 0.0001, the last two points will be merged, and only |
| the latter point is counted in linear regression. The result of point merge is the same as below: |
| ``` |
| TIME_DIFF(s) HW_LEVEL PLAYED DIFF RATE |
| 0.001000772 376 1064 48 47962.972585 |
| 0.001000089 328 1112 48 47995.728380 |
| 0.001000082 280 1160 48 47996.064323 |
| 0.001016092 232 1208 48 47239.816867 |
| 0.000983772 424 1256 48 48791.793220 |
| 0.001080968 376 1304 48 44404.6447258 |
| ``` |
| |
| |
| ### Stability of step |
| If a device consumes too large a block at once, we cannot maintain the buffer |
| level at low. This affects continuous latency. To measure this, the tool can |
| monitor hw_ptr and see whether the largest step during monitoring is small |
| enough. |
| |
| ``` |
| step min: 8 |
| step max: 288 |
| ``` |
| |
| ### Runtime of each ALSA API |
| The runtime of APIs is also important. If it needs a long time to open PCM |
| device, it might affect stream efficiency or cause long latency. |
| ``` |
| ---------TIMER RESULT--------- |
| Total_time(s) Counts Averages(s) |
| snd_pcm_open 0.003351390 1 0.003351 |
| snd_pcm_hw_params 0.039304997 1 0.039305 |
| snd_pcm_hw_params_any 0.000055113 1 0.000055 |
| snd_pcm_sw_params 0.000017586 1 0.000018 |
| snd_pcm_prepare 0.000007613 1 0.000008 |
| snd_pcm_start 0.000774880 1 0.000775 |
| snd_pcm_avail 0.807074662 246177 0.000003 |
| precision: 0.000000001 |
| ``` |
| |
| ### Correctness of recorded samples |
| Without any help from users, we can verify the correctness of recorded samples |
| by checking whether all of them are zeros. The tool shows whether each channel |
| is a zero channel, which means all samples in this channel are zeros. Usually |
| the recorded samples should not be all zeros. |
| ``` |
| zero channels: 0 1 // Channel 0 is not a zero channel but channel 1 is. |
| ``` |
| |
| ### Underrun and Overrun |
| The basic request of the audio device is not causing any underrun or overrun. |
| + Underrun happens when there are no frames to playback on a running device. |
| It makes the device play obsolete samples in the buffer, which sounds like |
| glitches. |
| |
| + Overrun happens when frames in a device buffer are more than buffer size. |
| It makes the device have a long delay and the latest recorded samples will |
| overlay the old ones. |
| |
| The block size is the size ALSA conformance test puts data into or gets data |
| from a device buffer. For playback, it puts data into the device once the |
| remaining frames in the device buffer are smaller than the block size. For |
| capture, it gets data from a device once the remaining frames in the device |
| buffer are larger than the block size. Users can adjust the block size to |
| detect whether this size might cause underrun or overrun. |
| |
| ``` |
| number of underrun: 0 |
| number of overrun: 0 |
| ``` |
| |
| ### Example |
| These are some examples to show how to use ALSA conformance test. |
| |
| + Get the PCM name |
| Before testing, to get the PCM name of the device is necessary. The name format |
| is `hw:{CARD},{DEVICE}`. Users can get it by the command `aplay -l` and |
| `arecord -l`. |
| ```Shell |
| > aplay -l |
| **** List of PLAYBACK Hardware Devices **** |
| card 0: sofglkda7219max [sof-glkda7219max], device 0: Speakers (*) [] |
| Subdevices: 1/1 |
| Subdevice #0: subdevice #0 |
| card 0: sofglkda7219max [sof-glkda7219max], device 1: Headset (*) [] |
| Subdevices: 1/1 |
| Subdevice #0: subdevice #0 |
| card 0: sofglkda7219max [sof-glkda7219max], device 5: HDMI1 (*) [] |
| Subdevices: 1/1 |
| Subdevice #0: subdevice #0 |
| card 0: sofglkda7219max [sof-glkda7219max], device 6: HDMI2 (*) [] |
| Subdevices: 1/1 |
| Subdevice #0: subdevice #0 |
| card 0: sofglkda7219max [sof-glkda7219max], device 7: HDMI3 (*) [] |
| Subdevices: 1/1 |
| Subdevice #0: subdevice #0 |
| ``` |
| From this result, the PCM name for internal speaker is `hw:0,0` and for |
| headset is `hw:0,1`. We will use `hw:0,0` as a playback and `hw:0,1` as capture |
| device for the below examples. |
| |
| + Show the range of parameters of PCM device |
| ``` |
| alsa_conformance_test -P hw:0,0 --dev_info_only |
| ``` |
| + Run playback and get the result |
| ``` |
| alsa_conformance_test -P hw:0,0 |
| ``` |
| + Run capture and get the result |
| ``` |
| alsa_conformance_test -C hw:0,1 |
| ``` |
| + Try to change the rate and format |
| ``` |
| alsa_conformance_test -P hw:0,0 -r 44100 -f S32_LE |
| ``` |
| + Try to show detail of playback |
| ``` |
| alsa_conformance_test -P hw:0,0 -d 0.1 --debug |
| ``` |
| + Playback and capture at the same time |
| ``` |
| alsa_conformance_test -P hw:0,0 -C hw:0,1 |
| ``` |
| + Test playback for more times |
| ``` |
| alsa_conformance_test -P hw:0,0 --iterations 10 |
| ``` |
| + Playback and capture at the same time with different params |
| ```Shell |
| > cat ./device |
| hw:0,0 PLAYBACK 2 S16_LE 48000 240 240 1 |
| hw:0,1 CAPTURE 2 S32_LE 44100 240 240 2 |
| > alsa_conformance_test --device_file ./device |
| ``` |
| |
| ## Script |
| For more convenience, this is a script that can test the basic funtions of |
| ALSA PCM device automatically. |
| ``` |
| alsa_conformance_test.py [-h] [-C INPUT_DEVICE] [-P OUTPUT_DEVICE] |
| [--rate-criteria-diff-pct RATE_CRITERIA_DIFF_PCT] |
| [--rate-err-criteria RATE_ERR_CRITERIA] |
| [--json] [--log-file LOG_FILE] |
| [--test-suites [...]] |
| ``` |
| |
| ### Options |
| + -h, --help |
| + show this help message and exit |
| + -C INPUT_DEVICE, --input_device INPUT_DEVICE |
| + Alsa input device, such as hw:0,0 |
| + -P OUTPUT_DEVICE, --output_device OUTPUT_DEVICE |
| + Alsa output device, such as hw:0,0 |
| + --rate_criteria RATE_CRITERIA |
| + The pass criteria of rate. The value is a percentage of rate. For example, |
| 0.01 means the pass range is [47995.2, 48004.8] for rate 48000. |
| (default: 0.01) |
| + --rate_err_criteria RATE_ERR_CRITERIA |
| + The pass criteria of rate error. (default: 20) |
| + --json |
| + Print result in JSON format |
| + --log-file LOG_FILE |
| + The file to save logs. |
| + --test-suites |
| + Customize which test suites should be run. If not set, all suites will |
| be run. See next section for more information. |
| |
| ### Test Suites |
| + test_card_name |
| + Check whether card name is in the block list. |
| + test_params |
| + Check whether all parameters can be set correctly. |
| + test_rates |
| + Check whether all estimated rates are the same as what it set. |
| + test_all_pairs |
| + Check whether the audio is still stable when mixing different params. |
| The test will check if rates meet our expectation when testing all |
| combinations of channels, sample rates and formats. |
| + test_usb_mixer |
| + Check whether the usb device exposes the required alsa mixer controls. |
| If the device is not a usb audio device, this test will be skiped. |
| |
| ### Results |
| The result will show pass or fail. |
| |
| ### Example |
| |
| #### Internal device example |
| |
| ``` |
| > alsa_conformance_test.py -P hw:0,0 |
| 11 passed, 0 failed |
| Device Information |
| Name: hw:0,0 |
| Card: sof-glkda7219max |
| Stream: PLAYBACK |
| Format: ['S16_LE', 'S32_LE'] |
| Channels: [2] |
| Rate: [44100, 48000, 96000, 192000] |
| Period_size range: [16, 8192] |
| Buffer_size range: [32, 16384] |
| Test card name |
| Test card name is not in the block list: pass |
| Test Params |
| Set channels 2: pass |
| Set format S16_LE: pass |
| Set format S32_LE: pass |
| Set rate 44100: pass |
| Set rate 48000: pass |
| Set rate 96000: pass |
| Set rate 192000: pass |
| Test Rates |
| Set rate 44100: pass |
| Set rate 48000: pass |
| Set rate 96000: pass |
| Set rate 192000: pass |
| Test All Pairs |
| Set channels 2, format S16_LE, rate 44100: pass |
| Set channels 2, format S16_LE, rate 48000: pass |
| Set channels 2, format S16_LE, rate 96000: pass |
| Set channels 2, format S16_LE, rate 192000: pass |
| Set channels 2, format S32_LE, rate 44100: pass |
| Set channels 2, format S32_LE, rate 48000: pass |
| Set channels 2, format S32_LE, rate 96000: pass |
| Set channels 2, format S32_LE, rate 192000: pass |
| Test USB mixer - skip |
| ``` |
| |
| #### USB device example |
| |
| ``` |
| > alsa_conformance_test.py -P hw:1,0 |
| 20 passed, 0 failed |
| Device Information |
| Name: hw:1,0 |
| Card: Wired [Zone Wired] |
| Device: USB Audio [USB Audio] |
| Stream: PLAYBACK |
| Format: ['S16_LE', 'S24_3LE'] |
| Channels: [2] |
| Rate: [44100, 48000, 96000] |
| Period_size range: [45, 131072] |
| Buffer_size range: [90, 262144] |
| Mixer: |
| Mixer name: PCM |
| Mixer index: 0 |
| Mixer has_volume: 1 |
| Mixer db_range: [-6562, 0] |
| Mixer volume_range: [0, 175] |
| Test card name |
| Test card name is not in the block list: pass |
| Test Params |
| Set channels 2: pass |
| Set format S16_LE: pass |
| Set format S24_3LE: pass |
| Set rate 44100: pass |
| Set rate 48000: pass |
| Set rate 96000: pass |
| Test Rates |
| Set rate 44100: pass |
| Set rate 48000: pass |
| Set rate 96000: pass |
| Test All Pairs |
| Set channels 2, format S16_LE, rate 44100: pass |
| Set channels 2, format S16_LE, rate 48000: pass |
| Set channels 2, format S16_LE, rate 96000: pass |
| Set channels 2, format S24_3LE, rate 44100: pass |
| Set channels 2, format S24_3LE, rate 48000: pass |
| Set channels 2, format S24_3LE, rate 96000: pass |
| Test USB mixer |
| Test usb mixer number correctness: pass |
| Test usb mixer has_volume correctness: pass |
| Test usb mixer playback db range correctness: pass |
| Test usb mixer playback volume range correctness: pass |
| |
| ``` |
| ## Bugs we found |
| + Unsupported channel number on Octopus. ([Issue](http://b/122867610), [Fix](http://crrev.com/c/1440921)) |
| + Incorrect sample rate and params on Octopus. ([Issue](http://b/119390555), [Fix](http://crrev.com/c/1391434)) |
| + Incorrect sample rate on Veyron HDMI. ([Issue](http://b/122017492), [Fix](http://crrev.com/c/1394465)) |
| + Incorrect sample rate after suspending on Grunt. ([Issue](http://b/117585955), [Fix](http://crrev.com/i/718908)) |
| + Incorrect sample rate with period size 256 on strago. ([Issue](http://b/127192730), [Fix](http://crrev.com/c/1505076)) |
| + Incorrect params supported on Veyron. ([Issue](http://b/123053423), [Fix](http://crrev.com/c/1680381)) |
| + Incorrect params supported on Peach. ([Issue](http://b/128960223), [Fix](http://crrev.com/c/1680381)) |
| + Incorrect params supported on Coral. ([Issue](http://b/128955894), [Fix](http://crrev.com/c/1617381)) |
| + Incorrect sample rate supported on Gru. ([Issue](http://b/128960612), [Fix](http://crrev.com/c/1592502)) |
| + Incorrect sample rate supported on Scarlet. ([Issue](http://b/128959797), [Fix](http://crrev.com/c/1592502)) |
| + Read a large block of frames once on Grunt. ([Issue](http://b/138076270), [Fix](http://crrev.com/c/1708817)) |
| + Abnormal consuming size on Kefka-kernelnext. ([Issue](http://b/136629398), [Fix](http://crrev.com/c/1688675)) |
| + Unstable sample rate after suspending on Kukui. ([Issue](http://b/130053411), [Fix](http://crrev.com/c/1614344)) |
| + Incorrect sample rate with period size 256 on Reks. ([Issue](http://b/35565625)) |
| + Capture is not stable in the beginning on Grunt. ([Issue](http://b/113768831), [Fix](http://crrev.com/c/1228453)) |
| + Unstable playback rate on Grunt. ([Issue](http://b/112404244), [Fix](http://crrev.com/c/1183010)) |
| + Incorrect sample rate supported on Grunt. ([Issue](http://b/112402329), [Fix](http://crrev.com/c/1183006)) |
| + Incorrect sample rate with 1 channel on veyron-minnie. ([Issue](http://crbug.com/987550), [Fix](http://crrev.com/c/1720373)) |
| + Incorrect sample rate with 3 and 4 channels on samus. ([Issue](http://b/138825612)) |
| + Incorrect sample rate with 3 channels on buddy. ([Issue](http://b/138825293)) |