All possible Tuning & Scale combinations

This thread is open for code review, improvements, ideas, or general discussion regarding the nature of the topic.

With classes Tuning & Scale working in tandem with each other, it seems natural that one would search for a way to achieve all possible combinations.

With rudimentary knowledge of how scales and temperaments work in theory, and while browsing the class sources, I noticed the only active restriction for Tuning & Scale combinations is a compatibility check ensuring .pitchesPerOctave for scale matches the .size of it’s tuning argument.

This amounts to a total of one-thousand and ninety-nine possible combinations, using only the core library.

Requests for feedback are mostly aimed towards improving & extending the code, discussion of best practices…or, anything regarding the process or experience of tuning SC.

((
    this.d_

    (
        Dictionary.fill

        (
            Tuning.names.size,

            {
                |index| 

                Tuning.names[index] ->

                Scale.names.select
                {
                    |scale|

                    Tuning.perform
                    (
                        Tuning.names[index]
                    )

                    .size === pitchesPerOctave

                    (
                        Scale.perform(scale)
                    )
                }

                .as(IdentitySet)

                .add(\chromatic)

                .as(SortedList)
            }
        )
    )

    .d

    .pairsDo

    {
        |t s|

        Post << Char.vtab <<< t

        << $\n << $\n <<<* s

        << $\n << $\n
    }

    .flatSize 
    
    .postln 
))

I’m kinda curious what the point of this is - how are you planning to use it?

An older project of mine used a similar function… allowing one to play any score in any scale, or infinitely cycle through all of them.

The purpose is to construct a foundation, where & depending on one’s preferred method, one is able to access everything available to them….directory methods only print to the post window.

1 Like

An updated & more concise version:

Tuning.names.collect
{ 
	|tuning| tuning -> 
	
	Scale.names.select
	{ 
		|scale| tuning.asTuning.size === 
		
		Scale.at(scale).pitchesPerOctave
	} 
	.as(IdentitySet).add('chromatic').as(SortedList)
} 
.asEvent

.asSortedArray.reverse.flatten.pairsDo 
{ 
	|t s| Post << $\n <<< t << $\n << $\n <<* s << $\n << $\n 
}

.asEvent.flatSize

Reverse posted in order to browse scrolling up.

OK, that makes sense - I don’t really work like this, so was intrigued to see what the point of having such an exhaustive list was!

Ah this is great, I’ve tried a couple of times to figure how to extract this sort of thing and failed.

Here’s a follow up function, to convert scales into frequency sets:

~scale =

{
	|root tuning scale octaves|
	
	Array.geom
	(
		octaves , 1 , octaveRatio ( tuning = tuning.asTuning )
	)
	
	*.x
	
	Array.fill
	(
		size ( scale = newFromKey( Scale , scale , tuning ) ),
		{
			|index| root * scale.ratios.at(index).round(0.001)
		}
	)
}

Let me know if there's any questions.
(((
    ~scale.(27, \catler, \iraq, 8) 

    .reshape (8, 7) // (octaves, size)

    .reverseDo ( _ . postln ) .flat .size
))) 

[ 3456.0, 3888.0, 4212.864, 4534.272, 4859.136, 5616.0, 6144.768 ]
[ 1728.0, 1944.0, 2106.432, 2267.136, 2429.568, 2808.0, 3072.384 ]
[ 864.0, 972.0, 1053.216, 1133.568, 1214.784, 1404.0, 1536.192 ]
[ 432.0, 486.0, 526.608, 566.784, 607.392, 702.0, 768.096 ]
[ 216.0, 243.0, 263.304, 283.392, 303.696, 351.0, 384.048 ]
[ 108.0, 121.5, 131.652, 141.696, 151.848, 175.5, 192.024 ]
[ 54.0, 60.75, 65.826, 70.848, 75.924, 87.75, 96.012 ]
[ 27.0, 30.375, 32.913, 35.424, 37.962, 43.875, 48.006 ]