Generating Music with AI: Combining Markov Chains with an 8-Step MIDI Sequencer

In my past few posts, I’ve spoken about the process of integrating Max and Arduino. I want to do this because I’m going to be using Max to program an artificially intelligent 8 step sequencer with a physical analog controller that I’m calling the After-Eight-Step!

I am using Max for this partially because of a pre-existing machine learning package called ml.lib. At first, I planned to build a Markov chain from scratch – but using ml.lib makes the process faster, allowing room to experiment and create interesting things. Also, considering this is my first coding project, that might have been a bit ambitious!

Markov Chains

Like a probability tree, a Markov chain shows the likelihood that one step will move to another.

Image

The above diagram shows the probabilities within a three-step chain, also known as a transition matrix. Data, such as MIDI notes, is often analysed to generate probabilities in machine learning. The After-Eight-Step has two different Markov chains. One trains directly from the sequence, whilst another is influenced externally.

Auto Markov

My first attempt at creating a Markov chain is the Auto Markov. It is brilliant at generating very similar patterns to the original, but has a tendency to get stuck repeating elements of the loop.

The Auto Markov is fed MIDI data from the coll (the object storing the 8 MIDI steps), which is then processed into a Markov chain by the ml.markov object. The ml.markov object analyses these eight input steps and translates them into a transition matrix, similar to the one I mentioned above, before outputting a stream of MIDI notes.

This Markov chain trains on the last 1024 MIDI inputs every eight steps. This high number makes it excellent for slow transitions when switching notes, though this effect isn’t consistently tuneful.

Image

The order of a Markov chain indicates how many previous steps factor into calculating it’s transition matrix. When analysing large sets of data such as entire songs, this is particularly useful. In these large data sets, an order of 1 would be almost useless when analysing 100s of notes. However, it actually causes issues when trying to implement it into my tiny data set.

One issue is that anything above order 2 creates a sequence identical to the input. Because there are only eight steps in the chain, it doesn’t have enough options to move between. When the data is analysed, the Markov chain can only theorise that step 1 moves to step 2. It can’t form any links between the others as they provably only move in one direction.

Image
Markov Chain with Insufficient Transition Data

A solution is to ensure two of the notes in the sequence are the same, which helps mitigate issues – though this takes away from the freedom of expressivity and makes the device harder to learn.

Image
Markov Chain with Repeating Data Points

Using an order of 0 creates a random sequence of notes, but this isn’t a Markov chain. I recommend sticking with an order of 1, as this is a mid-ground that will work particularly well when changing the inputs.

The Auto Markov tracks all notes in the MIDI scale rather than the sequenced steps. This means that it is more flexible and adaptable when notes change, as they heavily affect its output until the chain is reset.

Creating the Adjustable Markov Chain

The previous Markov chain analysed MIDI notes. Whereas the user directly controls this second chain by manually adjusting the transition matrix. This directly controls the probabilities, producing an adaptive ever-changing flow of notes.

To start this process of building this, I’m going to need to actually bring in the second set of inputs.

Previously, I used a join object to consolidate the potentiometer data into a list before scaling them. These new potentiometers control probabilities, not MIDI notes, so the scaling is done entirely by the ml.markov object.

Image

Creating a Modifiable Transition Matrix

The below patch is a transition matrix. It looks like the scariest part; however, to help decipher the patch, it is important to notice that elements repeat.

Image

A rotary encoder controls the section with ‘addition’ objects. These addition objects sum the potentiometer value and encoder setting value.

Each setting adjusts the likelihood to move to the previous, next, or current steps. This means that the ‘addition’ objects have to shift position to affect the correct step in each list. To make sure ‘next step’ is always the next step relative to the current position in the sequence, the list’s value positions are incremented by 1 with each step.

Image

The potentiometers provide the Markov chain with the likelihood for each specific step, whilst the encoder controls the chance of direction of play. Each could cancel each other out with large enough values, allowing many creative tangles of probability.

At the bottom, the message box shows the total likelihood of each step to play next.

Image

This data is sent to the ml.markov object via a ‘prepend transitions’ message. The step numbers are independent of the pitches, meaning a step in the sequence can keep the same probability but change its pitch.

Unlike the previous chain, the coll is accessed after the ml.markov object. This gives it a huge amount of bias, making it less of a Markov chain. However, this allows for more expressivity when performing music.

Image

One thing to work on in future might be varying the user’s control over the chains. I’d like to combine the first and second Markov chain together to create something controlled partially by the AI judging the note progressions and partly from the manually entered transitions. I might explore this in the future, as I do plan to go further with this project!

I will be uploading a Youtube video very soon detailing the After-Eight-Step sequencer’s build process, which is what I programmed these chains for. So, keep an eye out for that!

References

Pearce-Davies, S., 2019. Ml.Markov Tutorial (Part 1) – Machine Learning In Max/MSP. [online] Youtube.com. Available at: <https://www.youtube.com/watch?v=LG-GYFyJw74&gt; [Accessed 8 December 2020].

Pancake, M., 2020. MORE AI Generated Continuations Of Never Gonna Give You Up (Openai Jukebox). [online] Youtube. Available at: <https://www.youtube.com/watch? v=13W0PxWLuP0> [Accessed 26 January 2021].

Building an 8 Step Sequencer in Max

I’ve recently posted about the process of multiplexing 16 potentiometers into Arduino. To start building a step sequencer, I won’t need much more hardware than that – it’s possible to program one with no hardware! I’ve gone over how I put this together here:

To start with, I use the serial input patch I covered in this post. This patch brings in 16 inputs, 8 of which I’m using to control pitches in the sequence.

Max Part 1: Pots to MIDI

The first step is to scale the potentiometer inputs to fit more neatly into MIDI notes.

Image

I take each of these eight MIDI notes, add an index number, and put them into a coll object. A coll is a list accessible from anywhere in the patch.

Image

Max Part 2: Sequencing MIDI Notes

Now that I have a way to control the pitches, I need to play them in sequence. The coll has index numbers, which are used to find and output the relevant MIDI note.

I’m using the counter object to send off index values. Once the counter receives a bang message, it will output the next value in a set range and then loop.

The metro object repeatedly sends bangs. By adding a speed argument, the metro can plug into the counter to change tempo.

Image

This is now essentially a sequencer! There are many ways to use this series of MIDI notes, including linking it into your DAW of choice. For this project, I am translating them into dial-up-esque sine waves.

The mtof object ‘performs MIDI-note-number to frequency conversion.’ (mtof Reference, 2020). It takes the MIDI note I’m sending and converts it to its frequency in Hertz.

The cycle~ object then converts this frequency value into a cosine wave signal. This signal can then finally join to an ezdac~ object, which routes this output to the speakers.

Image

I’ve included the Max patch for this sequencer below, just copy and paste it in. The sequencer is controlled either by entering the potentiometer values/MIDI notes directly or from an external source such as an Arduino. If you want to grab the Arduino code and instructions on routing this into max, please check out the links at the top!

<pre><code>
----------begin_max5_patcher----------
4869.3oc2ck0jaajj94V+JPvm1MBYN08w7zJ6Pwr6Fgmv6nYdxdhN.IQSAMn
A3B.Jq1SL929VnJP1.jEIKBVE.6UxpMZbkY9kGUVYcf+46dX1hhukTMK5OF8
yQO7v+7cO7f9TMm3g1e+gYOG+skYwU5aaVdxuVr3Kydu4R0IeqVe5s4eoHMO
Rr6B4aeNMOKoV+Pv1SltReupm+6HXPmasXa8t6U1dVyopeYShg6lM68W3eQ+
81GcSb8xOmlu9wxjk0lmFx.y4T..PEDHhRgBw6ivX4bv6if.by+CglCh96Mu
g+06dWyOd+sgHUKiyRh.pWOBGQAQBfUrgYAaPb6XC7jXyUK6HIQK6PPHjcis
Pz+QcY550IkUQeGzpvKrI7rvK7TjQwyuIgeYwyOmjWejz+8Q+Tghm2wTkIUp
6JtNsHuCiQH1XLHUyXTiVAzvXt5PAOgQCvBez8cXAzHH5w7FjCOh2FhEy1mW
jTdAvAKD1.GsIKsiEqyXy93RahKieNoNo7wj73EYIcQH0KI84sO28T8gRz62
KP6twFu6K.vVijsHNe8YMbwbrMc.3PHHTp.Hxyp.4aNU.DNsp.Ll4UU.B71y
K.AmTU.RP7qJ.9lSEfXxoUEfQ9UEfd6oBf7oss.Avup.7au1BXzIUEv8bSAj
2bZ.1D2RfmaHf9lSADrlANU+n93+0e5+7uF8o+5G+onO8w+m+1G+y+vG+K+R
9O743xzpneHtN5O7Gh9gOmlsJp3on37nOjuprHckpuIHPz+1R88sLtd9uVTt
pAJplqnz+9NxjklmrrXatlVXG0aX4E5j0wvFcNpA1PRDfg3TI48Qsc6Ta7P3
dF09Swo4WJdNCMmggPk5CyUZRFfqThPU+6z+gCoJELGqNofd0cDkg8V+PoP4
w7ojHOlOUYJeKcJcsBy98K0gcKM.PDV3Ek42bDQJkDHUvIDADcsPnaIpuMqN
UYkmmmj8UksdrwT97AJbNjPU5573rKVXML0RejjH9UAKdz5+G2VmbolRwyUd
gXFRvAD.m1vbTURlGwwDwUa7S8YQXTrTW9DnPVQSY6NGeN.n7YUjw30IGAkf
A3PXEGQx4zy09HxBNRNeQ.cuksyZ7pPSzghfUHtqH3Qq0eJK9knOk7+tMIe4
kSAzhcKQkVJTz7WgjC3Xo.1TWS3bHkxHH04DDNiBuRyXLyalwXArOa2XFyHj
qhsG.juXaccQtqCGAD6TBwNVQ5KmKq33jBDljnH69oOqGeVQ7JMS4Lbfl4YQ
tQ6dT3BgtjxTQPRebYQ9WSJqqhdNcUZTcQzSkFOsWbGFfWc5dDUqbzFIi4ip
keJYSQu3rrpnpcwNhRy2rsVIhEOGsrHKyYQDwuZQj0lnXWMImqSpEZzndWd0
IpW0nDSRW+4Z2kNxUKcPUFMG0n.mn0iBdPZo09figrIOHeN3XHhkj23lQEkf
ChS4m1jjr5RcOgvNN7HVEejJAp+J.LHiHnxgMPYPDvi4nwOlSoRzE3zPUiBo
kJkh4xi4lATwBnaig1M1qigOzXTI77xoG8W4RmcXgifCKm4AG1SIqEaRxcWZ
A9TZoM8X7PuKBUGdBSCw7VPYmpD2UtOMVf.uNaErZYSLs5vvgTh4tKwTuJw1
FfOhQVCqDybWhId0G113oMJ5Xp6RL1qRrsguZTzwD2kXjem0QzIRGicWhgdU
hsMWIFEcLxcI1qsNYa7nFEULzUAVJ8o7ZKpkos3vHtM8hM56er6To6hBrvt.
SbZNkdNg25Lham3KOJY5p3ulr5QEKodAOFWWWltXasYd19vdT3gYIpb5W0I6
YchxKSqZyRloOoFMmPLkGJL05PGzlL68HlVWrdcVxPFkHkCxQU6rag9bUUvF
dMJctGXoMihz0p0XDz4kP+YBqD6xBW6Phj36xXZW7k5HfXYXhCtMWooifmnW
IziEaA8J8YaT5cbaGjM.GKNzyc.fPxusJd4PFJVq04fn6kXyOcejm73PePrT
IQoPziO4DVe9zm0oPfmKk.AG4pCC0uoAKIy00BWKdxfzk8kurLK42cV9t1T.
aGF7q1aPP8QUTOULv5hmbdg130j.sKqF8KFElJF2rNZVE41hpvVZZH.XnKpB
HKrqoBBBOdqoBqKn.obnyfMVvl.aXv3U1X8rrXjlEsP+p.Pu0T.1qQv3o.ru
XBFrB.Kdy4AXsXqimBv9RIX3Jf2bd.1q86Hp.rtPBFtB.7lSAXsTziXa.VWF
ACVADvkSTnZCvZkwGOE.yqMA.Iu0ve1z1BfWi+idyg9gK5+IFxflIHldlg47
fjXuCOHvf2VFtl+csyYRnYV1ggziLdzuAk83AkG9ozrjulTV0Wc9vr3Ma5b5
G57HMH8WJJ6rcEXLpLmBt+TkIeceUle8FiKUxQsRH1VpQrYeisyps40TrJoL
eap9MYNoRm2xRZsatxttZS7RyC2XDr6xcWmv5JuI0S8NI2TFNy5JY2MuXcVw
x+wAEFuYpcnLNNvBe+kWk7T71r5GepHutJ82zb.rwX0x0epkCsdwFYPy9enL
MNau.rtLcUQtd9kzUSzb5cjqY13Y5fbWgQeG4war7vlgG3DWrRIjaqVDW1nn
ZiRf1cw5hhr9WZ+ykk7Tc6k2jlme.JVWr4zWrrY1Jd5KunPcwmO26VekpG2l
at5iJah5GaFJj92WbVVaHf9u9uEqB9EWmTmZTAHv9KZhT94pkkMSWztxq4Je
0xUVorwWl7qoqp+rlPcMFT2d5lcFQy1qkWktNopt+4piWW0+LU0uX.8NmZ6h
Ve3GqSddSlRJ5eCJuizp5pOW7qUs23NCst.vq6tMc8o6FOs24OWb0CJ73mUQ
6S5dIawVOp.jhCdhNAYwcth03rskoua44OYzRfNxnvL0vNXBV+5Pf8vtFY7L
3nTWu76WK1re1ic5FouzZUpWKvW.afbyn1DFvwvjWKDvjmFB.mWp3Tz7l1G6
0.IyjcAFr6m8vlckbsqaklgxWknkQnb5QDwvQDlEDgi.2Bhf.SOhvGNhPrfH
TxsYiH7HhzeP.NXf.T97JwY+Tbxc.iMb.qYDrNxoRHrV09QDCPWMFP7KFPkf
oFCfWMF.8LF.BpcPuNT6tPRm0STNYGiu1FYct4U6AYLX0gcycZgJzcIT0Fd4
tBpPr6SqJIHfPU+oUwAI3VTVGs7jvH5DVb3SGABdVX5RvS6VQBDHsMSjFA.Y
YVQUxUiGffgGTpYQJ1qbQDcIRv76nL9oCOielY2WgX5NyfyeiORsa+WJpiKe
I5i4Kap5Uz22cAZ6LbQtgjeM8EhPj2esZCgSc70cFSD9DEQ8nd+ewnGbTvhd
.fcVMyPQXBm5tyxU6kL7fJMIpcT6rsVE2Y9L3o1mgPY1vJV.8fznxIQJvIPJ
4.cTbBEfJKFbye3HBGygLtBEP7apMoouYYvf8frZUPk6xGARnbFG0r6GMX3g
N83yv6KMAPBM9fmd7AMX7ASQgFetC7uvCGe.f.iOSeUuoCubcHhHvvCe5gG5
fgGnLzQmuCBNO7BdCIgN37cPr4g2GRYvCMO4nCA64LeLU9cv.Ba5QDpmy04F
QDxjiHXtmyt4FQDzziHROmOyMhHS+vTSfdNElaCQDSuIhuSZ41.joOtJF64z
TtM.4NHrJzuIlba3AZjJ0YuU1o6iHomKwIkMYSngOLH..x7bMd42g03kHtKq
wKkJu6FhdB69rb3b18GTQlZnxdC52iVUn6Rn5tzpBL4NfV6s4cnUEVbWBU2i
VU3IOrt8dreOZUM4g0s2U96QnBcWZUcW5.BtKsptGgJzjGV2dkitCc.Qv6Rn
5dzpBxm7TPIuQxqBRtKgp6xXUStUk7MRZUHx8HREXapSuzV2l+khz7HD3Zww
yLlYH3kvofrMKbU3Mx7IVDEl4BauMWW2MMugE6zYgbWVh0Vm2ibnsuhWg2pT
us3FgnW6bnFgG8oFJDX9p4xF8IUsRqlrd.A.G9b4ixLaMlhfN3BmaZ0yv7lM
izHIfDQnfHJBFw3LEviTGSzetfU8v.GQg7l44s4DbH1b.gKi.5+1CD58wyEc
s1bf.Ou8agajX+mJlQyDq5kmWTjc04sItYKLyRaZJF9p3pkooQYoUW83cBo2
rXCPM6vvSgiEknbYTIrEgUNRrHpxChqOV4FITtV5iQMmW8qX8AJ2OhTeLo4A
U+r4XP6+s+dLOq5NadOfFpzbIn9Wgs2l5psj17eTaOBUeaPaWReUCCaXut+r
+je+l70ggdM5fLqVVgYa7v695myB3W7Xv0eYl2fbYnit1tJJMaqxdGwOSRNM
ecKGV.VVnvjlc9efkOUgf8oBNhcMIstH9jHC9DiZ0rvkgrUvA2tnUGav42xh
VWVrcix0Cb0K0P7rg2E2AAS6BqM51PUIYQ53SWqgDZfarUluKwu9+GFbgYyY
bvq+AxT8vfO9aK.ko40WqwkHTAmfL7bl5ObpJAIrjPZ58EEYNmB.IpyIExi2
598DRsXPKw6P0m9anGq1vrwuO9UIM6ehQKijLvUW8I3.ifs6SCyP7JgRnFLQ
DBFg4X.g27Ed5r1ZutMO1jDzI179zzu451AzphskK2I.sBeT+Y+3pjp5z78a
5k+79XXGbeEkqLahntsvLBEi0rT2if14LPX3LWYslMvhHW8AtZtf3HWzLWhC
GWvcEKXgjKjtZESCHWzXHhbQgDXd.L07fq9GDXH4BrqVlgjIb1IMnPA0U2if
ZVvbjKXgLrIx4.V7PxEBW4BR.4BW8PfidSrtxYjwNqjlo7jaNzgzHF6bX9PZ
DicM3FMjgUvtFVghBIW3pCMIjM23pBgM1tMtxXxwNPCwU+YZHafl3r+bPsdb
1eNnbgq9yjPFUg3bCzf.xEtl2VHsMcUczLz0iquqqbFeri1w.2C5MWiovDgj
Kv2CPgq9QrPltTy6FJcAJfAlKboHEPRfYBrKLQn0GLWBrEZ0gSQWCs5.xcgK
XglKbgIBsQgSHgHvLgvkrAAgVc3TNon.yEtDyDGb+CWLMwA2+vo5KGZaSnKw
tIg13j3BSDZaSpKLQvsMcBJBtsoSXQvsMcIvIKzlEPWRrfExtQKt47taO4tu
5hyZ9z5s5QymQvGiqqKSWrs1LLzcXlq6yc25rhEwYG7Mpy1WCu28Jy098+TK
a8GIbymCzCQp1OJnVGf4W8bN4PT0wf8nu7nWCotLkjdhRu1HwIiJB7Eo.WjT
POQJwEoDxSThcQJg8Dktrhh3IJcY8D0GTB.bfVRu3RAfiGoPiGoviGoHiGon
iGoXiFofimwNT3BoHldPeajB6hwtWBpiIiEkbhP.eznHB5Do7QSUHm7e8QRE
XfSBkOZUzMqOfOZVDKbhT9ncQhS9uDc4032LsjNSKzsRK2zV9vGdeNqNHVra
lVPmoE8loExYZQtYZgclV3alVDmoE7loE0YZc61gLmnE2Kzh6Bsv9ngKyaAd
IeYh2HE9BjxKcbj3TSWH+IUWpW9dTWQuDoPdiTWpGwXg2H0k6Rr2HE+RjxKw
KXN0DoWHkKM7S66PzeJZDXR2aZjdvjvXvjl5hGNyGtcTxXQIlKxDkYGNAiDo
A9VSJboUbHJHVQNQaIOD9NRWLqD9naHRlqUQ3lojKIt.gAQU5DsOHfkuTkxg
UQHyXYrn3a5kg8Aimg5z6FhiWmRU67U0ultr5g2sNn0tEzvEuasExtgxt8tM
LW7lMeMorpUTzO9rmi+RgF0DuW+qo4leUuNKmUl70zc2u4FhKW94z5jk0aKM
Czy2Xl0X+rma9TtluMsU6qGyoYU0ujc3f6reLh9v1UoEepNtda0i+XR9VyXF
of7mh2lU2WOsX8SoYYKKxLrW2QqZ2x4bl4p6F3o826OGAlijDHTnzYywPLGw
zGoNfR6NgULOCb2CQnDI.0bqDFhSn5iDHLE2bD3fGC8Js.PogB.o.PLGoNET
QqtOVb9ZyR9Eo+5D2d1MkEMe90aQc0iJ2e+aqKVWFuJs8CaAn2Hp89Vy7R0U
0vdmwga1yJ.Mc2yz05Yux3G+w+6mJJ+w16ucsM2SEzAPM6FOc+wdGlEq6A7X
jBD0H.gpPC8Qby459TIYIM6.O8dTl.AvZ3mofQp9HNQvaV76v4689NiQiqb7
MYZ4lDdnskXuEkfCnBg4HAACs8XSms0IzpPHf.MtSDpzH9PAlx8fZs8Y2Gv1
UzFLW12l3eTkk11lfy1Dce9K73NqD8nOAFxgs.uJflAFwXFBXymX2V4fOCj9
+us16hdsamnG.dCQQFub4QNA6teldmWvbjAiNRM1tmIL8Lxfaa443seM4bst
+Tg58l9aI61kc8FkMXG..OetE8AyFKTsmEkATFo5iZRf.0GMswzmFnul25N1
WkQ2GZ1TLl4IznL.nvPDU+HLEKRy+tEYaSBPdi6ibg4LB2DvsIbkVVH9In0A
sJBgRcdkbJfZBwyoBSBpCITmOyMz.0c8huwW0l3rjuS8BJe4b5tlsFFMH8XZ
dSGWR1AVf224GmogVUBIRCTJvBSl7BIAcXlFG3HiBg2noGX5MBml2W0lXSuN
06aNu6e8t+OPC9rg5
-----------end_max5_patcher-----------
</code></pre>

