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