Permalink
Browse files

Many bugfixes and various features, most listed in changelog

  • Loading branch information...
hecanjog committed Apr 28, 2018
1 parent c6713cf commit b21905362b829a3cdec26739ffa1812e2beb5ab5
Showing with 19,347 additions and 10,008 deletions.
  1. +26 −3 README.md
  2. +1,506 −1,152 pippi/dsp.c
  3. +28 −14 pippi/dsp.pyx
  4. +2,360 −1,070 pippi/grains.c
  5. +3 −0 pippi/grains.pxd
  6. +16 −1 pippi/grains.pyx
  7. +1,387 −1,057 pippi/oscs.c
  8. +50 −24 pippi/oscs.pyx
  9. +75 −36 pippi/rhythm.py
  10. +21 −2 pippi/soundbuffer.c
  11. +13,483 −6,584 pippi/wavetables.c
  12. +3 −0 pippi/wavetables.pxd
  13. +293 −17 pippi/wavetables.pyx
  14. +1 −1 requirements.txt
  15. +29 −1 tests/test_oscs.py
  16. +15 −46 tests/test_rhythm.py
  17. +51 −0 tests/test_wavetables.py
@@ -89,7 +89,7 @@ There are more examples, give em a whirl, and try your own.
Final (probably) feature releases / big api changes before going to beta.
- Breaking API changes:
- Breaking changes:
- Durations for most APIs are now given in seconds (floats) rather than
integer frames. `len(SoundBuffer)` still returns a length in frames per
python convention, and slicing into a `SoundBuffer` is also still done by frame
@@ -101,13 +101,36 @@ Final (probably) feature releases / big api changes before going to beta.
The wavetypes available are `SINE`, `COS`, `TRI`, `SAW` (which is also aliased to
`PHASOR`), `RSAW` (reverse sawtooth), `HANN`, `HAMM`, `BLACK` or `BLACKMAN`,
`BART` or `BARTLETT`, `KAISER`, `SQUARE`, and the `RND` flag to select one at random.
- `Osc` changes:
- Added 2d wavetable synthesis (similar to max/msp `2d.wave~`) to `Osc` plus example script
- To create a 2d `Osc`, use the `stack` keyword arg on initialization: `Osc(stack=[dsp.RND, [0,1], dsp.SINE], lfo=dsp.SINE)`
- `Osc` wavetables may be:
- an int flag for standard wavetables (dsp.SINE, dsp.TRI, etc)
- a python list of floats
- a wavetable instance
- a soundbuffer
- 2d wavetable stacks are a python list of any combination of the above.
- The same types are acceptable for:
- `wavetable` (the basic waveform)
- `window` (an optional window to apply to the waveform wavetable - useful for eg pulsar synthesis)
- `mod` (the frequency modulation wavetable)
- and `lfo` (the 2d modulation wavetable)
- `SoundBuffer` changes:
- Added `remix` for remixing a soundbuffer from N channels to N channels.
- Panning algorithms operate on arbitrary numbers of channels (but use same algorithms applied to odd & even numbered channels instead of left & right)
- Return a reversed copy of a soundbuffer with `sound.reversed()` or reverse in place with `sound.reverse()`
- New ADSR envelopes with `sound.adsr(a=1, d=1, s=0.5, r=1)`
- Generate a `GrainCloud` from a `SoundBuffer` with `sound.cloud()`
- Clip samples to min/max with `sound.clip(minval=-1, maxval=1)`
- Taper ends of sounds (linear fade-in, fade-out) with `sound.taper(length)`
- ADSR wavetable generator with `wavetables.adsr(a=100, d=100, s=0.5, r=100, 1024)`
- New `Wavetable` type for `SoundBuffer`-like operator-overloaded wavetable manipulation & composition
- New `GrainCloud` wavetable-driven granulator. See the `examples/swarmy_graincloud.py` example for more.
- `GrainCloud`-driven pitch shift without time change (`sound.transpose(speed)`)
and time stretch without pitch shift (`sound.stretch(length)`) methods for `SoundBuffer`.
- `dsp.cloud(SoundBuffer, *args, **kwargs)` shortcut for `GrainCloud` creation.
- Added 2d wavetable synthesis (similar to max/msp `2d.wave~`) to `Osc` plus example script
- Read wavetables from 1 channel sound files with `wavetables.fromfile`
- Added a helper for async rendering with `multiprocessing.Pool`
- Added a simple helper for async rendering with `multiprocessing.Pool`
- `SoundBuffer`s can now be pickled (enables passing them between processes)
- `SoundBuffer` can be initialized (and spread across channels) from a normal python list
2,658 pippi/dsp.c

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -49,6 +49,9 @@ def mix(sounds):
out &= sound
return out
cpdef wts.Wavetable wt(list values=None, int initwt=-1, int initwin=-1, int length=-1):
return wts.Wavetable(values, initwt, initwin, length)
cpdef SoundBuffer stack(list sounds):
cdef int channels = 0
@@ -68,25 +71,37 @@ cpdef SoundBuffer stack(list sounds):
cdef int current_sound_index = 0
cdef int source_channel_to_copy = 0
# TODO
while copied_channels < channels:
out.frames.base[:,channel_to_copy] = sounds[current_sound_index].frames.base[:,source_channel_to_copy]
copied_channels += 1
return out
def join(sounds, channels=2, samplerate=44100):
def join(sounds, overlap=None, channels=None, samplerate=None):
""" Concatenate a list of sounds into a new sound
"""
out = SoundBuffer(length=1, channels=channels, samplerate=samplerate)
channels = channels or sounds[0].channels
samplerate = samplerate or sounds[0].samplerate
cdef double total_length = 0
cdef SoundBuffer sound
for sound in sounds:
out += sound
total_length += sound.dur
if overlap is not None:
total_length -= overlap
return out
out = SoundBuffer(length=total_length, channels=channels, samplerate=samplerate)
def silence(length=-1, channels=2, samplerate=44100):
""" Create a buffer of silence of a given length
"""
return SoundBuffer(length=length, channels=channels, samplerate=samplerate)
pos = 0
for sound in sounds:
out.dub(sound, pos)
pos += sound.dur
if overlap is not None:
pos -= overlap
return out
def buffer(frames=None, length=-1, channels=2, samplerate=44100):
""" Identical to `silence` -- creates an empty buffer of a given length
@@ -118,13 +133,12 @@ def find(pattern, channels=2, samplerate=44100):
for filename in glob.iglob(pattern, recursive=True):
yield SoundBuffer(filename, channels=channels, samplerate=samplerate)
def cloud(snd, *args, **kwargs):
return grains.GrainCloud(snd, *args, **kwargs)
def pool(callback, params, processes=4):
def pool(callback, reps=10, params=None, processes=4):
out = []
if params is None:
params = [None]
with mp.Pool(processes=processes) as process_pool:
for result in [ process_pool.apply_async(callback, p) for p in params ]:
for result in [ process_pool.apply_async(callback, params[i % len(params)]) for i in range(reps) ]:
out += [ result.get() ]
return out
Oops, something went wrong.

0 comments on commit b219053

Please sign in to comment.