See the following instructions and write the code in Python: # Other imports as
ID: 3749658 • Letter: S
Question
See the following instructions and write the code in Python:
# Other imports as needed
Explanation / Answer
import math import numpy as np import scipy.io.wavfile class AudioFrames: """AudioFrames A class for iterating over frames of audio data """ def __init__(self, filename, adv_ms, len_ms): """"AudioFrames(filename, adv_ms, len_ms) Create a stream of audio frames where each is in len_ms milliseconds long and frames are advanced by adv_ms. """ self.len_ms = len_ms self.adv_ms = adv_ms self.offset = 0 # offset start of frames to sample N (currently unused # Set up audio file self.filename = filename self.incomplete = False # Determine which interface to use self.init_scipywavfile() # Compute framing parameters in samples self.adv_N = int(self.Fs * (self.adv_ms / 1000.0)) self.len_N = int(self.Fs * (self.len_ms / 1000.0)) self.framedelta = np.timedelta64(adv_ms, 'ms') self.cumoffset = np.timedelta64(0, 's') # Compute parameters related to frame overlap and skip # number new samples each frame self.nonoverlap_N = min([self.len_N - self.adv_N, self.len_N]) # number of samples from previous frame self.overlap_N = max([self.len_N - self.adv_N, 0]) # number of samples needed to advance past the end of frame # to the beginning of the next self.next_frame_adv = max([0, self.adv_N - self.len_N]) self.current_sample = 0 # initial position self.repositioned = True # Let iterator know that this will be first frame def init_scipywavfile(self): "init_scipywavfile() - initialize scientific python wavfile interface" # get data as memory mapped file [self.Fs, self.data] = scipy.io.wavfile.read(self.filename, mmap=True) self.samplesN = self.data.shape[0] self.channels = 1 if len(self.data.shape) == 1 else self.data.shape[1] self.format = "WAV" def get_framelen_samples(self): "get_framelen_ms - Return frame length in samples" return self.len_N def get_framelen_ms(self): "get_framelen_ms - Return frame length in ms" return self.len_ms def get_frameadv_samples(self): "get_frameadv_ms - Return frame advance in samples" return self.adv_N def get_frameadv_ms(self): "get_frameadv_ms - Return frame advance in ms" return self.adv_ms def get_Fs(self): "get_Fs() - Return sample rate" return self.Fs def __len__(self): "len() - number of frames" # Number of frames computation remainingN = self.samplesN - self.offset # account for possible non-zero start # if self.incomplete: if self.interface == "SoundFileStream": # Returns all possible frames return math.ceil((remainingN-1)/ self.adv_N) else: return math.floor(remainingN / self.adv_N) else: # complete frames # length of frame - advance can be subtracted from sample count and divided by # advance return math.floor((remainingN - (self.len_N - self.adv_N)) / self.adv_N); def get_Nyquist(self): return self.Fs/2.0 def get_params(self): "Return dict with file parameters" params = { "filename" : self.filename, "Fs" : self.Fs, "samples" : self.samplesN, "framing" : {"adv_ms" : self.adv_ms, "len_ms" : self.len_ms, "adv_N":self.adv_N, "len_N" : self.len_N}, "format" : self.format } if self.use_soundfile: params["subtype"] = self.subtype return params def shape(self): "shape() - shape of tensor generated by iterator" return np.asarray([self.len_N, 1]) def size(self): "size() - number of elements in tensor generated by iterator" return np.asarray(np.product(self.shape())) def __iter__(self): """"iter() - Return a frame iterator WARNING: Multiple iterators on same soundfile are not guaranteed to work as expected""" # Differs from assignment as this is derived from a reader # that supports multiple file interfaces # (Works same way though - no change in API) return scipywavefile_it(self) def seek_sample(self, N): "seek_sample(N) - Next iterator will start with sample N" if N > self.samplesN: raise ValueError("File %s seek to sample {}: past end of file {}"%( self.filename, N, self.samplesN)) else: # memory mapped, just set counter self.current_sample = N def get_data(self, startidx, N): """get_data(startidx, N) - Retrieve N samples starting at startidx. This has no side effects, the file position of iterators is unchanged """ if startidx > len(self): raise ValueError("Read past end of data") stopidx = min(startidx+N, len(self)) if self.channels > 1: data = self.data[startidx:stopidx,:] else: data = self.data[startidx:stopidx] return data def seek_frame(self, N): "seek_frame(N) - Next read will start at frame N" raise NotImplemented() class scipywavefile_it(object): def __init__(self, frameobj): self.frameobj = frameobj self.start_sample = frameobj.current_sample self.current_sample = self.start_sample def __iter__(self): return self def __next__(self): "next() - Return next frame, offset (s), and absolute time (if available, otherwise None" # setup frame indices start = self.current_sample stop = start + self.frameobj.len_N # check for valid frame indices # start past end check if start >= self.frameobj.samplesN: raise StopIteration # past end check if stop > self.frameobj.samplesN: # frame runs off end if self.frameobj.incomplete: # User wants the incomplete frame. Set stop to last # sample and set current_sample to trigger StopIteration # on next call stop = self.frameobj.samplesN self.current_sample = self.frameobj.samplesN else: raise StopIteration else: self.current_sample = start + self.frameobj.adv_N if self.frameobj.channels > 1: frames = self.frameobj.data[start:stop,:] else: frames = self.frameobj.data[start:stop] # frame data, frame offset (s) from start, frame start time return frames