# Path: ripyl/streaming.py
# class SampleChunk(object):
#     ''''Sample stream object
#     
#     This represents a "chunk" of samples contained in a numpy array
#     stored in the samples attribute.
# 
#     '''
#     def __init__(self, samples, start_time, sample_period):
#         self.samples = samples
#         self.start_time = start_time
#         self.sample_period = sample_period
# 
# class StreamError(RuntimeError):
#     '''Custom exception class for edge and sample streams'''
#     pass
# 
# class ChunkExtractor(object):
#     '''Utility class that pulls arbitrarily sized chunks from a sample stream'''
#     def __init__(self, stream):
#         self.stream = stream
#         self.sample_buf = []
#         self.buf_count = 0
#         self.stream_ended = False
#         self.buf_start_time = 0.0
#         self.sample_period = 0.0
# 
#     def next_chunk(self, chunk_size=10000):
#         '''Get a new chunk of samples from the stream
# 
#         chunk_size (int)
#             The number of samples for this chunk.
# 
#         Returns a SampleChunk object. If the stream had fewer than chunk_size samples
#           remaining then the SampleChunk.samples array is sized to hold only those samples.
# 
#         Returns None if the stream has ended.
#         '''
#         if self.stream_ended:
#             return None
# 
#         while True:
#             if self.buf_count < chunk_size: # Need to get another chunk
#                 try:
#                     sc = next(self.stream)
#                     if len(self.sample_buf) == 0:
#                         self.buf_start_time = sc.start_time
#                         self.sample_period = sc.sample_period
#                     self.sample_buf.append(sc.samples)
#                     self.buf_count += len(sc.samples)
# 
#                 except StopIteration:
#                     self.stream_ended = True
# 
#             if self.stream_ended and self.buf_count < chunk_size:
#                 # Not enough samples in buffer for a full sized chunk
#                 chunk_size = self.buf_count
#                 if chunk_size == 0:
#                     break
# 
#             if self.buf_count >= chunk_size:
#                 # We have enough buffered samples to return a new chunk
#                 out_samp = np.empty(chunk_size, dtype=float)
#                 out_count = 0
#                 for b in self.sample_buf:
#                     if out_count + len(b) <= chunk_size:
#                         use_count = len(b)
#                     else:
#                         use_count = chunk_size - out_count
# 
#                     out_samp[out_count:out_count + use_count] = b[:use_count]
#                     out_count += use_count
# 
#                 out_time = self.buf_start_time
# 
#                 if self.buf_count > chunk_size:
#                     # There are unused samples remaining in the buffer
#                     # Place these at the start of the buffer for the next call to next_chunk()
#                     self.sample_buf = [self.sample_buf[-1][use_count:]]
#                     self.buf_count = len(self.sample_buf[0])
#                     self.buf_start_time = self.buf_start_time + self.sample_period * chunk_size
#                 else:
#                     self.sample_buf = []
#                     self.buf_count = 0
# 
#                 return SampleChunk(out_samp, out_time, self.sample_period)
# 
#             if self.stream_ended:
#                 break
# 
#         return None
# 
#     def next_samples(self, sample_count=10000):
#         '''Get a new set of raw samples from the stream
# 
#         sample_count (int)
#             The number of samples for the array.
# 
#         Returns a numpy array of float. If the stream had fewer than sample_count samples
#           remaining then the array is sized to hold only those samples.
# 
#         Returns None if the stream has ended.
#         '''
# 
#         sc = self.next_chunk(sample_count)
#         if sc is not None:
#             return sc.samples
#         else:
#             return None
# 
#     def buffered_chunk(self):
#         '''Get all remaining buffered samples'''
#         return self.next_chunk(self.buf_count)


