[autotest] audio_data: Use numpy fromstring function

Use numpy fromstring function to read samples from data string.
This speeds up the time to read data from large file or large data
string and parse data input channel samples.

The speed boost is significant. For example, for 150 seconds long data,
the time to read data dropped from 140 seconds to 0.2 seconds.

Also, remove some unused functions.

BUG=chromium:663616
TEST=Use check_quality tool, check the time to read samples becomes much
faster.

Change-Id: I6a9028445b3311cd01a3c75621eaf64b5b9d11b8
Reviewed-on: https://chromium-review.googlesource.com/409893
Commit-Ready: Cheng-Yi Chiang <cychiang@chromium.org>
Tested-by: Cheng-Yi Chiang <cychiang@chromium.org>
Reviewed-by: Kalin Stoyanov <kalin@chromium.org>
(cherry picked from commit 443d9083210bd0acd94e1e38153eb1d3a5312f90)
Reviewed-on: https://chromium-review.googlesource.com/422122
Reviewed-by: Ting Shen <phoenixshen@chromium.org>
Commit-Queue: Ting Shen <phoenixshen@chromium.org>
Tested-by: Ting Shen <phoenixshen@chromium.org>
diff --git a/client/cros/audio/audio_data.py b/client/cros/audio/audio_data.py
index 3cc150d..02f34da 100644
--- a/client/cros/audio/audio_data.py
+++ b/client/cros/audio/audio_data.py
@@ -7,6 +7,7 @@
 
 import contextlib
 import copy
+import numpy as np
 import struct
 import StringIO
 
@@ -73,69 +74,25 @@
             self.read_binary(binary)
 
 
-    def read_one_sample(self, handle):
-        """Reads one sample from handle.
-
-        @param handle: A handle that supports read() method.
-
-        @return: A number read from file handle based on sample format.
-                 None if there is no data to read.
-        """
-        data = handle.read(SAMPLE_FORMATS[self.sample_format]['size_bytes'])
-        if data == '':
-            return None
-        number, = struct.unpack(
-                SAMPLE_FORMATS[self.sample_format]['struct_format'], data)
-        return number
-
-
     def read_binary(self, binary):
         """Reads samples from binary and fills channel_data.
 
-        Reads one sample for each channel and repeats until the end of
-        input binary.
+        Reads samples of fixed width from binary string into a numpy array
+        and shapes them into each channel.
 
         @param binary: A string containing binary data.
         """
-        channel_index = 0
-        with contextlib.closing(StringIO.StringIO(binary)) as f:
-            number = self.read_one_sample(f)
-            while number is not None:
-                self.channel_data[channel_index].append(number)
-                channel_index = (channel_index + 1) % self.channel
-                number = self.read_one_sample(f)
+        sample_format_dict = SAMPLE_FORMATS[self.sample_format]
 
+        # The data type used in numpy fromstring function. For example,
+        # <i4 for 32-bit signed int.
+        np_dtype = '%s%d' % (sample_format_dict['struct_format'],
+                             sample_format_dict['size_bytes'])
 
-    def copy_channel_data(self, channel_data):
-        """Copies channel data and updates channel number.
-
-        @param channel_data: A list of list. The channel data to be copied.
-
-        """
-        self.channel_data = copy.deepcopy(channel_data)
-        self.channel = len(self.channel_data)
-
-
-    def write_to_file(self, file_path):
-        """Writes channel data to file.
-
-        Writes samples in each channel into file in index-first sequence.
-        E.g. (index_0, ch_0), (index_0, ch_1), ... ,(index_0, ch_N),
-             (index_1, ch_0), (index_1, ch_1), ... ,(index_1, ch_N).
-
-        @param file_path: The path to the file.
-
-        """
-        lengths = [len(self.channel_data[ch])
-                   for ch in xrange(self.channel)]
-        if len(set(lengths)) != 1:
-            raise AudioRawDataError(
-                    'Channel lengths are not the same: %r' % lengths)
-        length = lengths[0]
-
-        with open(file_path, 'wb') as f:
-            for index in xrange(length):
-                for ch in xrange(self.channel):
-                    f.write(struct.pack(
-                            SAMPLE_FORMATS[self.sample_format]['struct_format'],
-                            self.channel_data[ch][index]))
+        # Reads data from a string into 1-D array.
+        np_array = np.fromstring(binary, dtype=np_dtype)
+        n_frames = len(np_array) / self.channel
+        # Reshape np_array into an array of shape (n_frames, channel).
+        np_array = np_array.reshape(n_frames, self.channel)
+        # Transpose np_arrya so it becomes of shape (channel, n_frames).
+        self.channel_data = np_array.transpose()