content: #!/usr/bin/env python
  '''Timing interface'''
  
  from bisect import bisect_left, bisect_right
  
  import numpy as np
  import pandas as pd
  
  import librosa
  
  
  class TimeSlice(object):
      """
      A slice of time:  has a start time, a duration, and a reference to an Audio object.
      """
  
      def __init__(self, time, duration, audio, unit='s'):
          self.time = pd.to_timedelta(time, unit=unit)
          self.duration = pd.to_timedelta(duration, unit=unit)
          self.audio = audio
  
      def __repr__(self):
          args = self.time.delta * 1e-9, self.duration.delta * 1e-9
          return '<TimeSlice, start: {0:.2f}, duration: {1:.2f}>'.format(*args)
  
      def get_samples(self):
          """
          Gets the samples corresponding to this TimeSlice from the parent audio object.
          """
          start = self.time.delta * 1e-9
          duration = self.duration.delta * 1e-9
          starting_sample, ending_sample = librosa.time_to_samples(
              [start, start + duration], self.audio.sample_rate
          )
  
          left_offsets, right_offsets = self._get_offsets(
              starting_sample, ending_sample, self.audio.num_channels
          )
  
          samples = self._offset_samples(
              starting_sample,
              ending_sample,
              left_offsets,
              right_offsets,
              self.audio.num_channels,
          )
  
          return samples, left_offsets[0], right_offsets[0]
  
      def _get_offsets(self, starting_sample, ending_sample, num_channels):
          """
          Find the offset to the next zero-crossing, for each channel.
          """
          offsets = []
          for zero_index in self.audio.zero_indexes:
              index = bisect_left(zero_index, starting_sample) - 1
              if index < 0:
                  starting_offset = 0
              else:
                  starting_crossing = zero_index[index]
                  starting_offset = starting_crossing - starting_sample
  
              index = bisect_left(zero_index, ending_sample)
              if index >= len(zero_index):
                  ending_offset = 0
              else:
                  zci = min(bisect_right(zero_index, ending_sample), len(zero_index) - 1)
                  ending_crossing = zero_index[zci]
                  ending_offset = ending_crossing - ending_sample
  
              offsets.append((starting_offset, ending_offset))
  
          if num_channels == 1:
              results = (offsets[0], offsets[0])
          elif num_channels == 2:
              results = (offsets[0], offsets[1])
  
          return results
  
      def _offset_samples(
          self, starting_sample, ending_sample, left_offsets, right_offsets, num_channels
      ):
          """
          Does the offset itself.
          """
          left_slice = (
              0,
              slice(starting_sample + left_offsets[0], ending_sample + left_offsets[1]),
          )
          right_slice = left_slice
  
          if num_channels == 2:
              right_slice = (
                  1,
                  slice(
                      starting_sample + right_offsets[0], ending_sample + right_offsets[1]
                  ),
              )
  
          left_channel = self.audio.raw_samples[left_slice]
          right_channel = self.audio.raw_samples[right_slice]
          return np.array([left_channel, right_channel])
  
  
  class TimingList(list):
      """
      A list of TimeSlices.
      """
  
      def __init__(self, name, timings, audio, unit='s'):
  
          super(self.__class__, self).__init__()
  
          self.name = name
          for (start, duration) in timings:
              time_slice = TimeSlice(start, duration, audio, unit=unit)
              self.append(time_slice)
  