-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbattery.py
executable file
·130 lines (102 loc) · 4.15 KB
/
battery.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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/env python
import curses, argparse, sys, time, threading, itertools
import pygame
import cui
from utils import load_banks
VERSION = 0.3
# That's all what MaKeyMaKey has in stock setting, except 'SPC' and 'w'
AVAILABLE_KEYS = 'LEFT RIGHT DOWN UP a s d f g h j'.split()
KEYS = dict(
[(getattr(curses, 'KEY_%s' % key, ord(key[0])), key) for key in AVAILABLE_KEYS])
def parse_args():
parser = argparse.ArgumentParser('Battery - a simple CLI & headless rompler')
parser.add_argument('-b', '--bank-kit', action='store', dest='bank_kit', default='default')
return parser.parse_args()
def init_mixer():
# We need to init mixer before pygame initializations, smaller buffer should avoid lags
pygame.mixer.pre_init(frequency=44100, size=-16, channels=2, buffer=512)
pygame.init()
pygame.mixer.set_num_channels(8 * len(KEYS)) # Get 8 channels for each key
class LoopThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.loop = []
self.running = False
def run(self):
try:
self.loop.next()[1].play()
except StopIteration:
return
self.running = True
while self.running:
sleep_time, sample = self.loop.next()
time.sleep(sleep_time)
sample.play()
def stop(self):
self.running = False
if __name__ == "__main__":
init_mixer()
args = parse_args()
banks_iter = load_banks(args.bank_kit)
if not banks_iter:
sys.exit('Can\'t load bank kit file "%s"' % args.bank_kit)
bank_desc, bank_samples, bank_nr = banks_iter.next()
cui = cui.CUI(VERSION)
cui.show_bank(bank_desc, bank_nr)
reverse, loop_recording, loops = False, False, []
t_prev, current_loop = 0, None
while True:
event = cui.screen.getch()
t = time.time()
if event in (ord('q'), 27): #q or ESC
break
elif event == ord(' '): # switch bank on SPACE
bank_desc, bank_samples, bank_nr = banks_iter.next()
cui.show_bank(bank_desc, bank_nr)
continue
elif event == ord('r'): # start/stop recording new loop
if loop_recording is False: #start new loop
current_loop = LoopThread()
current_loop.daemon = True
loops.append(current_loop)
else:
try:
dummy = current_loop.loop[-1]
except IndexError:
del(loops[-1])
loop_recording = not loop_recording
current_loop = None
cui.tray_msg('recording: %s' % ('on' if loop_recording else 'off'), style=curses.A_BOLD)
continue
# set the wait "before first sample" to time between last "sound" and "end recording"
current_loop.loop[0][0] = t - t_prev
current_loop.loop = itertools.cycle(current_loop.loop)
current_loop.start()
loop_recording = not loop_recording
cui.tray_msg('recording: %s' % ('on' if loop_recording else 'off'), style=curses.A_BOLD)
elif event == ord('p'): # stop & delete last loop
try:
loops[-1].stop()
del(loops[-1])
cui.tray_msg('last loop stopped & deleted', style=curses.A_BOLD)
except IndexError:
loops = []
elif event == ord('w'):
reverse = not reverse
cui.tray_msg('reverse mode: %s' % ('on' if reverse else 'off'), style=curses.A_BOLD)
try:
key = KEYS[event]
try:
sample = bank_samples[key][int(reverse)]
sample.play()
if loop_recording:
current_loop.loop.append([t-t_prev, sample])
t_prev = t
except KeyError:
cui.tray_msg('No sample defined for "%s" key\n' % key, row=1, style=curses.A_DIM)
except KeyError:
pass
# This should somehow restore terminal back, but it doesn't work all the time.
# Call "reset" in your shell if you need to
curses.endwin()
sys.exit(0)