-
Notifications
You must be signed in to change notification settings - Fork 0
/
celestialmusic.py
107 lines (89 loc) · 3.38 KB
/
celestialmusic.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import ephem
import abjad
import math
START_DATE = "1990/1/18"
END_DATE = "1990/2/18"
BARS_PER_DAY = 4
MAX_TUPLET = 7
DISTANCE_SCALE = 500
PITCH_MIN = -32
PITCH_MAX = 32
def main():
objects = (ephem.Mercury(), ephem.Venus(), ephem.Mars(), ephem.Jupiter(),
ephem.Saturn(), ephem.Uranus(), ephem.Neptune())
observer = ephem.Observer()
observer.lat = '57:10'
observer.lon = '-135:15'
object_events = []
for o in objects:
events = []
observer.date = START_DATE
while observer.date < ephem.Date(END_DATE):
next_rising = observer.next_rising(o)
next_setting = observer.next_setting(o)
events.append((next_rising, next_setting, o.earth_distance))
observer.date = next_setting
object_events.append(events)
staves = []
for object_events in object_events:
staff = abjad.Staff()
staves.append(staff)
current_time = ephem.Date(START_DATE)
for event_start, event_stop, distance in object_events:
# add rest to fill time until event start
rest_duration = get_duration_from_interval(event_start - current_time)
staff.extend(make_rests(rest_duration))
note_duration = get_duration_from_interval(event_stop - event_start)
staff.extend(make_notes(note_duration, get_pitch(distance)))
current_time = event_stop
abjad.show(abjad.Score(staves))
def get_duration_from_interval(days):
bars = days * BARS_PER_DAY
full_bars = math.floor(bars)
fraction = bars - full_bars
n, d = round_to_ratio(fraction)
return abjad.Duration(d * full_bars + n, d)
def round_to_ratio(fraction , max_denominator=MAX_TUPLET):
if fraction == 0:
return 0, 1
def get_all_ratios():
for denominator in range(1, max_denominator + 1):
for numerator in range(1, denominator + 1):
difference = abs(float(numerator) / denominator - fraction)
yield difference, numerator, denominator
diff, n, d = min(get_all_ratios(), key=lambda f: f[0])
return n, d
def make_rests(duration):
full_bars = int(math.floor(duration))
result = [abjad.Rest(1)] * full_bars
partial_bar = duration - full_bars
if partial_bar != 0:
notes = list(abjad.scoretools.make_notes(0, partial_bar))
if isinstance(notes[0], abjad.Tuplet):
change_notes_to_rests(notes[0])
else:
change_notes_to_rests(notes)
result += notes
return result
def make_notes(duration, pitch=0):
full_bars = int(math.floor(duration))
result = [abjad.Note(pitch, 1)] * full_bars
partial_bar = duration - full_bars
if partial_bar != 0:
notes = list(abjad.scoretools.make_notes(pitch, partial_bar))
result += notes
abjad.attach(abjad.Tie(), result)
return result
def change_notes_to_rests(container):
for i, note in enumerate(container):
container[i] = abjad.Rest(note.written_duration)
def get_pitch(distance):
# distance is in AU (mean distance from Earth to sun)
return round(distance * DISTANCE_SCALE) % abs(PITCH_MAX - PITCH_MIN) + PITCH_MIN
# first instrument per planet - dynamic according to distance
# then instruments from different locations on same planets
# the instrument on "polar"
# instrument by moon phase - pitch changes
# instrument of sun
if __name__ == "__main__":
main()