-
Notifications
You must be signed in to change notification settings - Fork 46
Expand file tree
/
Copy pathsynthesis.py
More file actions
162 lines (139 loc) · 4.96 KB
/
synthesis.py
File metadata and controls
162 lines (139 loc) · 4.96 KB
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import pygame
from pygame.mixer import *
from pygame.locals import *
import math
import numpy as np
from Queue import Queue
import threading
from threading import Thread
import time
from KeyListener import KeyListener
import sys
from Tkinter import Tk
import entrytest
SUBDIVISIONS = 8
class Synth(object):
"""This class holds onto the state of our synthesizer. It is the M in our MVC.
This class has a list of "16th notes", which are lists of sounds to be played.
As it loops through the list, it adds any key presses to the list and places
any sounds already in the list onto the playing queue.
attributes: bpm, bars, click, sleep_time
"""
def __init__(self, bpm=120, bars=8, click=True):
"""This method initializes the synthesizer's bpm, the length of the loop,
its sleep time, and sets up the empty sample loop.
"""
self.bpm = bpm
self.bars = bars
self.click = click
self.sleep_time = 60.0/(bpm/4) / SUBDIVISIONS
self.count = 0
self.loop = [[] for sub in range(bars * SUBDIVISIONS)]
self.loop = [self.loop[beat] + ['Beep'] if beat % 4 == 0 and click else self.loop[beat] for beat in range(len(self.loop))]
self.playq = Queue()
# def frequencyMap(self, index):
# return 2**(index/12.0) * 440
def main(self):
"""This method keeps track of our position in the sample loop and puts
any samples it finds in the current subdivision into the sound playing
queue.
"""
for e in self.loop[self.count]:
# Note that queues will take tuples, but will break up strings.
self.playq.put((e,))
self.count += 1
self.count = self.count % (self.bars * SUBDIVISIONS)
class Viewer(object):
"""This class is the View part of our MVC model. It runs in its own separate
thread, waiting for sounds to appear in its queue. It then maps those sounds
to pygame Sound objects and plays them.
"""
def __init__(self, synth=None, filename='samplelist.txt'):
"""This method handles initializing this class's synth, and also prepares
the name-sound mapping for playing sounds.
"""
if synth == None:
synth = Synth()
self.synth = synth
pygame.mixer.init()
f = open(filename)
#preparing the sound map
self.soundmap = {line.strip('\n')[:-4]:Sound('Samples/' + line.strip('\n')) for line in f.readlines()}
print self.soundmap
def main(self):
"""This method interprets the play queue, and plays sounds unless it
receives a special exit tag.
"""
for sound in self.synth.playq.get():
if sound == ('exit'):
break
self.soundmap[sound].play()
if __name__ == '__main__':
"""This main function is where a lot of learning happened. We went through
many iterations to get the threading to work properly. First, everything is
initialized, and then three threads are started, one on each of the classes'
main methods. Conditionals in these loops serve to clean up when the
controller receives an exit event.
"""
#pygame initialization
pygame.init()
_display_surf = pygame.display.set_mode((46,45), pygame.HWSURFACE | pygame.DOUBLEBUF)
_running = True
img = pygame.image.load('speaker.bmp')
_display_surf.blit(img, pygame.Surface.get_rect(img))
#synthesizer initialization
d = entrytest.MyDialog(Tk())
a = Synth(*d.values())
b = KeyListener(a)
c = Viewer(a, 'samplelist.txt')
#thread target functions
def _synthstart():
print 'running SYNTH'
global _running
next_loop = time.time()
print a.sleep_time
while _running:
#handles processing time and drift
next_loop += a.sleep_time
a.main()
try:
time.sleep(next_loop - time.time())
except:
pass
print 'finished SYNTH'
def _keylistenerstart():
'running KEYLISTENER'
try:
global _running
while _running:
b.main()
except SystemExit:
_running = False
pygame.quit()
print 'finished KEYLISTENER'
def _viewerstart():
print 'running VIEWER'
global _running
while _running:
c.main()
print 'finished VIEWER'
# def _exit():
# print 'running EXIT'
# while True:
# for event in pygame.event.get():
# if event.type == pygame.QUIT:
# _running = False
# pygame.quit()
# sys.exit()
k = Thread(target=_keylistenerstart, name='KEYLISTENER')
v = Thread(target=_viewerstart, name='VIEWER')
s = Thread(target=_synthstart, name='SYNTH')
# exit = Thread(target=_exit, name='EXIT')
# exit.start()
s.start()
k.start()
v.start()
s.join()
v.join()
k.join()
# exit.join()