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