[sharing] Explore scala alternative tunings in Max with sslider

tmhglnd's icon

Hi all!

I would like to share this small library of abstractions that hopefully make the usage of alternative/microtonal tunings more accessible! It uses the library of 5000+ scala tuning files from the Stichting Huygens-Fokker, and you can also import your own.

The package contains 3 abstractions and a custom jsui file:

  • th.scala - Load a scala scale into a dictionary, refered to by other objects to easily switch scales for multiple scalaToFrequency converters (stof)

  • th.stof - Convert a number from scala scale-format to the corresponding frequency based on the tune, center and cents value.

  • th.stof~ - Similar as the th.stof object, except it works in the signal domain.

  • th.sslider.js - A custom jsui file that shows a kslider-like object to allow for playing with the custom tuning files. The keys are displayed side-by-side (no black keys), and their width denote the relative interval. A light-gray key denotes the octaves.

Works very nicely with the lately released Leimma platform by Khyam Allami and Counterpoint: https://isartum.net/leimma

Feedback/suggestions/bugs are very much welcome, here as a comment or in the github issues page.

Screenshot of the abstractions help-files

Max Gardener's icon

Wow. Thanks!

Mark Durham's icon

Going to check this out at some point - thanks!

tmhglnd's icon

One extra object has been included: th.ftomb (Frequency to Midi & Bend)

This object converts any frequency and outputs an integer MIDI note number together with a pitch bending value between 0 - 127 to allow for controlling external midi devices via [noteout] and [bendout] or [midiformat] and [midiout].

th.ftomb connect to noteout and bendout

mp's icon

Hi Timo,

great tool but it seems that the new version (8.2 beta) has broken something in the code.

Opening th.scala.maxhelp, the console returns a series of errors related to the js code:
js: error calling function loadScala [th.scala.js]
js: th.scala.js: Javascript TypeError: f is null, line 120
js: error calling function getmenu [th.scala.js]
js: error calling function loadScala [th.scala.js]
js: th.scala.js: Javascript TypeError: f is null, line 120
js: error calling function getmenu [th.scala.js]

Thanks.

mati

tmhglnd's icon

Hi! Thanks for letting me know. Have not tried the objects with the 8.2 beta, but will look into this soon and see what is going on.

tmhglnd's icon

Hi Mati, I am not getting these errors, so I'm wondering what the problem is. Are you on windows or mac? Did it work in the 8.1 version of Max? Did you put the package in the max searchpath? It looks like it is not able to find the /scl files that are shipped with the package. Can you try to click (getmenu) message and see if you still get the error? Can you try to just load a single .scl file from the /scl folder with the [opendialog]? Let me know :)

mp's icon

Hi Timo, thanks for the quick reaction!
I'm on Mac and it's weird… With version 8.1 it works flawlessly and yes, the package is in a recognized directory.
Using 'getmenu' or loading a single file returns the same error.

A short video explains the behaviour.

tmhglnd's icon

Thanks for the video. I will have to investigate a bit deeper, but it seems that maybe the reading/loading of files via JS has changed in some way. Do you get any errors if you try from the menubar Help>Examples>javascript>file both `filetester` and the `folderiter` patches?

mp's icon

No errors if I load the files you indicated.

tmhglnd's icon

Just to be sure, also no errors if you click those message boxes with (readfile) (c74:/init) etc?

mp's icon

Exactly. No errors.

Thanks!

tmhglnd's icon

Hey, I could replicate the bug after I downloaded the latest 8.2 beta from the forum (I had one version before I think). It seemed to have something to do with the .readbytes() method that I used in the js. I changed it to a different approach and now the bug should be fixed, please clone the latest commit from the github and give it a try: https://github.com/tmhglnd/th.scala

mp's icon

Hi Timo,
It runs again with the latest build.

Thanks for the quick reaction.

tmhglnd's icon

Great! Good to hear.

mp's icon

Hi,
I've run into another problem.
When randomly selecting files, Max would sometimes crash (the very last beta). At least I found a file that crashes every time: bell_mt_partials.scl
Are you experiencing it, too?

mp's icon

I'm not so JS savvy to unearth the eventual problem....
I baste a rough patch to load in dict a .scl file without js.

Max Patch
Copy patch and select New From Clipboard in Max.

tmhglnd's icon

I get this crash as well, but I also get this crash when trying to open it with the js file example, so not sure why this is. I'll investigate. Great that you've got a patch version that works in the meantime.

Dan Nigrin's icon

Just stumbled onto this and would like to express my thanks - very helpful, thank you Timo!

yaniki's icon

It’s brilliant.

Roman Thilenius's icon


timo, pls could you explain this?

why not the full range?

was the idea that a pb range of 2 semitones is more likely default than a range of 1 in synthesizers´ presets?

i am about to make a decision if i should eventually use 0 as center and then only pitch up or use a center in my version. (to avoid having two different halves around the center.)

tmhglnd's icon

@Dan/@Yaniki, thanks! If you have any feedback on the user experience let me know!

@Roman: Yes, the idea was that because -2/+2 semitones pb range is custom for most midi devices the user does not really have to change anything in the receiving devices settings. On the other hand of course the full range would give more precision. I could also include an MSB/LSB output option, making the range more precise. But since [midiformat] also has an @hires 1 option, allowing floating-point input, I thought using that option is more than enough for this object.

Roman Thilenius's icon


i came up with that question because if you only use half the range you could as well only use the positive or negative part.

it is interesting that i have never thought about the range contained in most presets (i had a lot of hardware back in the days)

but i just figured that your stuff are not externals but abstractions so maybe i will just look inside. :P

my (very old) approach looks like that (but there is an error in the mapping, i assumed 128 beeing the highest number which makes the whole range splitting procedure pointless^^) - and it is also 7 bit only (for the same reason: i have never investigated how common 14 bit actually is among hardware)

- mtof
- div/rest
- round integer (also missing here? not good. what did i do.)
- split rest into 0-64 and 64-127
- map these two parts from linear to the "center 64 problem"
- round PB to int

but maybe it would be more perfect to provide different modes from 1 to 12 semitones? hmm...




tmhglnd's icon

I personally also don't know how common 14-bit is amongst hardware. I made this object more for others (i think it was even a request) then that I actually use it myself.

You can definitely look at my code, it is al done with codebox in gen.

Mark Durham's icon

This is amazing - thank you so much Timo

edit - (and yes it did take me that long to check it out - looking at my post above ; )