Engine: Thebangs

Thebangs (*)

one-shot synthesis engine.

(oddly, this appears to be my first official contribution to the llllllllibrary.)

this is a norns engine which aims to extend the classic PolyPerc patch by @tehn.

in addition to the engine, the project provides awake_bangs.lua, a small modification of the classic startup script; it simply swaps PolyPerc for the new engine and adds a few parameters.

the engine adds two major features compared to its ancestor:

  • the ability to select among multiple synthesis algorithms on the fly.

  • control over polyphony, with multiple heuristics for voice stealing. (extremely fast sequences are now possible without stalls or dropouts.)


Instructions

install via maiden: ;install https://github.com/catfact/thebangs.git


while i hope that people enjoy the sounds it makes, this project is also a demonstration and proof-of-concept, intended to aid further development:

  • the possibilities for additional synth algos are limitless, and this small selection is quick and arbitrary; please feel free to PR some new ones. (i expect i will add a few now and then.)

  • the voicing logic is broken out into a generic utility class called OneShotVoicer. perhaps it would be helpful to resurrect the practice of keeping some utilties like this in norns/sc so they are available to all engines.

  • i also hope it is helpful for exploring a few intermediate/advanced concepts in SuperCollider:

    • combining a “wrapper” SynthDef (in Thebangs, which manages envelopes and panning) with a “sub-graph factory” (the Bangs class, whose methods generate the oscillator signals.)
    • a dash of meta-programming (inspecting the Bang methods.)
    • using NodeWatcher to monitor Synth lifecycles
    • custom setter methods in classes
    • &c… (feel free to ask if i can explain any methods/decisions.)

history and motivation

  • the ability to select different algorithms comes in response to this series of posts. (thanks @infinitedigits, and @license / @tyleretters who first executed a similar idea on norns with Dronecaster. ) the obvious issue arises: the interface of PolyPerc is reused, but the parameters “pw” and “gain” are no longer accurately-named, and their scaling is arbitrary. generic names might make it more obvious… or maybe there is a middle way.

  • engine-side management of self-freeing voice allocation for one-shot polyphony comes in response to this discussion. (thanks @distropolis, and i hope this is useful to you.)

roadmap:

  • add more algos
  • normalize parameter ranges for mod, keep polyperc ranges for aliases only
  • clean up and add another script or two
  • find bugs

Download

v0.0.3

github repo


(*) with apologies to maggie vail

72 Likes

I’m already excited to see what clever use will be made of it. Thanks a lot @zebra for your “first contribution” ^^

This looks great, looking forward to try it out.
A quick note on the awake_bangs script - halfsecond does not exist in Thebangs’ lib. Easy fix by changing the include path to awake/lib/halfsecond or perhaps instead it would be worth including halfsecond in the lib.

1 Like

ah thanks, i forgot to commit it. updated

This is amazing! Thank you for tackling this.