from ripyl.streaming import SampleChunk, StreamError, ChunkExtractor
import numpy as np
import scipy.signal as signal
    '''

    ulp = float(full_scale) / 2**bits

    for sc in samples:
        filt = np.floor(sc.samples / ulp) * ulp
        yield SampleChunk(filt, sc.start_time, sc.sample_period)


def amplify(samples, gain=1.0, offset=0.0):
    '''Apply gain and offset to a sample stream
    
    This modifies samples such that output = input * gain + offset.
    
    This is a generator function.
    
    samples (iterable of SampleChunk objects)
        An iterable sample stream to modify.
    
    gain (float)
        The gain multiplier for the samples
    
    offset (float)
        The additive offset for the samples
    
    Yields a sample stream.
    '''

    for sc in samples:
        filt = (sc.samples * gain) + offset

# Path: ripyl/streaming.py
# class SampleChunk(object):
#     ''''Sample stream object
#     
#     This represents a "chunk" of samples contained in a numpy array
#     stored in the samples attribute.
# 
#     '''
#     def __init__(self, samples, start_time, sample_period):
#         self.samples = samples
#         self.start_time = start_time
#         self.sample_period = sample_period
# 
# class StreamError(RuntimeError):
#     '''Custom exception class for edge and sample streams'''
#     pass
# 
# class ChunkExtractor(object):
#     '''Utility class that pulls arbitrarily sized chunks from a sample stream'''
#     def __init__(self, stream):
#         self.stream = stream
#         self.sample_buf = []
#         self.buf_count = 0
#         self.stream_ended = False
#         self.buf_start_time = 0.0
#         self.sample_period = 0.0
# 
#     def next_chunk(self, chunk_size=10000):
#         '''Get a new chunk of samples from the stream
# 
#         chunk_size (int)
#             The number of samples for this chunk.
# 
#         Returns a SampleChunk object. If the stream had fewer than chunk_size samples
#           remaining then the SampleChunk.samples array is sized to hold only those samples.
# 
#         Returns None if the stream has ended.
#         '''
#         if self.stream_ended:
#             return None
# 
#         while True:
#             if self.buf_count < chunk_size: # Need to get another chunk
#                 try:
#                     sc = next(self.stream)
#                     if len(self.sample_buf) == 0:
#                         self.buf_start_time = sc.start_time
#                         self.sample_period = sc.sample_period
#                     self.sample_buf.append(sc.samples)
#                     self.buf_count += len(sc.samples)
# 
#                 except StopIteration:
#                     self.stream_ended = True
# 
#             if self.stream_ended and self.buf_count < chunk_size:
#                 # Not enough samples in buffer for a full sized chunk
#                 chunk_size = self.buf_count
#                 if chunk_size == 0:
#                     break
# 
#             if self.buf_count >= chunk_size:
#                 # We have enough buffered samples to return a new chunk
#                 out_samp = np.empty(chunk_size, dtype=float)
#                 out_count = 0
#                 for b in self.sample_buf:
#                     if out_count + len(b) <= chunk_size:
#                         use_count = len(b)
#                     else:
#                         use_count = chunk_size - out_count
# 
#                     out_samp[out_count:out_count + use_count] = b[:use_count]
#                     out_count += use_count
# 
#                 out_time = self.buf_start_time
# 
#                 if self.buf_count > chunk_size:
#                     # There are unused samples remaining in the buffer
#                     # Place these at the start of the buffer for the next call to next_chunk()
#                     self.sample_buf = [self.sample_buf[-1][use_count:]]
#                     self.buf_count = len(self.sample_buf[0])
#                     self.buf_start_time = self.buf_start_time + self.sample_period * chunk_size
#                 else:
#                     self.sample_buf = []
#                     self.buf_count = 0
# 
#                 return SampleChunk(out_samp, out_time, self.sample_period)
# 
#             if self.stream_ended:
#                 break
# 
#         return None
# 
#     def next_samples(self, sample_count=10000):
#         '''Get a new set of raw samples from the stream
# 
#         sample_count (int)
#             The number of samples for the array.
# 
#         Returns a numpy array of float. If the stream had fewer than sample_count samples
#           remaining then the array is sized to hold only those samples.
# 
#         Returns None if the stream has ended.
#         '''
# 
#         sc = self.next_chunk(sample_count)
#         if sc is not None:
#             return sc.samples
#         else:
#             return None
# 
#     def buffered_chunk(self):
#         '''Get all remaining buffered samples'''
#         return self.next_chunk(self.buf_count)


# Path: ripyl/sigproc.py
from ripyl.streaming import SampleChunk, StreamError, ChunkExtractor
import numpy as np
import scipy.signal as signal

    '''

    ulp = float(full_scale) / 2**bits

    for sc in samples:
        filt = np.floor(sc.samples / ulp) * ulp
        yield SampleChunk(filt, sc.start_time, sc.sample_period)


def amplify(samples, gain=1.0, offset=0.0):
    '''Apply gain and offset to a sample stream
    
    This modifies samples such that output = input * gain + offset.
    
    This is a generator function.
    
    samples (iterable of SampleChunk objects)
        An iterable sample stream to modify.
    
    gain (float)
        The gain multiplier for the samples
    
    offset (float)
        The additive offset for the samples
    
    Yields a sample stream.
    '''

    for sc in samples:
        filt = (sc.samples * gain) + offset

        yield SampleChunk(filt, sc.start_time, sc.sample_period)