• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3#   Copyright 2017 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16"""This module provides abstraction of audio data."""
17
18import numpy
19"""The dict containing information on how to parse sample from raw data.
20
21Keys: The sample format as in aplay command.
22Values: A dict containing:
23    message: Human-readable sample format.
24    dtype_str: Data type used in numpy dtype.  Check
25               https://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html
26               for supported data type.
27    size_bytes: Number of bytes for one sample.
28"""
29SAMPLE_FORMATS = dict(
30    S32_LE=dict(message='Signed 32-bit integer, little-endian',
31                dtype_str='<i',
32                size_bytes=4),
33    S16_LE=dict(message='Signed 16-bit integer, little-endian',
34                dtype_str='<i',
35                size_bytes=2))
36
37
38def get_maximum_value_from_sample_format(sample_format):
39    """Gets the maximum value from sample format.
40
41    Args:
42        sample_format: A key in SAMPLE_FORMAT.
43
44    Returns:The maximum value the sample can hold + 1.
45
46    """
47    size_bits = SAMPLE_FORMATS[sample_format]['size_bytes'] * 8
48    return 1 << (size_bits - 1)
49
50
51class AudioRawDataError(Exception):
52    """Error in AudioRawData."""
53
54
55class AudioRawData(object):
56    """The abstraction of audio raw data.
57
58    @property channel: The number of channels.
59    @property channel_data: A list of lists containing samples in each channel.
60                            E.g., The third sample in the second channel is
61                            channel_data[1][2].
62    @property sample_format: The sample format which should be one of the keys
63                             in audio_data.SAMPLE_FORMATS.
64    """
65
66    def __init__(self, binary, channel, sample_format):
67        """Initializes an AudioRawData.
68
69        Args:
70            binary: A string containing binary data. If binary is not None,
71                       The samples in binary will be parsed and be filled into
72                       channel_data.
73            channel: The number of channels.
74            sample_format: One of the keys in audio_data.SAMPLE_FORMATS.
75        """
76        self.channel = channel
77        self.channel_data = [[] for _ in range(self.channel)]
78        self.sample_format = sample_format
79        if binary:
80            self.read_binary(binary)
81
82    def read_binary(self, binary):
83        """Reads samples from binary and fills channel_data.
84
85        Reads samples of fixed width from binary string into a numpy array
86        and shapes them into each channel.
87
88        Args:
89            binary: A string containing binary data.
90        """
91        sample_format_dict = SAMPLE_FORMATS[self.sample_format]
92
93        # The data type used in numpy fromstring function. For example,
94        # <i4 for 32-bit signed int.
95        np_dtype = '%s%d' % (sample_format_dict['dtype_str'],
96                             sample_format_dict['size_bytes'])
97
98        # Reads data from a string into 1-D array.
99        np_array = numpy.fromstring(binary, dtype=np_dtype)
100
101        n_frames = len(np_array) / self.channel
102        # Reshape np_array into an array of shape (n_frames, channel).
103        np_array = np_array.reshape(int(n_frames), self.channel)
104        # Transpose np_arrya so it becomes of shape (channel, n_frames).
105        self.channel_data = np_array.transpose()
106