Notes on programming the Bitstream 3x Midi Controller

First some terminology: a “group” is the 3x’s name for a what would usually be called a preset – a definition of what all the controls should transmit. For most uses one would have a group for each device one wants to control.

To select a group, press the “group” button on the bottom row (right of the cross fader) and then press the increment up/down buttons.

It is also necessary to set the mode of the machine. For most custom purposes you will select the “user” mode. Do this by pressing the “Mode” key. Each press cycles through the available modes.

By default, all controls generate messages on the same midi channel. The Bitstream 3x does not allow one to configure each control to transmit on a separate midi channel from the machine itself. To do this you need to use the configuration software. The software is a bit confusing and has a few bugs, here is the best way to set it up:

1. Set the Group name in the “Global Parameters” tab

2. In the “Assign User & Mackie/Logic modes” tab, do the following for each control:

a. click the control

b. change “Midi channel position (N) to 0 – setting it to zero makes it use its own control

c. change the text box to the right of it to the channel you want the control to send (the control is labeled “0=Fixed >> Channel”

d. Pick the midi cc to send in the “Use Standard Template” dropdown box

e. Set the LCD String

You can also type the midi string in directly but in my experience this can lead to errors when trying to save the settings to the machine.

Before you try to send/save the settings to the 3x hardware, press the Save icon. This will check the configuration and return any errors. If you attempt to save to the machine, the software will not signal an error until it attempts to copy the settings to the machine. This will stop the whole copy process and force you to fix the error and start again. You will save a lot of time by using the save button in the software as a method to check for errors before sending to the hardware.

In my experience, most errors tended to arise from typing in the midi string manually to save from dealing with the drop down boxes repeatedly. Sometimes it worked fine but other times I think the software got confused and would reset the controls to invalid values. After many such errors, I started using the drop down value pickers only and not manually typing in values.

Moving vpopmail directories

For years I have somehow thought moving vpopmail domains between vpopmail installs was hard (ie. from one server to another). I even went so far as to custom build vadddomain binaries which forced creation to a particular directory (ie: /home/vpopmail/domains/0/, /home/vpopmail/domains/1/).

If you are not familiar with the “problem” it is that in larger installs vpopmail balances the mail directories among subdirs in ~vpopmail/domains. If all your domains are in a single directory (~vpopmail/domains) you are fine – just vadddomain on the new machine then rsync the data directories. The problem arises when you vadddomain on the new install and it assigns a different directory on the new server than on the old one.

Because qmail stores much of the mapping in cdb databases I somehow began thinking it would be a lot of work to update these. I was wrong. It is actually quite easy. First, tar up the users dir on the source server :

cd /
tar czpf /root/backup.tgz /home/vpopmail/domains/2/whatever.org

On the new server:

~vpopmail/bin/vadddomain whatever.org
cd /
wget http://whatever.org/backup.tgz
tar xzpf backup.tgz

Edit /var/qmail/users/assign – vadddomain likely created the new install using a different subdirectory.

emacs /var/qmail/users/assign
# edit the directory the domain maps to (should be the last line in the file)
/var/qmail/bin/qmail-newu

Confirm all is well with a vdominfo on the new server:

~vpopmail/bin/vdominfo whatever.org

This should show the “old” vpopmail directory that you copied over. As always send a test to make sure all is well. Start with a local test before updating DNS, if the test is delivered correctly, update DNS and test remotely.

Basic git usage

Assuming you have already cloned a repository, the best place to start is with:

git status

This will show the current state of the files including the branch you are on and any untracked changes. If you’ve come back to a project after a few days off, this will remind you where you are (which branch) and what you were doing (un-committed changes and diffs).

‘git status’ may show modified files, such as:

On branch master
Your branch is ahead of 'origin/master' by 3 commits.
  (use "git push" to publish your local commits)

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   ../../../src/apps/sequencer/engine/generators/RandomGenerator.cpp

no changes added to commit (use "git add" and/or "git commit -a")

To find out what is different in your changed files, type:

 git diff ../../../src/apps/sequencer/engine/generators/RandomGenerator.cpp

If your diff output is formatted incorrectly (escape codes instead of colors), try:

git config --global core.pager "less -R"

To show all branches:

git branch -a

To create a new branch:

git checkout -b <NEW BRANCH NAME>

To change to a new branch:

git checkout <branch name here>

To change back to the “default” branch :

check checkout master

To delete a branch:

git branch -d <branch_name>

To commit your changes

git commit -m "comment here"

Upload your changes to github

git push --set-upstream origin <your-branch-name-here>

The simplest way to create a pull-request and contribute code to open source projects on Github

Make a github account

Go to the project you want to contribute to

Press the “fork” button

Check out your forked copy of the project

git clone https://github.com/YOUR_USER_NAME_HERE/FORKED_PROJECT_NAME_HERE.git

Create a new branch and check it out (change “branch-name” to something descriptive of your work)

git checkout -b branch-name

Make your changes

Add each new or modified file to your commit :

git add path/to/file/file_name.c

Commit your change to your local branch

git commit -m "Add a comment about your changes here"

Sync your fork in case there have been any additions to the primary repository since you started your work

git remote add upstream https://github.com/original-owner-username/original-repository.git
git fetch upstream
git checkout master
git merge upstream/master

Push your changes to your forked repository on github

git push --set-upstream origin branch-name

Go to the page on github for your forked repository and press the “Compare & Pull Request” button. Follow the directions to create the pull request.

(adapted from https://www.digitalocean.com/community/tutorials/how-to-create-a-pull-request-on-github)

The Martyr as Proof of Religious Truth

It is often said that the truth of Christianity can be “proved” by the devotion of the many people who have died rather than deny Christ. The problem with this assertion is that willingness to die for a belief is no proof of the beliefs validity. There are surely many people who have died for causes of dubious truth.

What’s more, one could easily and truthfully argue that nearly anyone might be convinced to die for a cause given the right indoctrination. The many Christian Martyrs may be sincere but sincerity is not enough to make one correct.

I believe the actual strength of this argument in regards to Christianity may be the number of eye-witnesses who gave their lives. In particular the Apostles each have well documented (as much as something of near antiquity can be well documented – your requirements may vary from mine) martyrdoms.

Unlike a non-eyewitness, the eyewitness has real physical reasons to believe or disbelieve. We often place our emphasis on the Apostles belief. It may be better to think for a moment the possibility of unbelief. If Christ did not rise from the grave, the Apostles would have clearly known this. Perhaps they carried on and fabricated evidence in a desire to further their leaders work. This would have perhaps worked until faced with death for what you personally knew was a lie. It may even be possible that the rare, hyper-devoted person would die for this lie. However with Christianity we have multiple eye-witnesses giving their lives for “the lie”. I think the odds start to get much lower that this would happen.

Critics often remind Christians of the other “Messiah cults” of the time. I see these not as a stumbling block but further proof that false movements tend to die out because the eye witnesses are given reason to doubt and do exactly that. They doubt and give up. As noted, it might be possible for someone heavily invested in a lie to die for it but to have multiple people do so is not as likely. If the Christian “movement” was a lie, I believe the odds would be that most of the eye-witnesses would find a way to safely walk away when persecution flared up.

Christianity is unique in that multiple first-hand witnesses end up dying for their belief. One may argue that later Christians gave their lives after periods of prolonged exposure to Church propaganda. Later Christian martyrs believe because they were never presented with any physical evidence to the contrary of their belief. The same cannot be said of the Apostles. If Christ did not rise, they would have known it. This knowledge would make it very hard to die for something you personally know is not true.

Other faiths do not have a large number of “founding Martyrs”. Even among those that do, their strong faith is in that of an “ideal”. A belief in the truth of teachings. Early Christian martyr’s died in defense not of a ideal or beautifully stated holy work. Early Christian martyr’s died for refusing to deny a nearly absurd claim – the claim that Jesus Christ died and then rose back to life.

Given the right motivation a person could choose to die for all sorts of noble causes. The Christian Apostles died for a claim that they each would have had first hand knowledge of – did Christ rise from the dead? Many may be able to die for an ideal or indoctrinated belief. How many would die for a fact which they knew to be false?

Deeply diving into the Performer Generator Code

I don’t ask for much in life. “Just a bed and a Bible” as a man once said. But beyond the basics I do crave effective randomization for my sequencer. The Westlicht performer actually provides quite a bit in this regard. However within the many options, I found myself needing basic, quick randomization. The following is my journey to add that feature.

After getting a basic compile of the sequencer to work (see http://metajack.org/blog/2019/10/04/getting-started-developing-for-the-westlicht-performer/) I decided to work backwards. Update the UI and then fill the features in. This also will hopefully help me better find the code I need to modify.

I’m writing this after a brief session of digging and upon reflection I would recommend one at least goes through the directories to get a feel of the organization. Me? I dug right in with find and grep. It worked but I should have took a more civilized look at the code first.

I started with:

$ pwd
/Users/jack/performer-sequencer/performer/src
$ find . -type f -exec grep -q "Euclidean" {} \; -print
./apps/sequencer/CMakeLists.txt
./apps/sequencer/engine/generators/EuclideanGenerator.cpp
./apps/sequencer/engine/generators/EuclideanGenerator.h
./apps/sequencer/engine/generators/Generator.cpp
./apps/sequencer/engine/generators/Generator.h
./apps/sequencer/ui/pages/GeneratorPage.cpp
./apps/sequencer/ui/pages/GeneratorPage.h

At first it appears that each generator type has a class but inspecting Generator.h shows some code involving both types (or at least the selecting between them). Not sure if this is purposeful or not.

First though, let’s make a minor tweak and make sure things work as we expect. Let’s modify a bit of UI, compile and test:

$ find . -type f -exec grep -q "Seed" {} \; -print
./apps/sequencer/engine/generators/RandomGenerator.cpp
./apps/sequencer/engine/generators/RandomGenerator.h
./platform/sim/libs/args/catch.hpp
./platform/sim/libs/soloud/doc/sfxr.mmd
./platform/sim/libs/soloud/include/soloud_c.h
./platform/sim/libs/soloud/include/soloud_sfxr.h
./platform/sim/libs/soloud/src/audiosource/sfxr/soloud_sfxr.cpp
./platform/sim/libs/soloud/src/c_api/soloud_c.cpp
./platform/stm32/libs/libopencm3/include/libopencm3/stm32/common/rng_common_v1.h

I had hoped keeping “Seed” case-sensitive would lesson the matches. Inspecting RandomGenerator.cpp :

emacs ./apps/sequencer/engine/generators/RandomGenerator.cpp

yields up :

const char *RandomGenerator::paramName(int index) const {
    switch (Param(index)) {
    case Param::Seed:   return "Seed";
    case Param::Smooth: return "Smooth";
    case Param::Bias:   return "Bias";
    case Param::Scale:  return "Scale";
    case Param::Last:   break;
    }
    return nullptr;
}

We’ll change “Seed” to “Gene” then compile and run :

cd ../build/sim/debug
make -j
./src/apps/sequencer/sequencer

and indeed our change is there. Undo our change and set about how to add our own option…

Generator.cpp seems to be where we call our new custom Generator but how to add to the UI? Generator.h has the text for the modes. If we add a line for ours (let’s call our mode “Quick Random”) :

   static const char *modeName(Mode mode) {
	switch (mode) {
        case Mode::Euclidean:   return "Euclidean";
        case Mode::Random:      return "Random";
        case Mode::QuickRandom:      return "Quick Random";
        case Mode::Last:        break;
	}

and compile we’ll get an error – we’ve not defined QuickRandom. What if we copy and existing mode and rename it QuickRandom?

$ cp RandomGenerator.h QuickRandomGenerator.h
$ cp RandomGenerator.cpp QuickRandomGenerator.cpp

If we just rename the class, we’ll stick get an error when compiling. Need to check the errors and add a few more things in Generator.cpp

# add (keeping the string hash/pound symbol as it is a compiler directive
#include "QuickRandomGenerator.h"

# modify
static Container<EuclideanGenerator, RandomGenerator, QuickRandom> generatorContainer;

# add
case Mode::QuickRandom:
        return generatorContainer.create<QuickRandomGenerator>(builder);

and in Generator.h

public:
    enum class Mode {
        Euclidean,
        Random,
        QuickRandom,
        Last
    };

still not compiling …. did a few more things …

Finally add your files to cmakeList.txt so that the files you add are compiled and linked

After all this, got it working and we now have a new Generator menu item. For now our generator does the same thing as the one we copied. We’ll work on that next.

Getting started developing for the Westlicht Performer

Here are my annotated notes on setting up OSX to contribute to the Westlicht performer sequencer.

1st step is cloning the github repository:

mkdir performer-sequencer
cd performer-sequencer
git clone --recursive https://github.com/westlicht/performer.git

I don’t recall if git is pre-installed on modern macs. If not you’ll need to install git as well. One item which is not installed is is cmake. Realizing I needed cmake and possibly other items is what prompted me to create this post.

I already had homebrew installed, so tried :

brew upgrade cmake
brew install sdl2
brew install libusb # perhaps not needed
brew install libusb-compat

which, after updating brew, worked (we’ll need sdl2 and libusb later on)

A first attempt at compiling is done with:

cd performer
make tools_install

Note that this step appears to install the arm compiler tools. I wish I would have skipped this and saw if things worked without them. Eventually I’ll need them but in case I do not move forward on this I’d prefer to not have the tools installed on my computer – especially as they are not under package management.

My install failed anyway with:

configure: error: libusb-1.x is required for the MPSSE mode of FTDI based devices
make[1]: *** No targets specified and no makefile found.  Stop.
make[1]: Nothing to be done for `install'.

so moving on…

make setup_stm32

sets up the build dirs according to the developers readme. Ran without fail.

Next up is:

make setup_sim

My first results i below. However as a note I performed a parallel install and had a different error:

libopencm3 submodule not found!

which was fixed with :

git submodule update --init --recursive

The first time I tried, it exited with:

-- Building for platform sim ...
CMake Error at /usr/local/Cellar/cmake/3.15.4/share/cmake/Modules/FindPackageHandleStandardArgs.cmake:137 (message):
  Could NOT find SDL2 (missing: SDL2_LIBRARY SDL2_INCLUDE_DIR)
Call Stack (most recent call first):
  /usr/local/Cellar/cmake/3.15.4/share/cmake/Modules/FindPackageHandleStandardArgs.cmake:378 (_FPHSA_FAILURE_MESSAGE)
  cmake/FindSDL2.cmake:173 (FIND_PACKAGE_HANDLE_STANDARD_ARGS)
  src/platform/sim/CMakeLists.txt:8 (find_package)


-- Configuring incomplete, errors occurred!
See also "/Users/jack/performer-sequencer/performer/build/sim/debug/CMakeFiles/CMakeOutput.log".
make: *** [setup_sim] Error 1

I need to fix this but first I may as well try compiling the sequencer itself:

cd build/stm32/release
make -j

which, surprisingly worked :

[100%] Built target sequencer

To build for the simulator:

make setup_sim
cd build/sim/debug
make -j

To run the simulator

./src/apps/sequencer/sequencer