# Week 6, lesson 2 - musical scales, including navigating them with waveforms

Time to look at musical scales. Moving around scales can be especially fun with waveforms, so there's a lot of focus on that. It's a bit fiddly because as I explain in the video, you have to convert between decimal and whole numbers with e.g. `floor <\$>`.. I'm going to have to look at making that easier in Tidal. Anyway, here's the video:

• 0:00 - intro
• 0:48 - Using sets of samples that are ready-tuned in scales (e.g. arpy)
• 02:59 - Random notes in a scale
• 03:30 - Random variation around a melody
• 03:57 - Motivating the 'scale' function
• 04:24 - Using the 'scale' function
• 04:55 - Shifting time to get different random streams
• 05:48 - Listing the available scales with scaleList
• 06:48 - Using waveforms to move around a scale
• 10:35 - Working outside of the scope of a 'scale'
• 12:40 - Patterning the range of a waveform
• 13:47 - Patterning the scale that's used
• 14:10 - Adding rhythmic structure with 'struct'
• 15:50 - Adding / multiplying waveforms together to make new waveforms
• 17:50 - Using 'off' to add some canon structure

And the worksheet:

``````-- The 'arpy' folder contains sounds sampled using a pentatonic
-- 'ritusen' scale, starting with 'c'. In this scale there are five
-- notes per octave.  So these are the same notes:
d1 \$ n "0 5" # sound "arpy"

d2 \$ n "0 12" # sound "superpiano"

-- Pentatonic scales like this are nice to work with because they all
-- sound good together. So if we add a random note to a melody, it
-- always sounds 'good':

d1 \$ n ("0 [7 2] 3 2" |+ irand 3) # sound "arpy"

-- This isn't really the case on the usual twelve-tone "equal
-- temperament" (12-TET) scale:
d1 \$ n ("0 [7 2] 3 2" |+ (irand 3)) # sound "superpiano"

-- 12-TET is the scale that pianos etc are normally tuned to in the west.

-- To use a different scale, we can use the "scale" function for converting
-- numbers from a different scale to 12-TET.
d1 \$ n (scale "ritusen" \$ "0 [7 2] 3 2" |+ (irand 3))
# sound "superpiano"

-- There's quite a few available:
scaleList

-- It's fun to use waveforms to pick notes from a scale. For example,
-- use a smooth sinewave to select notes from a minor scale:
d1 \$ segment 16 \$ n (scale "minor"
\$ floor <\$> (range 0 14 sine)
)
# sound "supersaw"
# legato 0.5
# lpf 1000 # lpq 0.1

-- Remember that waveforms don't have structure, so don't produce
-- events until you use something like 'segment', which in the example
-- above picks 16 notes per cycle.

-- There's also a complication that waveforms produce 'floating point'
-- decimal numbers, but scale only accepts 'integers' - whole numbers.
-- The 'floor <\$>' bit converts from decimal to whole numbers.  The
-- "range 0 14" bit converts from the usual range of 0 to 1 to the
-- given range of 0 to 14.

-- We can make this more exciting by patterning the range:
d1 \$ segment 16 \$ n (scale "minor"
\$ floor <\$> (range "<0 4 -8>" "<12 14 13 -13>" sine)
)
# sound "supersaw"
# legato 0.5
# lpf 1000 # lpq 0.1

-- And maybe even more exciting by using 'struct' to pattern the
-- rhythm using Euclidean syntax.. Taking the opportunity to pattern
-- the lpf (low pass filter) as well:
d1 \$ struct "t(<9 7>,16)"
\$ n (scale "minor"
\$ floor <\$> (range "<0 4 -8>" "<12 14 13 -13>" sine)
)
# sound "supersaw"
# legato 0.5
# lpf (range 400 5000 saw) # lpq 0.1

-- Using scales in this way allows us to play with movement while
-- still making tunes that make 'sense'. Here I add together
-- waveforms to create some longer-form movement:
d1 \$ segment 16 \$
n (scale "minor"
\$ floor <\$> (slow 2 \$ (slow 2 sine + slow 3 cosine) * "<6 -3>"
)
)
# sound "supersaw"
# legato 0.5
# lpf (range 400 5000 saw) # lpq 0.1

