• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# Copyright 2014 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""This module provides audio test data."""
7
8import logging
9import os
10import subprocess
11
12from autotest_lib.client.cros.audio import audio_data
13from autotest_lib.client.cros.audio import sox_utils
14
15
16class AudioTestDataException(Exception):
17    """Exception for audio test data."""
18    pass
19
20
21class AudioTestData(object):
22    """Class to represent audio test data."""
23    def __init__(self, data_format=None, path=None, frequencies=None,
24                 duration_secs=None):
25        """
26        Initializes an audio test file.
27
28        @param data_format: A dict containing data format including
29                            file_type, sample_format, channel, and rate.
30                            file_type: file type e.g. 'raw' or 'wav'.
31                            sample_format: One of the keys in
32                                           audio_data.SAMPLE_FORMAT.
33                            channel: number of channels.
34                            rate: sampling rate.
35        @param path: The path to the file.
36        @param frequencies: A list containing the frequency of each channel in
37                            this file. Only applicable to data of sine tone.
38        @param duration_secs: Duration of test file in seconds.
39
40        @raises: AudioTestDataException if the path does not exist.
41
42        """
43        self.data_format = data_format
44        if not os.path.exists(path):
45            raise AudioTestDataException('Can not find path %s' % path)
46        self.path = path
47        self.frequencies = frequencies
48        self.duration_secs = duration_secs
49
50
51    def get_binary(self):
52        """The binary of test data.
53
54        @returns: The binary of test data.
55
56        """
57        with open(self.path, 'rb') as f:
58            return f.read()
59
60
61    def convert(self, data_format, volume_scale):
62        """Converts the data format and returns a new AudioTestData object.
63
64        Converts the source file at self.path to a new data format.
65        The destination file path is self.path with a suffix. E.g.
66        original_path = '/tmp/test.raw'
67        data_format = dict(file_type='raw', sample_format='S32_LE',
68                           channel=2, rate=48000)
69        new_path = '/tmp/test_raw_48000_S32_LE_2.raw'
70
71        This method returns a new AudioTestData object so the original object is
72        not changed.
73
74        @param data_format: A dict containing new data format.
75        @param volume_scale: A float for volume scale used in sox command.
76                              E.g. 1.0 is the same. 0.5 to scale volume by
77                              half. -1.0 to invert the data.
78
79        @returns: A new AudioTestData object with converted format and new path.
80
81        """
82        original_path_without_ext, _ = os.path.splitext(self.path)
83        new_ext = '.' + data_format['file_type']
84        # New path will be the composition of original name, new data format,
85        # and new file type as extension.
86        new_path = (original_path_without_ext + '_' +
87                    '_'.join(str(x) for x in data_format.values()) + new_ext)
88
89        logging.debug('src data_format: %s', self.data_format)
90        logging.debug('dst data_format: %s', data_format)
91
92        # If source file has header, use that header.
93        if self.data_format['file_type'] != 'raw':
94            use_src_header = True
95            channels_src = None
96            rate_src = None
97            bits_src = None
98        else:
99            use_src_header = False
100            channels_src = self.data_format['channel']
101            rate_src = self.data_format['rate']
102            bits_src = audio_data.SAMPLE_FORMATS[
103                    self.data_format['sample_format']]['size_bytes'] * 8
104
105        # If dst file type is not raw, write file format into header of dst
106        # file.
107        use_dst_header = data_format['file_type'] != 'raw'
108
109        sox_utils.convert_format(
110                path_src=self.path,
111                channels_src=channels_src,
112                rate_src=rate_src,
113                bits_src=bits_src,
114                path_dst=new_path,
115                channels_dst=data_format['channel'],
116                rate_dst=data_format['rate'],
117                bits_dst=audio_data.SAMPLE_FORMATS[
118                        data_format['sample_format']]['size_bytes'] * 8,
119                volume_scale=volume_scale,
120                use_src_header=use_src_header,
121                use_dst_header=use_dst_header)
122
123        new_test_data = AudioTestData(path=new_path,
124                                      data_format=data_format)
125
126        return new_test_data
127
128
129    def delete(self):
130        """Deletes the file at self.path."""
131        os.unlink(self.path)
132
133
134class FakeTestData(object):
135    def __init__(self, frequencies, url=None, duration_secs=None):
136        """A fake test data which contains properties but no real data.
137
138        This is useful when we need to pass an AudioTestData object into a test
139        or audio_test_utils.check_recorded_frequency.
140
141        @param frequencies: A list containing the frequency of each channel in
142                            this file. Only applicable to data of sine tone.
143        @param url: The URL to the test file.
144        @param duration_secs: The duration of the file in seconds.
145
146        """
147        self.frequencies = frequencies
148        self.url = url
149        self.duration_secs = duration_secs
150
151
152class AudioTestDataGenerateOnDemand(AudioTestData):
153    """AudioTestData that generates real data on demand."""
154    def __init__(self, data_format=None, path=None, frequencies=None,
155                 duration_secs=None):
156        """
157        Initializes an audio test file that generate file on demand.
158
159        @param data_format: A dict containing data format including
160                            file_type, sample_format, channel, and rate.
161                            file_type: file type e.g. 'raw' or 'wav'.
162                            sample_format: One of the keys in
163                                           audio_data.SAMPLE_FORMAT.
164                            channel: number of channels.
165                            rate: sampling rate.
166        @param path: The path to the file.
167        @param frequencies: A list containing the frequency of each channel in
168                            this file. Only applicable to data of sine tone.
169        @param duration_secs: Duration of test file in seconds.
170
171        """
172        self.data_format = data_format
173        self.path = path
174        self.frequencies = frequencies
175        self.duration_secs = duration_secs
176
177
178    def generate_file(self):
179        """Generates the data with specified format and frequencies."""
180        sample_format = audio_data.SAMPLE_FORMATS[self.data_format['sample_format']]
181        bits = sample_format['size_bytes'] * 8
182
183        command = sox_utils.generate_sine_tone_cmd(
184                filename=self.path,
185                channels=self.data_format['channel'],
186                bits=bits,
187                rate=self.data_format['rate'],
188                duration=self.duration_secs,
189                frequencies=self.frequencies,
190                raw=(self.data_format['file_type'] == 'raw'))
191
192        subprocess.check_call(command)
193
194
195AUDIO_PATH = os.path.join(os.path.dirname(__file__))
196
197"""
198This test data contains frequency sweep from 20Hz to 20000Hz in two channels.
199Left channel sweeps from 20Hz to 20000Hz, while right channel sweeps from
20020000Hz to 20Hz. The sweep duration is 2 seconds. The begin and end of the file
201is padded with 0.4 seconds of silence. The file is two-channel raw data with
202each sample being a signed 16-bit integer in little-endian with sampling rate
20348000 samples/sec.
204"""
205SWEEP_TEST_FILE = AudioTestData(
206        path=os.path.join(AUDIO_PATH, 'pad_sweep_pad_16.raw'),
207        data_format=dict(file_type='raw',
208                         sample_format='S16_LE',
209                         channel=2,
210                         rate=48000))
211
212"""
213This test data contains fixed frequency sine wave in two channels.
214Left channel is 2KHz, while right channel is 1KHz. The duration is 6 seconds.
215The file format is two-channel raw data with each sample being a signed
21616-bit integer in little-endian with sampling rate 48000 samples/sec.
217"""
218FREQUENCY_TEST_FILE = AudioTestData(
219        path=os.path.join(AUDIO_PATH, 'fix_2k_1k_16.raw'),
220        data_format=dict(file_type='raw',
221                         sample_format='S16_LE',
222                         channel=2,
223                         rate=48000),
224        frequencies=[2000, 1000])
225
226"""
227This test data contains fixed frequency sine wave in two channels.
228Left and right channel are both 440Hz. The duration is 10 seconds.
229The file format is two-channel raw data with each sample being a signed
23016-bit integer in little-endian with sampling rate 48000 samples/sec.
231The volume is 0.1. The small volume is to avoid distortion when played
232on Chameleon.
233"""
234SIMPLE_FREQUENCY_TEST_FILE = AudioTestData(
235        path=os.path.join(AUDIO_PATH, 'fix_440_16.raw'),
236        data_format=dict(file_type='raw',
237                         sample_format='S16_LE',
238                         channel=2,
239                         rate=48000),
240        frequencies=[440, 440])
241
242"""
243This test data contains fixed frequency sine wave in two channels.
244Left and right channel are both 1330 Hz. The duration is 10 seconds.
245The file format is two-channel raw data with each sample being a signed
24616-bit integer in little-endian with sampling rate 48000 samples/sec.
247The volume is 0.1. The small volume is to avoid distortion when played
248on Chameleon.
249"""
250SIMPLE_FREQUENCY_TEST_1330_FILE = AudioTestData(
251        path=os.path.join(AUDIO_PATH, 'fix_1330_16.raw'),
252        data_format=dict(file_type='raw',
253                         sample_format='S16_LE',
254                         channel=2,
255                         rate=48000),
256        frequencies=[1330, 1330])
257
258"""
259This test data contains fixed frequency sine wave in two channels.
260Left and right channel are both 660Hz. The duration is 60 seconds.
261The file format is two-channel wav data with each sample being a signed
26216-bit integer in little-endian with sampling rate 48000 samples/sec.
263The volume is 1.0.
264"""
265SIMPLE_FREQUENCY_LOUD_WAVE_FILE = AudioTestDataGenerateOnDemand(
266        path=os.path.join(AUDIO_PATH, 'fix_660_16.wav'),
267        data_format=dict(file_type='wav',
268                         sample_format='S16_LE',
269                         channel=2,
270                         rate=48000),
271        duration_secs=60,
272        frequencies=[660, 660])
273
274
275"""
276This test data contains fixed frequency sine wave in one channel.
277Left channel is 440Hz. The duration is 10 seconds.
278The file format is two-channel raw data with each sample being a signed
27916-bit integer in little-endian with sampling rate 48000 samples/sec.
280The volume is 0.5.
281"""
282LEFT_CHANNEL_TEST_FILE = AudioTestDataGenerateOnDemand(
283        path=os.path.join(AUDIO_PATH, 'left_440_half.raw'),
284        data_format=dict(file_type='raw',
285                         sample_format='S16_LE',
286                         channel=2,
287                         rate=48000),
288        duration_secs=10,
289        frequencies=[440, 0])
290
291"""
292This test data contains fixed frequency sine wave in one channel.
293Right channel is 440Hz. The duration is 10 seconds.
294The file format is two-channel raw data with each sample being a signed
29516-bit integer in little-endian with sampling rate 48000 samples/sec.
296The volume is 0.5.
297"""
298RIGHT_CHANNEL_TEST_FILE = AudioTestDataGenerateOnDemand(
299        path=os.path.join(AUDIO_PATH, 'right_440_half.raw'),
300        data_format=dict(file_type='raw',
301                         sample_format='S16_LE',
302                         channel=2,
303                         rate=48000),
304        duration_secs=10,
305        frequencies=[0, 440])
306
307"""
308This test data contains fixed frequency sine wave in two channels.
309Left and right channel are both 440Hz. The duration is 10 seconds.
310The file format is two-channel raw data with each sample being a signed
31116-bit integer in little-endian with sampling rate 48000 samples/sec.
312The volume is 0.5. The larger volume is needed to test internal
313speaker of Cros device because the microphone of Chameleon is not sensitive
314enough.
315"""
316SIMPLE_FREQUENCY_SPEAKER_TEST_FILE = AudioTestData(
317        path=os.path.join(AUDIO_PATH, 'fix_440_16_half.raw'),
318        data_format=dict(file_type='raw',
319                         sample_format='S16_LE',
320                         channel=2,
321                         rate=48000),
322        frequencies=[440, 440])
323
324"""
325Media test verification for 256Hz frequency (headphone audio).
326"""
327MEDIA_HEADPHONE_TEST_FILE = FakeTestData(frequencies=[256, 256])
328
329"""
330Media test verification for 512Hz frequency (onboard speakers).
331"""
332MEDIA_SPEAKER_TEST_FILE = FakeTestData(frequencies=[512, 512])
333
334"""
335Test file for 10 min playback for headphone. Left frequency is 1350Hz, right
336frequency is 870 Hz, and amplitude is 0.85.
337"""
338HEADPHONE_10MIN_TEST_FILE = FakeTestData(
339        frequencies=[1350, 870],
340        url=('http://commondatastorage.googleapis.com/chromiumos-test-assets-'
341             'public/audio_test/chameleon/Headphone/L1350_R870_A085_10min.wav'),
342        duration_secs=600)
343