def main(): """ main function """ sgen = sig.SigGen() waves = [dsp.tube(sgen.sin(), x) for x in range(1, 17)] wtab = wavetable.WaveTable(waves) visualize.plot_wavetable(wtab, title='tube') waves = [dsp.clip(sgen.sin(), x) for x in range(1, 17)] wtab = wavetable.WaveTable(waves) visualize.plot_wavetable(wtab, title='clip') waves = [dsp.fold(sgen.sin(), x) for x in range(1, 17)] wtab = wavetable.WaveTable(waves) visualize.plot_wavetable(wtab, title='fold') waves = [dsp.shape(sgen.sin(), x) for x in range(1, 17)] wtab = wavetable.WaveTable(waves) visualize.plot_wavetable(wtab, title='shape') waves = [dsp.slew(sgen.sqr(), 1 / x) for x in range(1, 9)] waves += [dsp.slew(sgen.sqr(), 1 / x) for x in range(-8, 0)] wtab = wavetable.WaveTable(waves) visualize.plot_wavetable(wtab, title='slew') waves = [dsp.downsample(sgen.sin(), x) for x in range(1, 17)] wtab = wavetable.WaveTable(waves) visualize.plot_wavetable(wtab, title='downsample') waves = [dsp.quantize(sgen.sin(), x) for x in range(1, 17)] wtab = wavetable.WaveTable(waves) visualize.plot_wavetable(wtab, title='quantize')
def test_resynthesize(): """ test resynthesize """ a = np.array([-1.0, 1.0, 1.0, -1.0]) e = a a = np.tile(a, 1024) s = sig.SigGen() s.num_points = 4 o = dsp.resynthesize(a, s) assert np.allclose(o, e)
def from_wav(self, filename, sig_gen=None, resynthesize=False): """ Populate the wavetable from a wav file by filling all slots with cycles from a wav file. @param filename str : Wav file name. @param sig_gen SigGen : SigGen to use for regenerating the signal. @param resynthesize : If True, the signal is resynthesised using the harmonic series of the original signal - works best on signals with a low findamental frequency (< 200 Hz). If False, n evenly spaced single cycles are extracted from the input (default False). @returns WaveTable : self, populated by content from the wav file settings as this one """ data, fs = wavfile.read(filename, with_sample_rate=True) if sig_gen is None: sig_gen = sig.SigGen() if resynthesize: num_sections = self.num_waves while True: data = data[:data.size - (data.size % num_sections)] sections = np.split(data, num_sections) try: self.waves = [ dsp.resynthesize(s, sig_gen) for s in sections ] break except dsp.NotEnoughSamplesError as exc: num_sections -= 1 if num_sections <= 0: raise exc if num_sections < self.num_waves: self.waves = sig.morph(self.waves, self.num_waves) else: cycles = dsp.slice_cycles(data, self.num_waves, fs) self.waves = [sig_gen.arb(c) for c in cycles] return self
def main(): """ main """ # create a signal generator sig_gen = sig.SigGen() # create a wave table to store the waves zwt = wavetable.WaveTable(num_waves=16) # example 1: generate and save a simple saw wave # generate a saw using our signal generator and store it in a new Wave # object. saw_wave = sig_gen.saw() # put the saw wave into our wave table. zwt.waves = [saw_wave] # as we're only adding one wave to the wave table, only the first slot of # the resulting oscillator in zebra will contain the saw. the remaining # slots will be empty, because we haven't added anything to those yet. render(zwt, 'osc_gen_saw') # you could fill all 16 slots with the same saw, by repeating it 16 times zwt.waves = [saw_wave for _ in range(16)] render(zwt, 'osc_gen_saw_16') # example 2: morphing between two waveforms we can use up all 16 slots in # the zebra oscillator, even with fewer than 16 starting waveforms, if we # use morph() to morph from one waveform to the other, to fill in the # in-between slots. # morph from sine to triangle over 16 slots zwt.waves = sig.morph((sig_gen.sin(), sig_gen.tri()), 16) render(zwt, 'osc_gen_sin_tri') # of course, we don't have to use all 16 slots. we could use only the first # 5, for example. # morph from sine to triangle over 5 slots zwt.waves = sig.morph((sig_gen.sin(), sig_gen.tri()), 5) render(zwt, 'osc_gen_sin_tri_5') # example 3: morphing between many waveforms # it is possible to morph between any number of waveforms, to produce # interpolated waves between the given waves. # morph between sine, triangle, saw and square over 16 slots zwt.waves = sig.morph( (sig_gen.sin(), sig_gen.tri(), sig_gen.saw(), sig_gen.sqr()), 16) render(zwt, 'osc_gen_sin_tri_saw_sqr') # example 4: generting arbitrary waves # a custom signal can be used as an oscillator. # in this example, one slot is filled with random data, but any data, # generated or, say, read in from a wav file, can be used. # the custom signal generator function automatically normaises and scales # any data you throw at it to the right ranges, which is useful. zwt.waves = [ sig_gen.arb(np.random.uniform(low=-1, high=1, size=128)) for _ in range(16) ] render(zwt, 'osc_gen_random') # example 5: pulse-width modulation # SigGen has a pulse wave generator too. # let's use that to make a pwm wavetable. # pulse widths are between 0 and 1 (0 to 100%). 0 and 1 are silent as the # pulse is a flat line. so, we want to have 16 different, equally spaced # pulse widths, increasing in duration, but also avoid any silence: pws = (i / 17. for i in range(1, 17)) # generate the 16 pulse waves zwt.waves = [sig_gen.pls(p) for p in pws] render(zwt, 'osc_gen_pwm') # example 6: processing wave forms # the dsp module can be used to process waves in various ways # let's try downsampling a sine downsampled = dsp.downsample(sig_gen.sin(), 16) # that downsampled sine from probably sounds pretty edgy # let's try that again with some slew this time, to smooth it out a bit slewed = dsp.slew(dsp.downsample(sig_gen.sin(), 16), 0.8) # generate a triangle wave and quantize (bit crush) it quantized = dsp.quantize(sig_gen.tri(), 3) # applying inverse slew, or overshoot, to a square wave slewed_square = dsp.slew(sig_gen.sqr(), 0.8, inv=True) # overshoot might make the wave quieter, so let's normalize it dsp.normalize(slewed_square) # morph between the waves over 16 slots zwt.waves = sig.morph((downsampled, slewed, quantized, slewed_square), 16) render(zwt, 'osc_gen_dsp') # example 7: longer wavetables, more processing and writing a wav file # wavetables can have any number of slots, this one has 120 slots lwt = wavetable.WaveTable(num_waves=120) # similarly, a signal generator can generate any number of samples # a waveform coresponding to the frequency of C3 at 44.1 kHz would # have approx. 337 samples. mc_sig_gen = sig.SigGen() mc_sig_gen.num_points = 337 # create ever-decreasing wave folding distortion over the wavetable lwt.waves = [ dsp.fold(mc_sig_gen.sin(), (lwt.num_waves - i) / 50.) for i in range(lwt.num_waves) ] wavfile.write_wavetable(lwt, os.path.join(make_osc_path(), 'folding.wav')) # create ever-increasing wave shaping distortion over the wavetable lwt.waves = [ dsp.shape(mc_sig_gen.sin(), power=i + 1) for i in range(lwt.num_waves) ] wavfile.write_wavetable(lwt, os.path.join(make_osc_path(), 'shaping.wav'))
from osc_gen import wavetable, dsp, sig, visualize import numpy as np from scipy import fftpack from scipy.io.wavfile import write import matplotlib.pyplot as plt # Create a signal generator. sample_length = 2048 sg = sig.SigGen(num_points=sample_length) Fs = 44100 # Create a wave table with 1024 slots to store the waves. wt = wavetable.WaveTable(1, wave_len=1024) #m = sg.saw() m = sg.tri() wt.waves = [m] #visualize.plot_wavetable(wt) timestep = 1.0 / Fs rFFT_values = fftpack.rfft(m) freq = fftpack.rfftfreq(rFFT_values.size, d=timestep) # Make array uniform with C input data X = rFFT_values X = np.insert(X, 0, 0) freq = np.insert(freq, 0, 0) X = np.delete(X, -1) freq = np.delete(freq, -1) outfile = "./waves/tri_%d.cgw" % sample_length