• getting started with aleph

    by oddlogic

    January 25th, 2020

    I've been meaning to write an approachable guide to getting started with aleph for ages, but I guess later is better than never! If you're not sure what this is about, I'd encourage you to check out my introductory post about aleph.

    step one: download aleph

    Head on over to the aleph downloads page and grab the version for your OS. It will download an archive (.zip for mac/win, .tar for linux). Extract the contents of the folder wherever you want to keep aleph - that's it! aleph is "portable", meaning it does not require installation. Just double click the aleph executable to start the app.

    step two: set up your audio routing

    In order for aleph to visualize sound - it has to be able to "hear"! We need some way to pipe sounds into aleph. There are two approaches to routing audio into aleph:

    Method 1: Use a software loopback

    This approach uses third-party software to create synthetic audio routes which will allow you to funnel in sounds from your operating system (i.e. any sound playing through your computer). There are many software tools like this available, but to be honest I don't have enough experience with any of them to be in a position to offer authoritative suggestions. The good news is fundamentally these programs do the same thing - create a virtual "pipe" to route audio sources.

    The only cross-platform one I know of is JACK, so Linux users should look to that, but it's a bit more complex than what we need, so for the sake of simplicity I'm going to roll with a program a friend recently put me onto called VB-CABLE because it's very easy to configure. Caveat - it's only available for Windows. MacOS users - look into BlackHole.

    So after downloading VB-CABLE, extract the contents of the .zip and run the installer in administrator mode (right click -> 'run as administrator'), and reboot your machine.

    Once we have VB-CABLE installed, we need to select it as our audio device. The easiest way to do this is just to hit the Windows key and search for "change system sounds".

    Once you open the utility, find the CABLE device in your list of audio devices and click "Set Default".

    little green arrow = all good

    If you were listening to audio at this stage you might have noticed something - where the hell did my audio go? Don't worry, we'll fix that in the next step. Click on the "Recording" tab on the same Sound utility. Select the CABLE device and click "Set Default" as we did before. Now click on the "Properties" button and we'll configure the output device so we can hear again. Make sure the "Listen to this Device" checkbox is ticked, and then select your regular output device (audio interface, built-in speakers, etc) in the list under "Playback through this device". Click apply and you should be able to hear again.

    Alright - home stretch. Now open aleph and select the CABLE device under "Audio Device Selection".

    All done!

    Method 2: Use a hardware loopback

    For this approach, you must have an audio interface with at least two sets of stereo outputs and one set of stereo inputs. I think you will also need one that supports monitoring so you can hear the output in a manner that doesn't introduce a dreaded feedback loop. I have a Native Instruments Komplete Audio 6 which works great for this - I'd imagine any comparable device will work just fine.

    So let's call the output that leads to your speakers Output A. The secondary output will be Output B, and we'll just call the input Input (note these are all just placeholder names - the real ones depend on your audio device).

    Grab a pair of stereo cables and run them from Output B to Input. Output A should be connected with your speakers as they normally would be.

    In your OS, set the default output to Output B. Set the default input to Input. Finally, enable monitoring of the Input on your audio interface (this might be a physical switch or something controlled by software depending on the interface).

    Lastly, inside aleph, select Input under "Audio Device Selection" - all done!

    step three: video output

    Now, to make sure everything is working correctly before proceeding, let's try a quick test.

    Make sure you are playing some audio. This could be an MP3 file, a youtube video, whatever. With aleph open, click the "Create Display" button. A second window should appear. But it's just black - oh no!

    That's okay, it's only because we haven't selected a "sketch" to play. "Sketches" are what we call individual programs that aleph runs to generate display output. This nomenclature is borrowed from the p5.js library that aleph heavily makes use of (which in turn comes from p5's grandpappy, the programming language Processing).

    Alright, so back to the matter at hand - select one of the built-in example sketches under "Sketch Selection". Let's try the "spectrum" example. By the end of this article we will build our own spectrum sketch, so let's keep with the theme. If everything is working, you should be seeing something like this:

    cool. that color is ugly, but cool.

    step four (optional): connect a MIDI controller

    Within aleph you can use any number of MIDI controllers to control some of the controls on the editor window (the main window w/ all the UI controls and stuff) as well as arbitrary controls you can configure to manipulate custom properties within your sketches.

    When you load aleph, it will scan your computer for connected MIDI devices and list them out under the "MIDI Device Selection" section. First select your chosen device. Next, we will create some mappings.

    For now we will just control some of the existing UI elements. Under the "Audio Settings" section, click the "map" button under the "bass" knob. Now, turn a knob or move a fader on your midi controller. You should now be able to control the knob with your midi controller, and you should see the knob reacting in the UI.

    If you're still running the "spectrum" example, notice how turning the bass knob changes the color of the lines - this is because the coloration is based on the frequency content (the amount of red is equal to the amount of bass). So by adjusting the bass sensitivity, we have effectively changed the color. Neat.

    As far as controls in the UI you can control with midi, you can control the following things:

    -all the audio controls

    -changing which sketch is being displayed

    -changing which displays sketches should be displayed on

    And in addition you can create any number of custom controls which you can use to manipulate properties in your sketches to add variation and performance opportunities. To learn more about this, refer to the "custom MIDI controls" section of the documentation. But look into that later - let's keep it moving.

    step five: make your first sketch

    Alright - let's get into some code. We'll start very simple and then move up to building our own version of the "spectrum" example from earlier - but we'll make it even better by adding midi controls!

    Before we get coding, a quick but important note: when you open a sketch in aleph then make changes to it and want to see those changes reflected, you must refresh the window the sketch is being displayed in by pressing CTRL+R (CMD+D on macs) or F5 and the re-select the sketch in the editor window. I am looking into adding a "hot-reloading" feature in a future update, but for now we must manually refresh to see updates to our code. Alright, with that out of the way, let's move on.

    Aleph comes with (very simple) pre-built templates for 2D and 3D sketches. For now we'll limit ourselves to the simpler 2D world.

    To create a new sketch, click the "Create 2D Sketch" button under "Sketch Selection". This will bring up a file save dialog where you can name your sketch. aleph will automatically save the file as a javascript file so you can just enter the name itself, sans file extension. Let's call our sketch "betterSpectrum".

    The file will be saved as "betterSpectrum.js" - but where? Relative to the "root" folder (where aleph's executable is located), your sketches live in resources/app/aleph_modules/sketches. We need to know this because we need to open the javascript file in a text editor. There are many great text editors available - look into VSCode, Sublime Text, or Atom if you don't have a proper text editor on your machine (you will want something more than notepad, etc).

    So anyway, open up our shiny new aleph sketch in your text editor of choice. Again it should live at ./resources/app/aleph_modules/sketches/betterSpectrum.js.

    It should look like this:

    function setup() {
      // code you only want to run ONCE
    }
    
    function draw() {
      // code you want to run every frame
    }
    
    exports.run = () => {
      utils.runOnce(state[path.basename(__filename)], setup);
      draw();
    };

    Right now we're only concerned with the second block of code with the draw function in it. This is used to run some code on every frame, which we want to do most of the time. The setup function is just for, well, setup. Sometimes you need to do something once right when the program starts, but we don't need to worry about that for what we're doing.

    Start by adding the following lines so your draw function looks like this:

    function draw() {
      background(0); 
      fill(255);
      rect(0, 0, width, audio.volume * height);
    }

    Let's break this down line by line:

    background(0) sets a black background (0 = black, 255 = white)

    fill(255) means the color inside any shapes we draw will be white

    rect(0, 0, width, audio.volume * height) means "draw a rectangle with an x1 coordinate of 0, a y1 coordinate of 0, an x2 coordinate of the edge of the screen's width, and a y2 coordinate of the current volume (always between 0-1) multiplied by the height of the screen". To translate that further to human speak it means - "draw a rectangle as wide as the screen and as tall as the volume".

    Open up this sketch and see what we've got so far. You should see a giant white bar coming down from the top of the screen, like a reverse volume meter. This actually exposes a very important detail about p5.js (which aleph uses for pretty much everything) - the y-coordinates are sorta reversed. In terms of the y-axis, 0 is the top and "height" (a global variable that refers to the height of the window) is the bottom. So if we wanted this to resemble a more "normal" volume meter, we would have to sorta flip our y-coords.

    If you're not seeing anything on the screen at this stage, try opening the developer tools with F12 - in the console it should log out any errors or warnings. It's very easy to make small typos when programming that can be hard to spot in the code, so the console helps us catch these mistakes.

    So we would change

    rect(0, 0, width, audio.volume * height);

    to this:

    rect(0, height, width, audio.volume * -height);

    Now it's sort of like we're saying "start at the bottom ("full", or 100% height) and subtract from there, bringing us closer to 0 ("empty", or zero height)". It's not the most intuitive thing in the world. If it really bothers you there are ways to reset the 0,0 coordinates but I won't get into that now (look at p5's "transform" function if you want to know).

    Okay, so we have a giant volume bar. Big whoop. Let's dig into the underlying frequency content of the sound itself, then we can access a lot more detailed information about the sound, which we will need to build the spectrum.

    So let's think about what we want to do here. Before we just had the volume of the entire sound - all the frequencies smashed together. What we need to do here is access the volume of each particular frequency. Thankfully aleph gives us a real-time stream of this information. We can access it with aleph's built-in "audio" object. Remember how earlier we were doing "audio.volume"? There are a bunch of other things you can put after the dot. What we're looking for "audio.spectrum".

    This will return us an "array" of 1024 individual audio "bands", each representing a slice of the audio frequency spectrum, each with their own volume information. So we need a way to programmatically get at all that information. Before we were rendering just one rectangle so we only had to type it out once - there's no way I'm gonna type it 1024 times. Enter FOR LOOPS. For loops let us repeat some action for a given number of times. In this case we want to run the loop once for each band that exists in the spectrum. We don't even have to know it off the top of our heads because arrays in javascript have a built-in property called "length" which will return the number of items in the array.

    So here's what our loop will look like:

    for (let i = 0; i < audio.spectrum.length; i++){
        // anything in here happens 1024 times every frame! 
    }

    This creates a for loop, initializes it's "index" value to 0 (let i = 0), says "run this loop while i is less than the number of bands in the spectrum" (i < audio.spectrum.length), and increment the index variable once each time the loop runs (i++). We will use this index value to access the individual bands of the spectrum.

    But presently, we don't have any functionality in our loop - it does nothing 1024 times. What we need to do is for each band, get the amplitude at that band and use that to define a height for each line. That takes care of the y-axis. We will also need a variable for setting the x-axis position of each line in the spectrum. Putting that together we get this:

    for (let i = 0; i < audio.spectrum.length; i++) {
        let barHeight = map(audio.spectrum[i], 0, 255, height, 0);
        let xPosition = map(i, 0, audio.spectrum.length, 0, width);
    }

    Let's break those two new lines down:

    The code on the right side of the equals sign, after "barHeight", means "take the volume at this slice of the audio spectrum, which will always be between 0 and 255, and scale that to a value between 0 and the height of the screen". "map()" is a function built in to p5, which scales a value from one range to another. It is very useful in aleph.

    The code on the right side of the equals sign, after "xPosition", means "take the number of the current slice of the audio spectrum that we're in, which will always be between 0 and the "length" of the spectrum array, and give me a proportional slice of the window's width for that line to live in". In other words, it gives an equal slice of the screen's width to each band in the spectrum so it spans the full width of the display.

    Okay, so we have our values but we still don't have any output. We need two things - to call a line() function that will draw a line for each band in the spectrum, and we also need to set that line's color using a function called stroke(), which controls the outlines of shapes and the color of lines.

    Putting it all together, our draw function now looks like this:

    function draw() {
      background(0);
      stroke(255);
    
      for (let i = 0; i < audio.spectrum.length; i++) {
        let barHeight = map(audio.spectrum[i], 0, 255, height, 0);
        let xPosition = map(i, 0, audio.spectrum.length, 0, width);
        line(xPosition, height, xPosition, barHeight);
      }
    }

    The line() function takes in an x1, y1, x2, and y2 coordinates that define the beginning and end points of the line. Also notice how we didn't put the "stroke(255)" inside the loop - it sits outside of it right below the background. Why is that? It's because in this case we're just setting all the lines to the same color, there's no variation per-line, so there's no reason to stick it in the loop. Doing so would only cause 1023 unnecessary calls to change the color to the same color it already is.

    But what if we did want to change the color for each band? We have different information that describes each band, so couldn't we use that to affect the color somehow? Yeah, we can!

    function draw() {
      background(0);
    
      for (let i = 0; i < audio.spectrum.length; i++) {
        let barHeight = map(audio.spectrum[i], 0, 255, height, 0);
        let xPosition = map(i, 0, audio.spectrum.length, 0, width);
        stroke(audio.spectrum[i]);
        line(xPosition, height, xPosition, barHeight);
      }
    }

    By putting the stroke() function inside the loop, and also giving it a non-static value of "audio.spectrum[i]" (the amplitude of a given frequency band at that moment in time, which changes), we now have an additional layer of visual feedback in our spectrum - the color and height of each line is determined by the amplitude at that frequency. Cool.

    So now let's spice it up a bit by introducing some midi controls. In theory you can control literally anything inside your sketch with midi - the midi controls just return numbers, and we can use those numbers to modify values in our sketches. Let's start small by adding the ability to invert all the colors by turning a knob.

    So first, inside the editor window with a midi device selected as described in step four, go down to the "Midi Mapping" section and click "Add Control". A little button with a zero on it should have appeared. This is our "midi entry". Clicking it will set it to "listen" for incoming midi messages from you turning a knob/pressing a button/etc on your midi controller, which will link that control to the entry. So click the "0" button, and turn a knob on your midi controller. Now click "Lock Midi Assignment" to prevent it from accidentally being overwritten.

    Back in our sketch, we can access the current value of that midi control with this syntax:

    midi[0].value

    The number inside the square brackets corresponds to the number on the button we clicked to map the control back in the editor window, so if you wanted to add multiple midi controls, use the numbers on the buttons to refer to them in your code. The number returned by this code is always between 0 and 127 - typical midi cc values.

    Okay so we want to invert all the colors in the sketch. We're only working with greyscale values so far, which go from 0 to 255. So how could we invert them? The background is easy enough - if we scale the incoming midi range from 0-127 to 0-255 (the range of numbers we use for colors), we can just set the background to equal the new mapped value from the knob, which means it will be black if the knob is all the way turned down, and get closer to white as the knob approaches it's highest value. The color of the individual bands is already in a range of 0-255, so we can simply set it's value to 255 (max value) minus the current value, which should land somewhere between 0-255, but inverted such that what was 0 is now 255 and vice versa. In code it looks like this:

    function draw() {
      let controllerValue = map(midi[0].value, 0, 127, 0, 255);
      background(controllerValue);
    
      for (let i = 0; i < audio.spectrum.length; i++) {
        let barHeight = map(audio.spectrum[i], 0, 255, height, 0);
        let xPosition = map(i, 0, audio.spectrum.length, 0, width);
        stroke(controllerValue - audio.spectrum[i]);
        line(xPosition, height, xPosition, barHeight);
      }
    }

    This works, mostly, but there's a small issue. If our knob is turned all the way down, controllerValue will be 0, so we will be subtracting audio.spectrum[i] (a value between 0-255) from nothing, resulting in many negative numbers, which will simply by drawn as black (the color values are clamped between 0-255, i.e. there's no such thing as "sub-black"). So now if our knob is turned too low, we're drawing black lines on a black background. Great. Fortunately we can fix this with just a very simple if/else "conditional". Logic!

    Our fixed version that accounts for this negative-number-trap looks like this:

    function draw() {
      let controllerValue = map(midi[0].value, 0, 127, 0, 255);
      background(controllerValue);
    
      for (let i = 0; i < audio.spectrum.length; i++) {
        let barHeight = map(audio.spectrum[i], 0, 255, height, 0);
        let xPosition = map(i, 0, audio.spectrum.length, 0, width);
        
        if (controllerValue > audio.spectrum[i]) {
          stroke(controllerValue - audio.spectrum[i]);
        } else {
          stroke(audio.spectrum[i] - controllerValue);
        }
        
        line(xPosition, height, xPosition, barHeight);
      }
    }

    All we're doing here is saying "if the controller value is greater than the amplitude at the current slice of the spectrum, subtract the amplitude from the controller value, otherwise subtract the controller value from the amplitude value". Now we're cooking!

    Audio is Rob Clouth's new single "Vaccuum State" - it rips. I'm controlling the black/white inversion w/ the midi knob we set up earlier - cool!

    That's it for now, but hopefully this article has helped you understand how to obtain and configure aleph, in addition to giving you an idea about the kinds of things you can do in aleph. Like many programming-related things, the sky is really the limit, so I encourage you to think about ways you could make this sketch more interesting. Maybe you could use something other than lines to draw the spectrum bands? Maybe you can mess with rotating the lines somehow? Add additional midi controlled parameters? Colors? 3D meshes? Textures? So many possibilities!

    While exploring these possibilities, your best friend will be the official p5 documentation (linked below) - aleph sketches are p5 sketches, just with some additional functionality layered on. I also recommend checking out Dan Shiffman's youtube channel for p5 related ideas. For the full list of aleph-related functions and helpers, refer to the aleph docs.

    If you want to stay in touch, offer feedback, or share your creations, please join the aleph discord server!