References

Docs.cycling74.com. 2020. Mtof Reference. [online] Available at: https://docs.cycling74.com/max8/refpages/mtof.html [Accessed 30 December 2020].

Docs.cycling74.com. 2020. Cycle~ Reference – Max 8 Documentation. [online] Available at: https://docs.cycling74.com/max8/refpages/cycle~ [Accessed 30 December 2020].


Serial Communications between Arduino and Max

Over the past few posts, I’ve designed an analogue user interface with 16 potentiometers outputting a number between 0 and 1023 to the Arduino through two multiplexers. Now what I want to do is take this data and use it for something purposeful, eventually the ‘Markov Sequencer’!

The first step is to decide which program I want to use. I’m still relatively fresh to coding, so I thought it was sensible to do the more complex elements of this project in Max. The equivalent coding in Arduino would be significantly more time consuming to write and learn from scratch. Using Max also means that I can turn this device into a patch for Ableton using Max for Live.

One negative of this choice is that the Arduino will always have to be plugged into a computer running this Max patch, taking away from the standalone nature of the device I was envisioning. In the future, if I particularly like this design and am more comfortable with programming, I could try to recode it using Arduino.

Image

The Arduino Code

The Arduino code, detailed in my last post and video, sends each potentiometer output to the serial monitor with a space between them. Once all 16 have been read, a carriage return (enter) indicates to the computer that it has reached the end.

