Skip to content

Instantly share code, notes, and snippets.

@artfwo
Created October 28, 2024 11:24
Show Gist options
  • Save artfwo/01bb77bfc94c99c849c246e20d1ba747 to your computer and use it in GitHub Desktop.
Save artfwo/01bb77bfc94c99c849c246e20d1ba747 to your computer and use it in GitHub Desktop.
#! /usr/bin/env python3
def get_scale_note(scale, step, offset=0):
octave, scale_index = divmod(step, len(scale))
result = scale[scale_index]
offset_step = -1 if offset < 0 else 1
while offset != 0:
while True:
octave, result = divmod(result + offset_step + (octave * 12), 12)
if result not in scale:
break
offset = offset - offset_step
result += octave * 12
return result
import unittest
class TestScaleNote(unittest.TestCase):
def test_get_scale_note(self):
scale = [0, 2, 4, 5, 7, 9, 11]
self.assertEqual(get_scale_note(scale, 0), 0)
self.assertEqual(get_scale_note(scale, 3), 5)
self.assertEqual(get_scale_note(scale, 6), 11)
self.assertEqual(get_scale_note(scale, 7), 12)
self.assertEqual(get_scale_note(scale, 13), 23)
self.assertEqual(get_scale_note(scale, 14), 24)
self.assertEqual(get_scale_note(scale, -1), -1)
self.assertEqual(get_scale_note(scale, -4), -7)
self.assertEqual(get_scale_note(scale, -7), -12)
self.assertEqual(get_scale_note(scale, -8), -13)
def test_get_scale_note_offset(self):
scale = [0, 2, 4, 5, 7, 9, 11]
self.assertEqual(get_scale_note(scale, 3, -1), 3)
self.assertEqual(get_scale_note(scale, 3, -2), 1)
self.assertEqual(get_scale_note(scale, 3, -3), -2)
self.assertEqual(get_scale_note(scale, 10, 1), 18)
self.assertEqual(get_scale_note(scale, 10, 2), 20)
self.assertEqual(get_scale_note(scale, 10, 3), 22)
if __name__ == '__main__':
unittest.main()
@TriplEight
Copy link

I found your pres on BHNT #100 awesome! And this snippet is linked there.
Could you please describe how to play with it?

@artfwo
Copy link
Author

artfwo commented Oct 30, 2024

@TriplEight thanks! It depends on how familiar you are with music software in general and what's your target platform :)

For the most basic setup you could probably get a MIDI synth, such as SurgeXT.

Then maybe install python-rtmidi. It should show the synthesiser port if you run the following snippet:

import rtmidi

midiout = rtmidi.MidiOut()
print(midiout.get_ports())

You can then send notes to the port as described in this example:

https://github.com/SpotlightKid/python-rtmidi?tab=readme-ov-file#usage-example

If you'd like to snap them to a scale you can use this function as follows:

root = 36
scale = [0, 2, 4, 5, 7, 9, 11]

import time

for step in range(7):
    note = root + get_scale_note(scale, step)
    midiout.send_message([0x90, note, 127])
    time.sleep(0.5)
    midiout.send_message([0x80, note, 0])
    time.sleep(0.5)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment