1*9c5db199SXin Li#!/usr/bin/env python3 2*9c5db199SXin Li# Copyright 2014 The Chromium OS Authors. All rights reserved. 3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 4*9c5db199SXin Li# found in the LICENSE file. 5*9c5db199SXin Li 6*9c5db199SXin Li"""This module provides abstraction of audio data.""" 7*9c5db199SXin Li 8*9c5db199SXin Lifrom __future__ import absolute_import 9*9c5db199SXin Lifrom __future__ import division 10*9c5db199SXin Lifrom __future__ import print_function 11*9c5db199SXin Liimport contextlib 12*9c5db199SXin Liimport copy 13*9c5db199SXin Liimport numpy as np 14*9c5db199SXin Liimport struct 15*9c5db199SXin Lifrom six.moves import range 16*9c5db199SXin Liimport six 17*9c5db199SXin Li 18*9c5db199SXin Li 19*9c5db199SXin Li"""The dict containing information on how to parse sample from raw data. 20*9c5db199SXin Li 21*9c5db199SXin LiKeys: The sample format as in aplay command. 22*9c5db199SXin LiValues: A dict containing: 23*9c5db199SXin Li message: Human-readable sample format. 24*9c5db199SXin Li dtype_str: Data type used in numpy dtype. Check 25*9c5db199SXin Li https://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html 26*9c5db199SXin Li for supported data type. 27*9c5db199SXin Li size_bytes: Number of bytes for one sample. 28*9c5db199SXin Li""" 29*9c5db199SXin LiSAMPLE_FORMATS = dict( 30*9c5db199SXin Li S32_LE=dict( 31*9c5db199SXin Li message='Signed 32-bit integer, little-endian', 32*9c5db199SXin Li dtype_str='<i', 33*9c5db199SXin Li size_bytes=4), 34*9c5db199SXin Li S16_LE=dict( 35*9c5db199SXin Li message='Signed 16-bit integer, little-endian', 36*9c5db199SXin Li dtype_str='<i', 37*9c5db199SXin Li size_bytes=2)) 38*9c5db199SXin Li 39*9c5db199SXin Li 40*9c5db199SXin Lidef get_maximum_value_from_sample_format(sample_format): 41*9c5db199SXin Li """Gets the maximum value from sample format. 42*9c5db199SXin Li 43*9c5db199SXin Li @param sample_format: A key in SAMPLE_FORMAT. 44*9c5db199SXin Li 45*9c5db199SXin Li @returns: The maximum value the sample can hold + 1. 46*9c5db199SXin Li 47*9c5db199SXin Li """ 48*9c5db199SXin Li size_bits = SAMPLE_FORMATS[sample_format]['size_bytes'] * 8 49*9c5db199SXin Li return 1 << (size_bits - 1) 50*9c5db199SXin Li 51*9c5db199SXin Li 52*9c5db199SXin Liclass AudioRawDataError(Exception): 53*9c5db199SXin Li """Error in AudioRawData.""" 54*9c5db199SXin Li pass 55*9c5db199SXin Li 56*9c5db199SXin Li 57*9c5db199SXin Liclass AudioRawData(object): 58*9c5db199SXin Li """The abstraction of audio raw data. 59*9c5db199SXin Li 60*9c5db199SXin Li @property channel: The number of channels. 61*9c5db199SXin Li @property channel_data: A list of lists containing samples in each channel. 62*9c5db199SXin Li E.g., The third sample in the second channel is 63*9c5db199SXin Li channel_data[1][2]. 64*9c5db199SXin Li @property sample_format: The sample format which should be one of the keys 65*9c5db199SXin Li in audio_data.SAMPLE_FORMATS. 66*9c5db199SXin Li """ 67*9c5db199SXin Li def __init__(self, binary, channel, sample_format): 68*9c5db199SXin Li """Initializes an AudioRawData. 69*9c5db199SXin Li 70*9c5db199SXin Li @param binary: A string containing binary data. If binary is not None, 71*9c5db199SXin Li The samples in binary will be parsed and be filled into 72*9c5db199SXin Li channel_data. 73*9c5db199SXin Li @param channel: The number of channels. 74*9c5db199SXin Li @param sample_format: One of the keys in audio_data.SAMPLE_FORMATS. 75*9c5db199SXin Li """ 76*9c5db199SXin Li self.channel = channel 77*9c5db199SXin Li self.channel_data = [[] for _ in range(self.channel)] 78*9c5db199SXin Li self.sample_format = sample_format 79*9c5db199SXin Li if binary: 80*9c5db199SXin Li self.read_binary(binary) 81*9c5db199SXin Li 82*9c5db199SXin Li 83*9c5db199SXin Li def read_binary(self, binary): 84*9c5db199SXin Li """Reads samples from binary and fills channel_data. 85*9c5db199SXin Li 86*9c5db199SXin Li Reads samples of fixed width from binary string into a numpy array 87*9c5db199SXin Li and shapes them into each channel. 88*9c5db199SXin Li 89*9c5db199SXin Li @param binary: A string containing binary data. 90*9c5db199SXin Li """ 91*9c5db199SXin Li sample_format_dict = SAMPLE_FORMATS[self.sample_format] 92*9c5db199SXin Li 93*9c5db199SXin Li # The data type used in numpy fromstring function. For example, 94*9c5db199SXin Li # <i4 for 32-bit signed int. 95*9c5db199SXin Li np_dtype = '%s%d' % (sample_format_dict['dtype_str'], 96*9c5db199SXin Li sample_format_dict['size_bytes']) 97*9c5db199SXin Li 98*9c5db199SXin Li # Reads data from a string into 1-D array. 99*9c5db199SXin Li np_array = np.fromstring(binary, dtype=np_dtype) 100*9c5db199SXin Li n_frames = len(np_array) // self.channel 101*9c5db199SXin Li # Reshape np_array into an array of shape (n_frames, channel). 102*9c5db199SXin Li np_array = np_array.reshape(n_frames, self.channel) 103*9c5db199SXin Li # Transpose np_arrya so it becomes of shape (channel, n_frames). 104*9c5db199SXin Li self.channel_data = np_array.transpose() 105