Opening the serial monitor in Arduino before/while using Max will mean Max won’t read the data, as only one program can use the COM port at a time (Watson, 2019). This was a big point of confusion for me as I instinctually wanted to open the Arduino serial monitor every time there was an issue to check whether the information was coming in.

Image

Part of the Arduino code i’m using to feed the Max patch

The Max Patch

This patch is heavily based on one shown in Programming for Peoples tutorial (Mckellar, 2016), but I have adapted it to work for 16 multiplexed inputs coming in as a list. I’ve also made a comprehensive video tutorial explaining the information in this article if you’d rather listen than read.

Serial Object

The first step is to find which port the Arduinos serial data is on. There is an aptly named ‘serial’ object which does that.

Connecting a print message to the ‘serial’ object’s hot input shows a list of available ports in the max console – my Arduino shows on port C, but this changes depending on your set up.

To link Max to a serial port, type the correct port number and baud value into the serial object.

Image

ASCII

Arduino sends out data in ASCII format – a system where each character is converted into a string of at least seven binary numbers (Scott, 2013); upper and lower case, punctuation, and in this case numbers. The Arduino uses UTF-8 ASCII format – which is 8 bit, not 7 bit – but this still isn’t the format that Max expects to work with.

Metro Object

A ‘print raw’ object and a button connected into the ‘serial’ object prints directly to the Max console – meaning you can see the ASCII information coming in. There are, for example, a lot of 32s in this list representing spaces.

Image

To avoid having to click a button every time, a ‘metro’ (metronome) object and a toggle can be used to send bang messages through the serial object repeatedly. This means a steady stream of values will print into the Max Console until the ‘print raw’ object is deleted (as the numbers speeding past get pretty annoying!).

Sel Object

One important ASCII string is 13 10, which represents a carriage return. The code asks Arduino to output carriage returns after all 16 inputs have been read.

Using a ‘sel’ object with the arguments 13 10 will separate the data into lists of 16 values. When the ‘sel’ object reads 13 10, or a carriage return, it outputs a bang from its left output and outputs the rest of the data from it’s right (sel Object Reference – Max 7 Documentation, n.d.). Combining these into a group creates a list of the ASCII characters between carriage returns, a little closer to my required output.

Zl Group Object

Connecting the left and right outputs of the sel object into the hot input of a ‘zl group’ object creates this group. But it can’t do anything about the fact that the numbers are in ASCII.

Image

The ‘zl group’ object also needs a number argument specifying how long the group will be, and using the improbably high number of 1000 means that the list will always be read in full (Mckellar, 2016).

Itoa Object

There is an object called ‘itoa’ in Max. It stands for integer to ASCII – exactly what we need here!

According to the documentation, the ‘itoa’ object ‘converts a stream or list of up to 256 integers into a symbol’ (itoa Object Reference – Max 7 Documentation, n.d.). It recognises integers into its left input as in UTF-8 ASCII Unicode format, translates them into the correct characters, and outputs them as a symbol. 

The values which it outputs are between quotes, signifying that they are in symbol format, so there is still one more object to use.

Fromsymbol Object

The object which converts from symbol has a pretty handy name, it’s just ‘fromsymbol’. ‘fromsymbol’ reads the input and converts it into integers, by default using the space as the separator. This is why I have used a space between each number in the Arduino code.

Image

Unjoin Object

The final bit of coding here is to ‘unjoin’ this list into separate integer variables. Neatly enough, there is an ‘unjoin’ object I can use, giving an argument of 16 because there are 16 pieces of data to split. If I then connect a number box to each output, it creates a visual representation of the potentiometer rows on the breadboard.

Image

In the above image, I have also included one inlet and 16 outlets so that it can be used as a patcher object within any Max project. If you copy and paste the code below into Max you can mess around with this yourself:

