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