I have been trying to plot the twelve tone scale, from the lowest to the highest audible pitch, in hertz.
I found a way to do this using a while loop that is always true and then use break to exit the loop once the pitch is either lower or higher than the audible human range.
I know that this kind of loop is bad practice, but--as a novice programmer--I can't think of how to do this the proper way. How can I achieve the same result without using "while True"?
I have tried putting the condition "note > highest or note < lowest" where "True" is currently in the while loop, but then "note" is only defined within the loop so I get a "NameError: name 'note' is not defined" error.
highest = 20000
lowest = 20
key = 440
TET = 12
equal_temper = [key]
i = 1
while True:
note = key * (2**(1/TET))**i
if note > highest or note < lowest:
break
equal_temper.append(note)
i += 1
i = 1
while True:
note = key * (2**(1/TET))**-i
if note > highest or note < lowest:
break
equal_temper.append(note)
i += 1
equal_tempered = sorted(equal_temper)
for i in range(len(equal_temper)):
print(equal_tempered[i])
The output to the terminal seems to be correct. It may round down or up differently than other tables I have seen. I just want to know how to do this without using "while True."
I notice you're making your compiler re-make the same calculations over and over again while they only need to be done once! (And they can be done by you).
You have a set equation for your note value:
note = key * ( 2 ** (1/TET) ) ** i
. This can easily be inverted to solve fori
as a function ofnote
:i = TET * log_2(note / key)
. With this, and knowing your upper and lower bounds for note, we can determine the bounds fori
.Plugging in your upper limit,
20000
givesi=66.07
. Anything above this will result in a note greater than your upper limit, so we want to take the floor of this value.i_upper = 66
.Plugging in your lower limit,
20
, givesi=-53.51
. Anything lower than this will give a note lower than your lower limit, so we want to take the ceiling of this value.i_lower = -53
Now all we have to do is loop from
i_lower
toi_upper
and fill your array with the note values.This method avoids using while loops, is easily generalized if you change any of the parameters, and just through construction, returns a sorted array so you don't have to sort it afterwards.
Some notes:
Log(x)/Log(B)
gives log base B of X, no matter what the original base of Log is. This is why we divide bymath.log(2)
in ouri_value
function -- it gives us log base 2 ofN/key
.The letter
f
used right before a string indicates a format string and will replace anything within{}
with the whatever it evaluates to. For example,x=5; print(f"My x is {x}")
will printMy x is 5
. This shorthand feature is new to python 3.6. If you're using a version prior to python 3.6, then you will need to replace the format statements with the longer version:print("Number of notes above = {}".format(notes_above))
The very last print statement at the end uses
*
to 'unpack' the list ofequal_temper
notes and then prints them with a newline (\n
) between each element.You could simply write something like
That way you make the reverse of the condition in the if-statement the condition for the while loop and get rid of the hardcoded boolean value.
Edit: I have made some slight modifications to your code and came up with this:
When i run it on my machine, it produces the same output as your code. Can you please check?