-- Back with the struct:
d1 \$ struct "t(<9 7>,16)" \$
n (scale "minor"
\$ floor <\$> (slow 2 \$ (slow 2 sine + slow 3 cosine) * "<6 -3>"
)
)
# sound "supersaw"
# legato 0.5
# lpf (range 400 5000 saw) # lpq 0.1

-- And with an 'off' going up an octave:
d1 \$ off 0.25 (|+ n 12) \$ struct "t(<9 7>,16)" \$ segment 16 \$
n (scale "minor"
\$ floor <\$> (slow 2 \$ (slow 2 sine + slow 3 cosine) * "<6 -3>"
)
)
# sound "supersaw"
# legato 0.5
# lpf (range 400 5000 saw) # lpq 0.1

-- Note that in the above the 'off' is outside of the 'scale'
-- function, So we're back in 12-TET land, so add '12' to go up an
-- octave, rather than the number of notes in the minor scale (7)
``````
6 Likes

Thanks @yaxu ! I've ran into strange issues running the code in your worksheet :

``````d1 \$ segment 16 \$ n (scale "minor"
\$ floor <\$> (range 0 14 sine)
)
# sound "supersaw"
# legato 0.5
# lpf 1000 # lpq 0.1
``````
``````<interactive>:60:9: error:
• Ambiguous type variable ‘a0’ arising from a use of ‘floor’
prevents the constraint ‘(RealFrac a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance Integral a => RealFrac (Ratio a) -- Defined in ‘GHC.Real’
instance [safe] RealFrac a => RealFrac (Pattern a)
-- Defined in ‘Sound.Tidal.Pattern’
instance RealFrac Double -- Defined in ‘GHC.Float’
``````

On Tidal 1.5.2, GHCi 8.0.2. I do wonder, should I update GHCi at some point ?

Ah interesting. ghc 8.0.2 is a few years old, but it's the first time I've seen a problem with it - tidal is tested against it before release. Is this is a version that came with a linux distribution?

Does this work?

``````d1 \$ segment 16 \$ n (scale "minor"
\$ floor <\$> (range 0 14 sine :: Pattern Double)
)
# sound "supersaw"
# legato 0.5
# lpf 1000 # lpq 0.1

``````
1 Like

Thanks, it does work indeed ! Does it confirm I should somehow update my GHCi ? That old version corresponds to my very very first installation of Tidal a few years ago already, version 0.8.0, I guess, on osx.This setup has worked flawlessly for me through these years until this issue.

Up to you - I think it would solve this issue, but if you're fine with adding this type signature then you probably won't see other problems.

Thanks for your kind support, I can live with it for now.

Is this the best way to select a key:

``````d1
\$ slow 2
\$ n (scale "dorian" "0 1 . 2 3 4 . 5 6 7"+ "a4")
# s "superpiano"
``````

Or is there a better/different way?

I'd use `|+` to make sure that the structure of the `a4` (one event per cycle) has bearing on the rhythmic structure of the result (it doesn't in this case, because it aligns perfectly with an event on the left).

``````d1
\$ slow 2
\$ n (scale "dorian" "0 1 . 2 3 4 . 5 6 7" |+ "a4")
# s "superpiano"
``````

I like to do this as a separate control, but that's down to taste:

``````d1
\$ slow 2
\$ n (scale "dorian" "0 1 . 2 3 4 . 5 6 7")
# s "superpiano"
|+ n "a4"``````
3 Likes

ah, yes - makes sense - thx!

Been having lots of fun going through these lessons!

I'm curious about the best way to approach pitch collections outside the pre-defined scales, such as from a pitch class set perspective. I'm actually curious about this apropos chords also, but I figured I'd ask here as I imagine it would be something similar? I tend to work with pitches in a sense that is often removed from strict adherence to a scale-based concept.

I think I probably already have some notions regarding moving groups notes around and manipulating them, but I was curious if there was anyone who already had similar thoughts in this area who was working this way, and if so, how?

thanks!

1 Like

I found getScale, which is very cool. It allowed me to make "scales" greater than one octave and 7 notes, which is right in line with what I was hoping to do.

2 Likes

Great! Sorry I have been super busy the last few days, please shout if you have more questions.

No problem, thanks!

I guess one thing that would be great would be if there was a corresponding type of function for chords, like “getChord”, where one could define user chords. I didn’t seem to see one, though. Maybe this is straying from the topic a little...