blob: 02f34da58ccb16876a74ba7adde9dca465f9a466 [file] [log] [blame]
Cheng-Yi Chiang348270a2014-09-01 19:59:38 +08001#!/usr/bin/env python
Cheng-Yi Chiang17a25272014-11-28 19:05:13 +08002# Copyright 2014 The Chromium OS Authors. All rights reserved.
Cheng-Yi Chiang348270a2014-09-01 19:59:38 +08003# 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
Cheng-Yi Chiang17a25272014-11-28 19:05:13 +08009import copy
Cheng-Yi Chiang443d9082016-11-10 16:55:47 +080010import numpy as np
Cheng-Yi Chiang348270a2014-09-01 19:59:38 +080011import 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
Cheng-Yi Chiang8302dbf2015-11-19 12:56:25 +080034def 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
Cheng-Yi Chiang17a25272014-11-28 19:05:13 +080046class AudioRawDataError(Exception):
47 """Error in AudioRawData."""
48 pass
49
50
Cheng-Yi Chiang348270a2014-09-01 19:59:38 +080051class 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
Cheng-Yi Chiang17a25272014-11-28 19:05:13 +080064 @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.
Cheng-Yi Chiang348270a2014-09-01 19:59:38 +080067 @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
Cheng-Yi Chiang17a25272014-11-28 19:05:13 +080073 if binary:
74 self.read_binary(binary)
Cheng-Yi Chiang348270a2014-09-01 19:59:38 +080075
76
Cheng-Yi Chiang348270a2014-09-01 19:59:38 +080077 def read_binary(self, binary):
78 """Reads samples from binary and fills channel_data.
79
Cheng-Yi Chiang443d9082016-11-10 16:55:47 +080080 Reads samples of fixed width from binary string into a numpy array
81 and shapes them into each channel.
Cheng-Yi Chiang348270a2014-09-01 19:59:38 +080082
83 @param binary: A string containing binary data.
84 """
Cheng-Yi Chiang443d9082016-11-10 16:55:47 +080085 sample_format_dict = SAMPLE_FORMATS[self.sample_format]
Cheng-Yi Chiang17a25272014-11-28 19:05:13 +080086
Cheng-Yi Chiang443d9082016-11-10 16:55:47 +080087 # 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'])
Cheng-Yi Chiang17a25272014-11-28 19:05:13 +080091
Cheng-Yi Chiang443d9082016-11-10 16:55:47 +080092 # 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()