1 package com.android.cts.verifier.audio;
2 
3 import android.media.AudioManager;
4 import android.media.AudioTrack;
5 
6 import java.util.ArrayList;
7 import java.util.Random;
8 
9 /**
10  * This class stores common constants and methods.
11  */
12 public class Common {
13 
14     // Internal version code for tracking small changes.
15     // Format is a monotonic decimal number with Mmmpp
16     // M = major version
17     // mm = minor version
18     // pp = patch
19     public static final int VERSION_CODE = 10102;
20     // Key for storing in a protobuf.
21     public static final String KEY_VERSION_CODE = "version_code";
22 
23   public static final int RECORDING_SAMPLE_RATE_HZ
24       = AudioRecordHelper.getInstance().getSampleRate();
25   public static final int PLAYING_SAMPLE_RATE_HZ
26       = AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC);
27 
28   // Default constants.
29   public static final double PASSING_THRESHOLD_DB = -40.0;
30   public static final double PIP_DURATION_S = 0.004;
31   public static final double PAUSE_DURATION_S = 0.016;
32   public static final int PREFIX_NUM_CHIPS = 1023;
33   public static final int PREFIX_SAMPLES_PER_CHIP = 4;
34   public static final double PREFIX_LENGTH_S = 0.1;
35   public static final double PAUSE_BEFORE_PREFIX_DURATION_S = 0.5;
36   public static final double PAUSE_AFTER_PREFIX_DURATION_S = 0.4;
37   public static final double MIN_FREQUENCY_HZ = 500;
38   public static final double MAX_FREQUENCY_HZ = 21000;
39   public static final double FREQUENCY_STEP_HZ = 100;
40   public static final int SIGNAL_MIN_STRENGTH_DB_ABOVE_NOISE = 10;
41   public static final int REPETITIONS = 5;
42   public static final int NOISE_SAMPLES = 3;
43 
44   public static final double[] FREQUENCIES_ORIGINAL = originalFrequencies();
45   public static final int PIP_NUM = FREQUENCIES_ORIGINAL.length;
46   public static final int[] ORDER = order();
47   public static final double[] FREQUENCIES = frequencies();
48 
49   public static final double[] WINDOW_FOR_RECORDER =
50       hann(Util.toLength(PIP_DURATION_S, RECORDING_SAMPLE_RATE_HZ));
51   public static final double[] WINDOW_FOR_PLAYER =
52       hann(Util.toLength(PIP_DURATION_S, PLAYING_SAMPLE_RATE_HZ));
53 
54   public static final double[] PREFIX_FOR_RECORDER = prefix(RECORDING_SAMPLE_RATE_HZ);
55   public static final double[] PREFIX_FOR_PLAYER = prefix(PLAYING_SAMPLE_RATE_HZ);
56 
57   /**
58    * Get a Hann window.
59    */
hann(int windowWidth)60   private static double[] hann(int windowWidth) {
61     double[] envelopeArray = new double[windowWidth];
62     for (int i = 0; i < windowWidth; i++) {
63       envelopeArray[i] = 0.5
64           * (1 - Math.cos(2 * Math.PI * i / windowWidth));
65     }
66     return envelopeArray;
67   }
68 
69   /**
70    * Get a maximum length sequence, used as prefix to indicate start of signal.
71    */
prefix(int rate)72   private static double[] prefix(int rate) {
73     double[] codeSequence = new double[PREFIX_NUM_CHIPS];
74     for (int i = 0; i < PREFIX_NUM_CHIPS; i++) {
75       if (i < 10) {
76         codeSequence[i] = 1;
77       } else {
78         codeSequence[i] = -codeSequence[i - 6] * codeSequence[i - 7]
79             * codeSequence[i - 9] * codeSequence[i - 10];
80       }
81     }
82     double[] prefixArray = new double[PREFIX_NUM_CHIPS * PREFIX_SAMPLES_PER_CHIP];
83     int offset = 0;
84     for (int i = 0; i < PREFIX_NUM_CHIPS; i++) {
85       double value = codeSequence[i];
86       for (int j = 0; j < PREFIX_SAMPLES_PER_CHIP; j++) {
87         prefixArray[offset + j] = value;
88       }
89       offset += PREFIX_SAMPLES_PER_CHIP;
90     }
91     int prefixLength = (int) Math.round(PREFIX_LENGTH_S * rate);
92     double[] samplePrefixArray = new double[prefixLength];
93     for (int i = 0; i < prefixLength; i++) {
94       double index = (double) i / prefixLength * (prefixArray.length - 1);
95       samplePrefixArray[i] = (1 - index + Math.floor(index)) * prefixArray[(int) Math.floor(index)]
96           + (1 + index - Math.ceil(index)) * prefixArray[(int) Math.ceil(index)];
97     }
98     return samplePrefixArray;
99   }
100 
101   /**
102    * Returns array consists the frequencies of the test pips in the order that will be used in test.
103    */
frequencies()104   private static double[] frequencies() {
105     double[] originalFrequencies = originalFrequencies();
106 
107     double[] randomFrequencies = new double[Common.REPETITIONS * originalFrequencies.length];
108     for (int i = 0; i < REPETITIONS * originalFrequencies.length; i++) {
109       randomFrequencies[i] = originalFrequencies[ORDER[i] % originalFrequencies.length];
110     }
111 
112     return randomFrequencies;
113   }
114 
115   /**
116    * Returns array consists the frequencies of the test pips.
117    */
originalFrequencies()118   private static double[] originalFrequencies() {
119     ArrayList<Double> frequencies = new ArrayList<Double>();
120     double frequency = Common.MIN_FREQUENCY_HZ;
121     while (frequency <= Common.MAX_FREQUENCY_HZ) {
122       frequencies.add(new Double(frequency));
123       if ((frequency >= 18500) && (frequency < 20000)) {
124         frequency += Common.FREQUENCY_STEP_HZ;
125       } else {
126         frequency += Common.FREQUENCY_STEP_HZ * 10;
127       }
128     }
129     Double[] frequenciesArray = frequencies.toArray(new Double[frequencies.size()]);
130     double[] frequenciesPrimitiveArray = new double[frequenciesArray.length];
131     for (int i = 0; i < frequenciesArray.length; i++) {
132       frequenciesPrimitiveArray[i] = frequenciesArray[i];
133     }
134     return frequenciesPrimitiveArray;
135   }
136 
137   /**
138    * Fisher-Yates shuffle.
139    */
order()140   private static int[] order() {
141     int[] order = new int[REPETITIONS * PIP_NUM];
142     long seed = 0;
143     Random generator = new Random(seed);
144     for (int i = 0; i < REPETITIONS * PIP_NUM; i++) {
145       int j = generator.nextInt(i + 1);
146       order[i] = order[j];
147       order[j] = i;
148     }
149     return order;
150   }
151 }
152