Home | History | Annotate | Download | only in audio
      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 
      8 import contextlib
      9 import copy
     10 import numpy as np
     11 import struct
     12 import StringIO
     13 
     14 
     15 """The dict containing information on how to parse sample from raw data.
     16 
     17 Keys: The sample format as in aplay command.
     18 Values: 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 """
     25 SAMPLE_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 
     36 def 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 
     48 class AudioRawDataError(Exception):
     49     """Error in AudioRawData."""
     50     pass
     51 
     52 
     53 class 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