{
     "boxes" : [         {
             "box" :             {
                 "maxclass" : "outlet",
                 "id" : "obj-50",
                 "patching_rect" : [ 479.500002920627594, 555.000002145767212, 30.0, 30.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0,
                 "comment" : "",
                 "index" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "outlet",
                 "id" : "obj-51",
                 "patching_rect" : [ 427.500002920627594, 555.000002145767212, 30.0, 30.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0,
                 "comment" : "",
                 "index" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "outlet",
                 "id" : "obj-52",
                 "patching_rect" : [ 375.500002920627594, 555.000002145767212, 30.0, 30.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0,
                 "comment" : "",
                 "index" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "outlet",
                 "id" : "obj-53",
                 "patching_rect" : [ 323.500002920627594, 555.000002145767212, 30.0, 30.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0,
                 "comment" : "",
                 "index" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "outlet",
                 "id" : "obj-54",
                 "patching_rect" : [ 271.500002920627594, 555.000002145767212, 30.0, 30.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0,
                 "comment" : "",
                 "index" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "outlet",
                 "id" : "obj-55",
                 "patching_rect" : [ 219.500002920627594, 555.000002145767212, 30.0, 30.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0,
                 "comment" : "",
                 "index" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "outlet",
                 "id" : "obj-56",
                 "patching_rect" : [ 167.500002920627594, 555.000002145767212, 30.0, 30.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0,
                 "comment" : "",
                 "index" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "outlet",
                 "id" : "obj-57",
                 "patching_rect" : [ 115.500002920627594, 555.000002145767212, 30.0, 30.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0,
                 "comment" : "",
                 "index" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "outlet",
                 "id" : "obj-43",
                 "patching_rect" : [ 479.500002920627594, 468.0, 30.0, 30.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0,
                 "comment" : "",
                 "index" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "outlet",
                 "id" : "obj-45",
                 "patching_rect" : [ 427.500002920627594, 468.0, 30.0, 30.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0,
                 "comment" : "",
                 "index" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "outlet",
                 "id" : "obj-37",
                 "patching_rect" : [ 375.500002920627594, 468.0, 30.0, 30.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0,
                 "comment" : "",
                 "index" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "outlet",
                 "id" : "obj-39",
                 "patching_rect" : [ 323.500002920627594, 468.0, 30.0, 30.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0,
                 "comment" : "",
                 "index" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "outlet",
                 "id" : "obj-41",
                 "patching_rect" : [ 271.500002920627594, 468.0, 30.0, 30.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0,
                 "comment" : "",
                 "index" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "outlet",
                 "id" : "obj-35",
                 "patching_rect" : [ 219.500002920627594, 468.0, 30.0, 30.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0,
                 "comment" : "",
                 "index" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "outlet",
                 "id" : "obj-33",
                 "patching_rect" : [ 167.500002920627594, 468.0, 30.0, 30.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0,
                 "comment" : "",
                 "index" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "outlet",
                 "id" : "obj-31",
                 "patching_rect" : [ 115.500002920627594, 468.0, 30.0, 30.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0,
                 "comment" : "",
                 "index" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "comment",
                 "text" : "B Pots",
                 "id" : "obj-29",
                 "patching_rect" : [ 531.500002920627594, 520.0, 150.0, 20.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "comment",
                 "text" : "A Pots",
                 "id" : "obj-16",
                 "patching_rect" : [ 531.500002920627594, 437.0, 150.0, 20.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "number",
                 "outlettype" : [ "", "bang" ],
                 "id" : "obj-48",
                 "patching_rect" : [ 479.500002920627594, 519.0, 50.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 2,
                 "parameter_enable" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "number",
                 "outlettype" : [ "", "bang" ],
                 "id" : "obj-46",
                 "patching_rect" : [ 479.500002920627594, 436.0, 50.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 2,
                 "parameter_enable" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "number",
                 "outlettype" : [ "", "bang" ],
                 "id" : "obj-44",
                 "patching_rect" : [ 219.500002920627594, 519.0, 50.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 2,
                 "parameter_enable" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "number",
                 "outlettype" : [ "", "bang" ],
                 "id" : "obj-42",
                 "patching_rect" : [ 219.500002920627594, 436.0, 50.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 2,
                 "parameter_enable" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "number",
                 "outlettype" : [ "", "bang" ],
                 "id" : "obj-40",
                 "patching_rect" : [ 427.500002920627594, 519.0, 50.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 2,
                 "parameter_enable" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "number",
                 "outlettype" : [ "", "bang" ],
                 "id" : "obj-38",
                 "patching_rect" : [ 427.500002920627594, 436.0, 50.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 2,
                 "parameter_enable" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "number",
                 "outlettype" : [ "", "bang" ],
                 "id" : "obj-36",
                 "patching_rect" : [ 375.500002920627594, 519.0, 50.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 2,
                 "parameter_enable" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "number",
                 "outlettype" : [ "", "bang" ],
                 "id" : "obj-34",
                 "patching_rect" : [ 323.500002920627594, 519.0, 50.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 2,
                 "parameter_enable" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "number",
                 "outlettype" : [ "", "bang" ],
                 "id" : "obj-32",
                 "patching_rect" : [ 375.500002920627594, 436.0, 50.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 2,
                 "parameter_enable" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "number",
                 "outlettype" : [ "", "bang" ],
                 "id" : "obj-30",
                 "patching_rect" : [ 323.500002920627594, 436.0, 50.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 2,
                 "parameter_enable" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "number",
                 "outlettype" : [ "", "bang" ],
                 "id" : "obj-28",
                 "patching_rect" : [ 271.500002920627594, 519.0, 50.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 2,
                 "parameter_enable" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "number",
                 "outlettype" : [ "", "bang" ],
                 "id" : "obj-21",
                 "patching_rect" : [ 271.500002920627594, 436.0, 50.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 2,
                 "parameter_enable" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "number",
                 "outlettype" : [ "", "bang" ],
                 "id" : "obj-17",
                 "patching_rect" : [ 167.500002920627594, 519.0, 50.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 2,
                 "parameter_enable" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "number",
                 "outlettype" : [ "", "bang" ],
                 "id" : "obj-14",
                 "patching_rect" : [ 167.500002920627594, 436.0, 50.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 2,
                 "parameter_enable" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "number",
                 "outlettype" : [ "", "bang" ],
                 "id" : "obj-27",
                 "patching_rect" : [ 115.500002920627594, 519.0, 50.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 2,
                 "parameter_enable" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "number",
                 "outlettype" : [ "", "bang" ],
                 "id" : "obj-24",
                 "patching_rect" : [ 115.500002920627594, 436.0, 50.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 2,
                 "parameter_enable" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "newobj",
                 "text" : "unjoin 16",
                 "outlettype" : [ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" ],
                 "id" : "obj-7",
                 "patching_rect" : [ 115.500002920627594, 386.0, 187.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 17
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "toggle",
                 "outlettype" : [ "int" ],
                 "id" : "obj-25",
                 "patching_rect" : [ 131.333337247371674, 31.0, 24.0, 24.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 1,
                 "parameter_enable" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "newobj",
                 "text" : "metro",
                 "outlettype" : [ "bang" ],
                 "id" : "obj-23",
                 "patching_rect" : [ 131.333337247371674, 64.0, 39.0, 22.0 ],
                 "numinlets" : 2,
                 "numoutlets" : 1
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "comment",
                 "text" : "integer",
                 "id" : "obj-22",
                 "patching_rect" : [ 568.0, 330.0, 150.0, 20.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "message",
                 "text" : "281 554 113 642 95 287 48 64 20 123 72 93 183 40 429 0",
                 "outlettype" : [ "" ],
                 "id" : "obj-20",
                 "patching_rect" : [ 224.0, 330.0, 340.0, 22.0 ],
                 "numinlets" : 2,
                 "numoutlets" : 1
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "comment",
                 "text" : "symbol",
                 "id" : "obj-18",
                 "patching_rect" : [ 568.0, 301.0, 150.0, 20.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "comment",
                 "text" : "ascii list",
                 "id" : "obj-15",
                 "patching_rect" : [ 568.0, 259.0, 150.0, 20.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "message",
                 "text" : "50 56 49 32 53 53 52 32 49 49 51 32 54 52 50 32 57 53 32 50 56 55 32 52 56 32 54 52 32 50 48 32 49 50 51 32 55 50 32 57 51 32 49 56 51 32 52 48 32 52 50 57 32 48",
                 "linecount" : 3,
                 "outlettype" : [ "" ],
                 "id" : "obj-12",
                 "patching_rect" : [ 224.0, 248.0, 342.0, 49.0 ],
                 "numinlets" : 2,
                 "numoutlets" : 1
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "message",
                 "text" : "\"281 554 113 642 95 287 48 64 20 123 72 93 183 40 429 0\"",
                 "outlettype" : [ "" ],
                 "id" : "obj-9",
                 "patching_rect" : [ 224.0, 301.0, 340.0, 22.0 ],
                 "numinlets" : 2,
                 "numoutlets" : 1
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "newobj",
                 "text" : "fromsymbol",
                 "outlettype" : [ "" ],
                 "id" : "obj-6",
                 "patching_rect" : [ 116.000002920627594, 340.0, 71.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 1
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "newobj",
                 "text" : "itoa",
                 "outlettype" : [ "int" ],
                 "id" : "obj-4",
                 "patching_rect" : [ 116.000002920627594, 290.0, 40.0, 22.0 ],
                 "numinlets" : 3,
                 "numoutlets" : 1
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "newobj",
                 "text" : "zl group 1000",
                 "outlettype" : [ "", "" ],
                 "id" : "obj-3",
                 "patching_rect" : [ 116.000002920627594, 248.0, 81.0, 22.0 ],
                 "numinlets" : 2,
                 "numoutlets" : 2
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "newobj",
                 "text" : "sel 13 10",
                 "outlettype" : [ "bang", "bang", "" ],
                 "id" : "obj-2",
                 "patching_rect" : [ 116.000002920627594, 196.670000000000016, 57.0, 22.0 ],
                 "numinlets" : 3,
                 "numoutlets" : 3
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "newobj",
                 "text" : "print data @popup 1",
                 "id" : "obj-13",
                 "patching_rect" : [ 278.0, 207.670000000000016, 117.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "message",
                 "text" : "print",
                 "outlettype" : [ "" ],
                 "id" : "obj-8",
                 "patching_rect" : [ 186.66667515039444, 112.666670024394989, 32.0, 22.0 ],
                 "numinlets" : 2,
                 "numoutlets" : 1
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "button",
                 "outlettype" : [ "bang" ],
                 "id" : "obj-5",
                 "patching_rect" : [ 131.333337247371674, 112.666670024394989, 24.0, 24.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 1,
                 "parameter_enable" : 0
             }
     }
 ,         {
             "box" :             {
                 "maxclass" : "newobj",
                 "text" : "serial c 9600",
                 "outlettype" : [ "int", "" ],
                 "id" : "obj-1",
                 "patching_rect" : [ 116.000002920627594, 151.333324432373047, 77.0, 22.0 ],
                 "numinlets" : 1,
                 "numoutlets" : 2
             }
     }
 ],
     "lines" : [         {
             "patchline" :           {
                 "source" : [ "obj-8", 0 ],
                 "destination" : [ "obj-1", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-7", 15 ],
                 "destination" : [ "obj-48", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-7", 14 ],
                 "destination" : [ "obj-46", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-7", 5 ],
                 "destination" : [ "obj-44", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-7", 4 ],
                 "destination" : [ "obj-42", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-7", 13 ],
                 "destination" : [ "obj-40", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-7", 12 ],
                 "destination" : [ "obj-38", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-7", 11 ],
                 "destination" : [ "obj-36", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-7", 9 ],
                 "destination" : [ "obj-34", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-7", 10 ],
                 "destination" : [ "obj-32", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-7", 8 ],
                 "destination" : [ "obj-30", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-7", 7 ],
                 "destination" : [ "obj-28", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-7", 1 ],
                 "destination" : [ "obj-27", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-7", 0 ],
                 "destination" : [ "obj-24", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-7", 6 ],
                 "destination" : [ "obj-21", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-7", 3 ],
                 "destination" : [ "obj-17", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-7", 2 ],
                 "destination" : [ "obj-14", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-6", 0 ],
                 "destination" : [ "obj-7", 0 ],
                 "order" : 1
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-6", 0 ],
                 "destination" : [ "obj-20", 1 ],
                 "order" : 0
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-5", 0 ],
                 "destination" : [ "obj-1", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-48", 0 ],
                 "destination" : [ "obj-50", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-46", 0 ],
                 "destination" : [ "obj-43", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-44", 0 ],
                 "destination" : [ "obj-55", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-42", 0 ],
                 "destination" : [ "obj-35", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-40", 0 ],
                 "destination" : [ "obj-51", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-4", 0 ],
                 "destination" : [ "obj-9", 1 ],
                 "order" : 0
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-4", 0 ],
                 "destination" : [ "obj-6", 0 ],
                 "order" : 1
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-38", 0 ],
                 "destination" : [ "obj-45", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-36", 0 ],
                 "destination" : [ "obj-52", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-34", 0 ],
                 "destination" : [ "obj-53", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-32", 0 ],
                 "destination" : [ "obj-37", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-30", 0 ],
                 "destination" : [ "obj-39", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-3", 0 ],
                 "destination" : [ "obj-4", 0 ],
                 "order" : 1
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-3", 0 ],
                 "destination" : [ "obj-12", 1 ],
                 "order" : 0
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-28", 0 ],
                 "destination" : [ "obj-54", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-27", 0 ],
                 "destination" : [ "obj-57", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-25", 0 ],
                 "destination" : [ "obj-23", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-24", 0 ],
                 "destination" : [ "obj-31", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-23", 0 ],
                 "destination" : [ "obj-5", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-21", 0 ],
                 "destination" : [ "obj-41", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-2", 0 ],
                 "destination" : [ "obj-3", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-2", 2 ],
                 "destination" : [ "obj-3", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-17", 0 ],
                 "destination" : [ "obj-56", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-14", 0 ],
                 "destination" : [ "obj-33", 0 ]
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-1", 0 ],
                 "destination" : [ "obj-2", 0 ],
                 "order" : 1
             }
     }
 ,         {
             "patchline" :           {
                 "source" : [ "obj-1", 0 ],
                 "destination" : [ "obj-13", 0 ],
                 "order" : 0
             }
     }
 ],
     "appversion" :  {
         "major" : 8,
         "minor" : 1,
         "revision" : 7,
         "architecture" : "x64",
         "modernui" : 1
     }
 ,
     "classnamespace" : "box"
 }

References:

McKellar, M., 2016. MAX Msp 7: Sending Data From Arduino Into Max. Available at: https://www.youtube.com/watch?v=6bT3G4Mep7E [Accessed 26 November 2020].

Watson, R., 2019. How To Send And Receive Data Between An Arduino And Max/MSP. [online] Maker Pro. Available at: https://maker.pro/arduino/tutorial/how-to-send-and-receive-data-between-an-arduino-and-maxmsp [Accessed 26 November 2020].

Docs.cycling74.com. n.d. Sel Object Reference – Max 7 Documentation. [online] Available at: https://docs.cycling74.com/max7/refpages/sel [Accessed 30 November 2020].

Docs.cycling74.com. n.d. Zl.Group Object Reference – Max 7 Documentation. [online] Available at: https://docs.cycling74.com/max7/refpages/zl.group [Accessed 30 November 2020].

Docs.cycling74.com. n.d. Itoa Object Reference – Max 7 Documentation. [online] Available at: https://docs.cycling74.com/max7/refpages/itoa?q= [Accessed 30 November 2020].

Docs.cycling74.com. n.d. Fromsymbol Object Reference – Max 7 Documentation. [online] Available at: https://docs.cycling74.com/max7/refpages/fromsymbol?q= [Accessed 30 November 2020].

Scott, T., 2013. Characters, Symbols And The Unicode Miracle – Computerphile. [online] Available at: https://www.youtube.com/watch?v=MijmeoH9LT4 [Accessed 3 December 2020].

Adding Additional Analog Inputs in Arduino – CD4051BE Multiplexing Chip

One of the immediate ‘issues’ I noticed with integrating Arduino into music production was the finite number of inputs. Though Arduinos have ample inputs for many uses, musical instruments often have hundreds of things to pluck at, manipulate, and record.

Recently, I built a sequencer by Look Mum No Computer (link) – utilising the Arduino to replace the timer chip in a baby-8 style analogue sequencer. Once I put it together, I realised that I actually wanted to manipulate the potentiometers’ outputs in code. The problem was that all 21 inputs on the Arduino nano were already in use. Whilst starting from scratch was not very reassuring, I realised going back to breadboarding gave more room to experiment.

What is Multiplexing?

Beyond buying a larger Arduino, my first thoughts surrounded adding more inputs! Multiplexers are integrated circuits similar to an automated selector switch (Ahmed and Spreadbury, 1984, p. 263), allowing a system to read inputs from several sensors in quick succession.​​​​​​​

Image

The CD4051BE chip on the breadboard

Wiring it up

Once I’d gotten my head around the abbreviations on the datasheet, the wiring wasn’t too complicated. It’s similar to wiring a potentiometer, but using the chip’s outputs instead of the Arduino to connect the central signal pin.

Image

From the CD4051B Datasheet

Common Output/Input (COM OUT/IN: Pin 3)
Connect to Arduino signal pin 0

Multiplexed Output/Input Channels (CHANNELS IN/OUT 0-7: Pins 1, 2, 4, 5, 12, 13, 14 & 15)
Connect to signal pin (centre) of respective potentiometers

Voltage Emmiter Emmiter, Negative Supply Voltage (VEE)
Not connected

Voltage Source Source, Negative Supply Voltage (VSS)
Connected to Ground

Voltage Drain Drain, Positive Supply Voltage (VDD)
Connected to 5V

A, B, C
Connected to Pins 8, 9 & 10 on the Arduino

Image

Breadboard Layout for this Multiplexer Project – Made by Me using Fritzing

The Arduino Code

The below code writes the output from each potentiometer to the serial, to verify that they are working correctly and for use in further coding!

Boby Bob’s code uses ‘int64_t’ and ‘uint8_t’to set a more specific constant than just using ‘int’ or ‘float’. Using ‘int64_t’ means that the constant is an integer with a bit width of exactly 64 (CPP Reference, n.d.). I don’t fully understand his reason for choosing these, so I have changed them both to ‘int’ as it was messing up the potentiometer outputs.

int wait= 10;
 void setup() 
 {
   Serial.begin(9600);
   pinMode(7,OUTPUT); //Inhibit set to off
   digitalWrite(7,LOW);
   DDRB = 0b00000111;  //PORT 8,9,10 as output
   PORTB = 0b00000000; //PORT 8,9,10 set to LOW
   pinMode(A0,INPUT);
 }
 void loop() 
 {
   PORTB=0b00000000; //Data flow - X0
   int Sensor0 =  analogRead(A0);//read data from X0
   Serial.println("Sensor0");
   Serial.println(Sensor0);
   delay(wait);
 PORTB=0b00000001;  //Data flow - X1
   int Sensor1 =  analogRead(A0); //read data from X1
   //Serial.println("Sensor1");
   //Serial.println(Sensor1);
   delay(wait);
 PORTB=0b00000010;  //Data flow - X2
   int Sensor2 =  analogRead(A0); //read data from X2
   //Serial.println("Sensor2");
   //Serial.println(Sensor2);
   delay(wait);
 PORTB=0b00000011;  //Data flow - X3
   int Sensor3 =  analogRead(A0); //read data from X3
   //Serial.println("Sensor3");
   //Serial.println(Sensor3);
   delay(wait);
 PORTB=0b00000100;  //Data flow - X4
   int Sensor4 =  analogRead(A0); //read data from X4
   //Serial.println("Sensor4");
   //Serial.println(Sensor4);
   delay(wait);
 PORTB=0b00000101;  //Data flow - X5
   int Sensor5 =  analogRead(A0); //read data from X5
   //Serial.println("Sensor5");
   //Serial.println(Sensor5);
   delay(wait);
 PORTB=0b00000110;  //Data flow - X6
   int Sensor6 =  analogRead(A0); //read data from X6
   //Serial.println("Sensor6");
   //Serial.println(Sensor6);
   delay(wait);
 PORTB=0b00000111;  //Data flow - X7
   int Sensor7 =  analogRead(A0); //read data from X7
   //Serial.println("Sensor7");
   //Serial.println(Sensor7);
   delay(wait);
 digitalWrite(7,HIGH);  // Inhibit / stop all data
   delay(wait);  
   digitalWrite(7,LOW);  // Inhibit /  let data flow
 }

The multiplexers inputs are read sequentially by sending binary code via pins A, B & C. These pins relate to the last three digits of the Arduino port B numbers, digital pins 8, 9, and 10.

This table’s codes tell the multiplexer which pins to read, sending eight values using binary. Inhibit is wired to digital pin seven, acting as a pause function (Bob, 2015).

Image

From the CD4051B Datasheet

Port B maps six digital pins, 8 to 13, plus two on the crystal pin. This is the string of eight binary digits in PORTB=0b00000111. Changing the first two bits may cause issues with the crystal.

DDRB accesses the Port B Data Direction Register – allowing the pins in port B to be set as inputs or outputs (Arduino – Port Registers, n.d.). PORTB sets them as high or low. Much more information on this can be found in the arduino.cc help guide referenced.

Multiplying Multiplexers

A single multiplexer chip takes up five pins (four digital, one analogue) to create eight. Besides ‘converting’ digital to analogue, this isn’t particularly impressive in efficiency. The benefits of multiplexing become more apparent as you add extra multiplexers to the circuit.

When adding further CD4051B chips, you can use the same digital pins on the Arduino to send multiple multiplexers’ binary messages. This starts to make multiplexing very pin efficient, each extra chip only requiring a single analogue pin.

Image
Image

You can have as many multiplexers as available analogue pins, an Arduino Uno with six analogue pins could hypothetically read 48 analogue inputs. It’s possible to even stack multiplexers inside of multiplexers. One disadvantage of multiplexers is the delay between each read – and stacking multiplexers would create even longer wait times.

Video Explanation

The Code (2)

With 16 inputs coming into the serial monitor it is becoming increasingly difficult to read. I need to address this so that I can neatly pull the numbers into Max.

I send the numbers as a list with spaces between each integer and a carriage return at the end. This tidies up the serial monitor and means that Max can read the list as 16 different numbers, using an unjoin object to separate them into useful values.

int wait = 10;
 const int A_Pots = A0;
 const int B_Pots = A1;
 const int Inhibit_Mux = 7;
 void setup() 
 {
   Serial.begin(9600);
   pinMode(Inhibit_Mux,OUTPUT);
   digitalWrite(Inhibit_Mux,LOW);
   DDRB = 0b00000111;  //PORT 8,9,10 as output
   PORTB = 0b00000000; //PORT 8,9,10 set to low
   pinMode(A_Pots,INPUT);
   pinMode(B_Pots,INPUT);
 }
 void loop() 
 {
   PORTB=0b00000000; //Data flow - X0
   int Step1A = analogRead(A_Pots); //read data from X0 A_Pots
   Serial.print(Step1A);
   int Step1B = analogRead(B_Pots); //read data from X0 B_Pots
   Serial.print(" ");
   Serial.print(Step1B);
   delay(wait);
 PORTB=0b00000001;  //Data flow - X1
   int Step2A = analogRead(A_Pots); //read data from X1 A_Pots
   Serial.print(" ");
   Serial.print(Step2A);
   int Step2B = analogRead(B_Pots); //read data from X1 B_Pots
   Serial.print(" ");
   Serial.print(Step2B);
   delay(wait);
 PORTB=0b00000010;  //Data flow - X2
   int Step3A = analogRead(A_Pots); //read data from X2 A_Pots
   Serial.print(" ");
   Serial.print(Step3A);
   int Step3B = analogRead(B_Pots); //read data from X2 B_Pots
   Serial.print(" ");
   Serial.print(Step3B);
   delay(wait);
 PORTB=0b00000011;  //Data flow - X3
   int Step4A = analogRead(A_Pots); //read data from X3 A_Pots
   Serial.print(" ");
   Serial.print(Step4A);
   int Step4B = analogRead(B_Pots); //read data from X3 B_Pots
   Serial.print(" ");
   Serial.print(Step4B);
   delay(wait);
 PORTB=0b00000100;  //Data flow - X4
   int Step5A = analogRead(A_Pots); //read data from X4 A_Pots
   Serial.print(" ");
   Serial.print(Step5A);
   int Step5B = analogRead(B_Pots); //read data from X4 B_Pots
   Serial.print(" ");
   Serial.print(Step5B);
   delay(wait);
 PORTB=0b00000101;  //Data flow - X5
   int Step6A = analogRead(A_Pots); //read data from X5 A_Pots
   Serial.print(" ");
   Serial.print(Step6A);
   int Step6B = analogRead(B_Pots); //read data from X5 B_Pots
   Serial.print(" ");
   Serial.print(Step6B);
   delay(wait);
 PORTB=0b00000110;  //Data flow - X6
   int Step7A = analogRead(A_Pots); //read data from X7 A_Pots
   Serial.print(" ");
   Serial.print(Step7A);
   int Step7B = analogRead(B_Pots); //read data from X7 B_Pots
   Serial.print(" ");
   Serial.print(Step7B);
   delay(wait);
 PORTB=0b00000111;  //Data flow - X7
   int Step8A = analogRead(A_Pots); //read data from X8 A_Pots
   Serial.print(" ");
   Serial.print(Step8A);
   int Step8B = analogRead(B_Pots); //read data from X8 B_Pots
   Serial.print(" ");
   Serial.println(Step8B);
   delay(wait);
 digitalWrite(Inhibit_Mux,HIGH);  // Inhibit / stop all data
   delay(wait);  
   digitalWrite(Inhibit_Mux,LOW);  // Inhibit /  let data flow
 }
This image has an empty alt attribute; its file name is screenshot-2020-11-28-at-09.06.01.png
Max Patch that can read the above code

References:

2003. CD4051BE Texas Instruments Datasheet. [online] Available at: https://datasheet.octopart.com/CD4051BE-Texas-Instruments-datasheet-7280354.pdf [Accessed 15 November 2020].

Bob, B., 2015. Analog Multiplexer Demultiplexer MC14051B Basic Introduction.. Available at: https://www.youtube.com/watch?v=Q2WMlluVXmo&feature=emb_title [Accessed 15 November 2020].

Ahmed, H. and Spreadbury, P. J., 1984. Analogue and Digital Electronics for Engineers: An Introduction, 2nd ed. Electronics Texts for Engineers and Scientists, Cambridge, Cambridge University Press.

Arduino.cc. n.d. Arduino – Port Registers. [online] Available at: https://www.arduino.cc/en/Reference/PortManipulation [Accessed 24 November 2020].

En.cppreference.com. n.d. Fundamental Types. [online] Available at: https://en.cppreference.com/w/cpp/language/types [Accessed 24 November 2020].

En.cppreference.com. n.d. Fixed Width Integer Types (Since C++11). [online] Available at: https://en.cppreference.com/w/cpp/types/integer [Accessed 24 November 2020].

Gammon, N., 2013. Microprocessors: 74HC4051 Multiplexer/Demultiplexer. [online] Gammon.com.au. Available at: http://www.gammon.com.au/forum/?id=11976 [Accessed 25 November 2020].

Battle, S., 2017. Arduino 8 Step Sequencer Keyboard. [online] LOOK MUM NO COMPUTER. Available at: https://www.lookmumnocomputer.com/projects#/sequencer-keyboard [Accessed 9 November 2020].

Building the Auduino Synthesiser (Arduino Based DIY Synth)

I started this project after spending a lot of time online staring at other peoples modular synthesisers. I ended up diving deep into electronics research, and decided that it would be fun (and cost-efficient) to try to build my own! This is the first module I have created, and it makes some gorgeous ambient sounds (which you can hear in my video).

The Auduino works by creating two triangle waves with pitch and decay knobs, which you turn and tune to form interesting harmonics. The singular knob to the left of my Auduino affects the frequency of the repetition of a grain of this noise – this means that you can tune it to a wide variety of pitches from deep bass growls to high pitched ambient squeals. The code which runs it is relatively simple, and very well noted, and therefore this makes a great project for someone perhaps looking into changing the code up too!

Parts List

Part#Notes
Stripboard1At least 17×20 dots, but more space means you could add more upgrades later
Arduino Nano1
WireI used recycled wire from inside an old cable
100k Potentiometers5Can be anywhere between 10-100k, just make sure they’re the same
6.35mm Audio Jacks4If you have a modular system that uses 3.5mm, those work fine too.
LEDs4I used purple, use whatever you like!
Light Dependent Resistors3
560Ohm Resistors4(I replaced the one on my external LED with a 4.7k resistor to dim it, as I used ultrabright LEDs)
Enclosure1I used a biscuit box, anything that fits the components in will work!
Solder

Building the Auduino

1. Breadboarding the Basic Circuit

Before I started trying to come up with a permanent version of the circuit, I made sure to test everything out on a breadboard. This allowed me to think about my CV controls, which first started as an experimental light theremin control.

– All the potentiometers are connected to 5v on one side, and ground on the other side.

– The centre pin on each potentiometer is connected to (respectively) Arduinos analog inputs 0, 1, 2, 3, and 4. 

– The audio out jack is connected to ground from its ground pin, and to the Arduinos digital out 3 from its signal pin(s).

– The LED is connected to ground from its ground pin, and to the Arduinos digital out 13 from it’s positive pin.

Image

2. Creating the Enclosure

To create the enclosure for my Auduino, all I did was drill some appropriately sized holes into a wooden box. I also used sticky back velcro to stick the stripboard to the bottom of the box, to stop it from rolling around during use. I also painted the top of the wood using acrylic paint, and 3D printed some ‘Moog style potentiometer knobs’ from Thingiverse.

You could use anything as your enclosure! I would be interested in seeing any cool experimental enclosures people come up with for this thing!

3. Making a homemade CV Vactrol

In my circuit, I have included some very basic DIY Vactrols to give this thing CV inputs. These isolate the circuit from the input, and stop any issues if you were to plug something into it with the wrong voltage somehow. They also add a bit of ‘ambient niceness’ to the CV control!

ImageFor this bit, you’ll need 3 of your LEDs, and your 3 Light Dependent Resistors (LDRs).

– If your LED has a curved top, you can file it flat using sandpaper or a nail file. This allows it to get more solid contact between it and the LDR when you stick them together.

– Place the two pieces facing each other, and wrap them up in electrical tape (or use heat shrink, if you’re more prepared). One tip is to cut the negative leg on the LED even shorter, so that you can tell which side of the vactrol is which.

– Try to make them as isolated as possible, so that no external light gets in.

The way which you add these to the circuit is to bridge them over the potentiometers. 

– The positive leg of the LDR receives power from the Arduino.

– The negative leg of the LDR goes to the same signal pin as the potentiometer you want it to affect (I chose the main grain frequency (Analog pin 0), and the two individual wave pitches (Analog pins 3 and 1)).

– The positive leg of the LED is connected to the signal pin on the audio jack, and will be powered by the CV that you put into it.

– The ground led of the LED is connected to the ground on its corresponding audio jack. (not the ground for the rest of the circuit, this is providing the isolation)

4. Wiring and Soldering

Once you have mounted everything into your enclosure, made your vactrols, and wired everything together – it is time to solder. I did this in sections so that I could remember what I was doing.

5. Playing your Synth!

Now, take some time to play with the awesome sounds that the Auduino can make!

Thanks so much for reading, there is further visual information in my youtube video – and also links to others work I found useful along this journey! If you need any further help, I am happy to answer any comments! 

Update 10th December 2020: When I built and explained this I seriously skimmed over the code, it’s really cleverly written. This write up by diyelectromusic is fantastic at breaking it down to its elements, definitely go and check it out if you’re interested in coding granular synthesis from scratch! https://diyelectromusic.wordpress.com/2020/12/09/auduino-granular-synthesis-part-2

References

AuthorLink
The Auduino Wiki (code here)code.google.com/archive/p/tinkerit/wikis/Auduino.wiki
Peter Knight first Demoing the Auduinovimeo.com/2944731
Notes and Voltsnotesandvolts.com/2014/07/build-auduino-granular-synth-part-1.html
Joshua AC Newmangithub.com/JoshuaACNewman/Auduino2
Joshua AC Newmanglyphpress.com/talk/2018/build-a-scion
Potentiometer Knobswww.thingiverse.com/thing:2563351