It may take me a bit of experimenting to figure out how to implement this in my script “pixels”. If I understand correctly, I’ll need to drop everything in the /lib folder into my project (other than the Awake specific “half second.” Then set the new TheBangs specific parameters.

I’ll let you know how it goes!

that’s about right. if your script is currently using PolyPerc, i believe no changes should be needed except copying the .sc files into lib and changing engine.name:

  • the new engine has aliases from pw to mod1 and so on, so all PolyPerc commands will work
  • algo selection defaults to square, which is the polyperc sound
  • the only automatic change is voice stealing. behavior defaults to 32 voices with FIFO (round robin)

IMPORTANT: actually - sorry - if you copy the .sc files you must also change the class names. (and since there are several classes which use each other, this is not quite trivial.)

the alternative is to require Thebangs to be installed as a dependcy, and don’t copy any .sc files. any duplicate supercollider class name in the SC search path, will cause SC to fail on boot. (it’s the classname not the filename which matters, although the onscreen warning about “duplicate engines” assumes that they are congruent and just checks for duplicate filenames.)

i know this is an annoying limitation, but alas we have no reasonable workaround. i think specifying dependencies is probably the best way for multiple .sc components like this. (it’s another good reason to put utility classes like OneShotVoicer in some more central location.)

I dropped the .sc files into my script and they worked without error before I read your update above. Will the class names become an issue if there are multiple scripts with the classes in different /lib folders? As in, someone has your updated Awake script and my script on the same device?

yes exactly. sorry to say. it is an issue with how supercollider works. we have not found a workaround short of forking SC class compiler code, or completely restarting SC process between script loads.

this is utterly brilliant, thank you @zebra! making supercollider polyphonic has been a stumbling block for me several times now - i’ve made several scripts that would directly benefit from this.

so many cool tricks i learned just by reading the code! the use of NodeWatcher, the voice-stealing implementation. thanks for all the commenting, supercollider is hard to read but here it was so straightforward!

there’s one little thing i’m stuck with:

would you be so kind as to explain what .perform() refers to and also why the “_” is appended before converting to a symbol? this is a really cool way of folding up those functions.

I was able to rename the classes thebangs.sc and OneshotVoicer.sc and retain functionality … but when I try to rename bangs.sc and the corresponding callback in thebangs.sc on line

	bangs = Bangs.class.methods.collect({|m| m.name});

the script boots, but no longer produces sound. I’ve slept the machine after each incremental change and changing that class name seems to break everything for me.

Maybe that one isn’t a class name that needs to be changed or maybe there is a reference to it I missed somewhere?

EDIT:

WHOOPS! I found another Bangs hiding out in thebangs.sc I needed to change.

All good.

this is a norns engine which aims to extend the classic PolyPerc patch

“classic” lol

PolyPerc was my first attempt at supercollider, i apologize that it has become an unsophisticated yet canonical point of entry

10 Likes

seriously - unsophisticated is exactly what it needs to be.

would you be so kind as to explain what .perform() refers to and also why the “_” is appended before converting to a symbol? this is a really cool way of folding up those functions.

this is probably the hackiest thing in the pile. two parts:

(1) perform is a method of Object (the base class of all things.) it accepts a symbol, and if the receiver has a method with the same name as the symbol, that method is invoked with the remaining arguments passed along. relevant helpfile.

(2) when you set the value of a member variable, you are actually invoking a method with that variable’s name followed by an underscore. a default version of this method is generated automatically when you add > to the variable declaration in the class, but setters can also be customized. (like here.)

[not shown: same applies to getters, which are methods with the variable’s name alone - no underscore - and the declaration shortcut is <.]

relevant helpfile.

so those lines are just an inscrutable way of saying, “for each of these strings, add a command that calls the setter method for a variable with the same name as the string, on this object.” (maybe there is even a better way of doing this, but that is what i came up with.)

[aside: the difference between strings (mutable) and symbols (iummutable) is relevant here. it’s one of many places in SC where the string must be “made immutable” with .asSymbol.]

7 Likes

This is great. It’s awesome to see the work we did in Dronecaster grow into an extended exploration of how to make engines more flexible.

2 Likes

I was wondering if there was any correlation to maggie and sarah’s band. i lived in olympia and knew them when they were first getting started with that band (and going through a lot of drummers).

1 Like

yeah all the dronecaster sc props go to @license!!

really beautiful work on this @zebra.

2 Likes

Just a quick update …

My script pixels is up on the library and this is working beautifully for me. Thank you!

I’m working on adding a way for users to swap engines from inside the app … but I’ve noticed that both exp and lin klang engines are not making any sound. Also, the sinFMLP doesn’t seem to change much when scrolling mod1. I do not currently have a way to scroll mod2 from my app, so there may just need to be some interplay there. Not quite sure.

those sounds work for me when editing the parameters in awake_bangs. lmk if that doesn’t work for you. if it’s only in your script, maybe i can help dbg at a later time. (but ATM i have no time)

Plugging a dummy value (100) in for engine.mod2 makes those engines come alive. It seems as though if the mod2 parameter is not specifically initialized with a value, those klang engines make no sound. Perhaps your version of Awake always sends a value out, whereas other scripts may not, causing a possible issue?

I’m just reporting my findings here, no need for debugging.

Thanks so much for this.

ah i see - you’re right. values of zero for mod2 will be silent on those. (and zero is the default.) any nonzero should be audible. will fix

thanks

I’m still working my way through implementing all of the exposed parameters from your engine into my script “pixels”. I seem to have come across a bug or at least an inconsistent behaviour.

The engines reznoise, klangexo and klanglin do not respond to engine.pan. The other engines seem to respond just fine, but I’m getting some strange stuck results with these. Reznoise seems to respond differently to each left and right field as far as frequency response to mod1, mod2 and hz2 go. I’m actually experiencing some weirdness across the stereo field in the klang engines as well.

I’m looking into it my self and trying to learn supercollider. Just wanted to let you know.