(Teletype) The JI op

I first of all want to say I’m really happy this thing exists. This is opening the doors to a great way of exploring harmony and algorithmic composition.

These are some thoughts about the current implementation of the JI op and some suggestions for improvement.

Currently, JI A B will return a value based on the 1v/oct standard and 16-bit output scaling corresponding to A/(B*2^n), which is the frequency ratio of A to B shifted a number of octaves n downwards so that the returned value is smaller than an octave.

While the wrapping functionality can be useful in some cases, like when defining scales, where the ratios are that of primes wrapped down to fit within an octave (ie the third, 5/4, and the fifth, 3/2, (funny how their names have nothing to do with their ratios :stuck_out_tongue: )) I’ve found that it doesn’t allow to cover most use cases. If you want to generate an offset value for a modulating oscillator doing FM, and want, say a 7/1 ratio, you’d have to add back the octaves manually which is quite awkward, as JI 7 1 would actually return a value corresponding to 7/4. You’d have to do something like ADD JI 7 1 V 2. This also has the strange effect of having JI 7 1 = JI 7 2 = JI 7 4.

Making the JI op non-wrapping (or having two variations of the JI op, one wrapping, one non-wrapping) would solve these issues and make it’s use much more intuitive I think, because you’d get exactly what ratio you ask for. If you want to constrain a ratio to the range of an octave, it’s a matter of very simple mental math. For a fraction a/b which is initially greater than 2 (that is, a > 2*b), you will divide it by 2 until it is smaller than 2.

In my experience playing around with harmony and musical structures using ratios, it’s important to be aware of the octave shifts happening, because it has a very strong impact on the qualities it gives to a chord. With two notes in a 7/1 ratio, all of the higher note’s partials correspond to some of the lower note’s partials. Because of this the higher note is much less noticeable and strongly fused into the lower note. However, in a 7/4 ratio, only a small part of both notes’ partials are in common, which makes each stand out much more individually. I don’t think we should discard the ability to include octave relationships in ratios, because it’s a very powerful tool — and forcing you to be aware of it is a good thing.


The wrapping / octave constraint was intentional in the design (not my design, but I had the conversation with @Galapagoose on Github here). Might be worth a second op that doesn’t have the wrapping behavior.

1 Like

Then we are onto suggesting a name and getting approval I guess — who exactly should I reach out to?

I’d suggest using sub-ops (or whatever these are called, OP.THISTHINGHERE ), because we wouldn’t be taking up new op names and we’d unite two very similar functions under the same op. It’s then really a question of what we want the default behaviour to be. We’d either:

  • make the wrapping op JI.W or JI.O — Just Intonation Wrapping or Octave
  • make the non-wrapping op JI.NW or JI.F — Just Intonation Non-Wrapping or Full
1 Like

There’s a handful of developers, if something piques their interest they’ll pick it up and start working on solutions. You can see some of that happen in other threads. Everyone is a volunteer, so it might take a while. I’m interested in what you’re proposing as well, and I was on a path to fix JI, just got lapped by @Galapagoose.

Because the change you’re proposing is a relatively simple clone with modifications of an existing OP, it’s possible someone who’s new to TT dev and looking for a starter OP could pick it up and submit a pull request pretty quickly!

Best way to get things to happen in open source is to talk it up, find some like minded pals, build a block of support and then profit.


I realize this might be a fitting starter project for me — I’m just into my first semester of computer science + digital arts and know the basics object-oriented programming skills (started on Java, control flow, data types, classes, objects, privacy, static/non-static, passing, no fancy inheritance stuff yet). Would this get me past this? I don’t know GitHub but it’ll become necessary sooner or later, so I might as well learn it now. I already spotted the bit of code that needs to be removed to make the op non-wrapping, the biggest part of the challenge for me is defining the op properly and making the code for both variations as least redundant in good practice. Another thing worthy of note is that I don’t have a Teletype (yet), but that’ll inevitably happen soon. Are there ressources documenting the way Teletype is built up? How operators are declared, executed, etc?

About the redundancy — rather than duplicating all the code for each op, I think we could just make a function that takes the two parameters for JI plus a boolean indicating wether to wrap or not, encapsulating the whole algorithm, then have the two variations of JI call that function with their corresponding bool.

So, does this makes sense?


There’s a brief description in the README.md.

Here’s a commit that encompasses a set of operators. Reading that diff should demonstrate pretty clearly what needs to be changed.


Thank you! I’ll give it a look.

I’d encourage you to start by modifying the existing op to your liking. Once it’s working as you desire, then perhaps we can beta test it to decide whether there is any need for 2 separate ops (or an additional param). In general I think the Teletype mentality is about keeping things as simple & concise as possible, so I’d personally lean more toward a single op with a single functionality (even if it’s not the implementation I executed).

One small consideration - Teletype can’t produce negative CV, so ratios of less than 1:1 will unfortunately be clamped at 0V. Using JI for FM ratios is certainly interesting to me though (especially after reading this great thread: FM synthesis)

1 Like

I think the problem here is that adding an argument to JI will be a breaking change and should probably be disqualified on those grounds.

Totally. I’d prefer to change the behaviour of the current op, if it proves more useful.

1 Like

Since JI never worked, and is only fixed in master, we might be able to sneak in a breaking change without much issue.


Sounds good! I thing we can make it work in a single, simple op. We’ll see!

We can easily work around this by manually tuning our oscillator a few octaves down, then, within Teletype sum those octaves back with JI. Not ideal, but negative output from JI can still be relevant, which is great. It can also come handy when defining scales, too.

I think I’ll be ordering Teletype today — so I’ll be playing around with it this holidays :slight_smile: .


Bringing an update to this — I’ve rewritten the algorithm to be able return the ratios without octave wrapping, both positive and negative. Everything seems to work well in Java. My teletype didn’t come with the weird A to A USB cable, which is in the mail right now. Should try a build soon enough! I also can’t wait to get Teletype talking to Just Friends.


I’ve cloned @scanner_darkly’s branch and made the changes to the op code. Here it is on GitHub. Everything should be ok, but I’m not able to try a build as libavr32’s installer seems to have been killed by Atmel removing a file… I’ve searched for the missing file online but came back unsuccessful. Any help getting it to build or building it would be much appreciated!

i pulled in your remote and was able to build successfully.

at this point it’s probably best to use the main repository for something like this (otherwise it will have to become a part of my much larger pull request later which will make it more difficult to review properly. also i might be doing dangerous things to my branch as i’m getting it ready for a release, mostly due to my limited knowledge of git) could you clone monome/master instead, then make a new branch, make the changes there and submit a pull request against the main repo? and then once it’s merged i can pull it into my branch so you can use it together with the grid and/or er-301 support if needed.

also would you be able to fix this warning? i’ve been meaning to but since it’s part of the code you’re changing it’s easier to include it there.

../src/ops/maths.c: In function 'op_JI_get':
../src/ops/maths.c:474: warning: 'result' may be used uninitialized in this function

Thank you for your input! I didn’t quite know where to branch from. I’ll get to it now!

1 Like

20 characters of done!