def test_tap_activation(): a = Ring(16) t = a.create_tap() t.deactivate() assert t.name in a.inactive_taps assert t.name not in a.active_taps a.append(np.arange(15)) a.append([99]) t.activate() assert t.name in a.active_taps assert t.name not in a.inactive_taps s = t.get_samples(1) assert s[0] == 99
class Stretcher(object): """ Given a tap pointer in a Ring buffer, generate the stretched audio """ def __init__(self, tap): """ tap (RingPosition): the starting point where our stretch begins """ self.__in_tap = tap self.__buffer = Ring(2**16) self.__fading_out = False def step(self, windowsize, *args, **kwargs): results = self.stretch(windowsize, *args, **kwargs) return results def stretch(self, windowsize, stretch_amount=4): """ Run paulstretch once from the current location of the tap point """ sw = get_strech(windowsize) audio_in = self.__in_tap.get_samples(sw.size) # Magnitude spectrum of windowed samples mX = np.abs(fft.rfft(audio_in * sw.window)) # Randomise the phases for each bin between 0 and 2pi pX = np.random.uniform(0, 2 * np.pi, len(mX)) * 1j # use e^x to Convert our array of random values from 0 to 2pi to an # array of cartesian style real+imag vales distributed around the unit # circle. Then multiply with magnitude spectrum to rotate the magnitude # spectrum around the circle. freq = mX * np.exp(pX) # Get the audio samples with randomized phase. When we randomized the # phase, we changed the waveform so it no longer starts and ends at # zero. We will need to apply another window -- however do not know the # size of the next window, so instead of applying the full window to # our audio samples, we will close the window from the previous step, # and open the window on our current samples. audio_phased = fft.irfft(freq) # counter the tremelo for both halves of the audio snippet audio_phased *= sw.double_hinv_buf # Open the window to the newly generated audio sample audio_phased *= sw.open_window # Next we will do the overlap/add with the tail of our local buffer. # First, retrive the the samples, apply the closing window previous = self.__buffer.recent(sw.half) * sw.close_window # overlap add this the newly generated audio with the closing tail of # the previous signal audio_phased[:sw.half] += previous # replace the tail end of the output buffer with the new signal self.__buffer.rewind(sw.half) self.__buffer.append(audio_phased) # The last <sw.half> samples are not valid (the window has not yet # been closed). These will be closed the next time we call step. # Advance our input tap self.__in_tap.advance(sw.hopsize(stretch_amount)) # append the audio output to our output buffer self.__buffer.append(audio_phased) return audio_phased[:sw.half] def fade_out(self): """Begin fading the stretch with each .step() .step should deactivate Caution: fade_out is currently implemeted in StretchGroup. See: https://github.com/CharlesHolbrow/realtime-fft-experiment/issues/4 """ self.__fading_out = True def activate(self): self.__fading_out = False self.tap.activate() def deactivate(self): self.clear() self.tap.deactivate() @property def fading_out(self): return self.__fading_out @fading_out.setter def fading_out(self, val): self.__fading_out = bool(val) @property def tap(self): return self.__in_tap def clear(self): self.__buffer.raw.fill(0.)
def test(): a = Ring(4) a.append([1, 2]) assert np.all(a.raw == [1, 2, 0, 0]) a.append([3, 4]) assert np.all(a.raw == [1, 2, 3, 4]) a.append([5]) assert np.all(a.raw == [5, 2, 3, 4]) a.append([6, 7]) a.append([8, 9]) assert np.all(a.raw == [9, 6, 7, 8]) a.append([10, 11]) assert np.all(a.raw == [9, 10, 11, 8]) # test getitem a = Ring(4) a.append([0, 1, 2, 3]) assert a[0] == 3 assert a[-1] == 2 a.append([4]) assert (np.all(a.raw == [4, 1, 2, 3])) assert (a[0] == 4) assert (a[-1] == 3) assert (np.all(a.recent(0) == [])) assert (np.all(a.recent(1) == [4])) a.append([5]) assert (np.all(a.recent(2) == [4, 5])) # test wrap around assert (np.all(a.recent(3) == [3, 4, 5])) # test RingPosition a = Ring(8) p = a.create_tap() assert p.valid_buffer_length == 1 a.append(np.arange(4)) assert p.valid_buffer_length == 5 p.advance(1) assert p.valid_buffer_length == 4 assert p.index == 0 assert np.all(p.get_samples(4) == [0, 1, 2, 3]) p.advance(2) assert np.all(p.get_samples(2) == [2, 3]) a.append([4, 5, 6, 7, 8]) p.advance(4) assert np.all(p.get_samples(2) == [6, 7]) # test wraping assert np.all(p.get_samples(3) == [6, 7, 8]) p.advance(2) assert (p.valid) assert (a.raw[p.index] == 8) # Ensure that we throw when breaking a ring pointer a = Ring(8) a.append(np.arange(8)) p = a.create_tap() assert (p.get_samples(1)[0] == 7) # pointing to the last item in the sample a.append(np.arange(7)) try: a.append([99]) except RingPointerWarning as e: pass assert p.valid is False a = Ring(8) p = a.create_tap() try: p.advance(2) except RingPointerWarning as e: pass assert p.valid is False return a, p # test rewinding pointer a = Ring(5) a.append(np.arange(2)) assert a.pointer == 2 a.rewind(1) assert a.p == 1 a.rewind(3) assert a.p == 3