1
1
#!/usr/bin/env python3
2
2
"""A simple JACK timebase master."""
3
-
4
3
import argparse
5
- import sys
6
4
from threading import Event
7
5
8
6
import jack
@@ -12,31 +10,31 @@ class TimebaseMasterClient(jack.Client):
12
10
def __init__ (self , name , * , bpm = 120.0 , beats_per_bar = 4 , beat_type = 4 ,
13
11
ticks_per_beat = 1920 , conditional = False , debug = False , ** kw ):
14
12
super ().__init__ (name , ** kw )
15
- self .beats_per_bar = int ( beats_per_bar )
16
- self .beat_type = int ( beat_type )
13
+ self .beats_per_bar = beats_per_bar
14
+ self .beat_type = beat_type
17
15
self .bpm = bpm
18
16
self .conditional = conditional
19
17
self .debug = debug
20
- self .ticks_per_beat = int ( ticks_per_beat )
18
+ self .ticks_per_beat = ticks_per_beat
21
19
self .stop_event = Event ()
22
- self .set_shutdown_callback (self .shutdown )
20
+ self .set_shutdown_callback (self .shutdown_callback )
23
21
24
- def shutdown (self , status , reason ):
22
+ def shutdown_callback (self , status , reason ):
25
23
print ('JACK shutdown:' , reason , status )
26
24
self .stop_event .set ()
27
25
28
- def _tb_callback (self , state , nframes , pos , new_pos ):
26
+ def timebase_callback (self , state , nframes , pos , new_pos ):
29
27
if self .debug and new_pos :
30
- print (" New pos:" , jack .position2dict (pos ))
28
+ print (' New pos:' , jack .position2dict (pos ))
31
29
32
30
# Adapted from:
33
31
# https://github.com/jackaudio/jack2/blob/develop/example-clients/transport.c#L66
34
32
if new_pos :
35
- pos .beats_per_bar = float ( self .beats_per_bar )
33
+ pos .beats_per_bar = self .beats_per_bar
36
34
pos .beats_per_minute = self .bpm
37
- pos .beat_type = float ( self .beat_type )
38
- pos .ticks_per_beat = float ( self .ticks_per_beat )
39
- pos .valid = jack ._lib . JackPositionBBT
35
+ pos .beat_type = self .beat_type
36
+ pos .ticks_per_beat = self .ticks_per_beat
37
+ pos .valid = jack .POSITION_BBT
40
38
41
39
minutes = pos .frame / (pos .frame_rate * 60.0 )
42
40
abs_tick = minutes * self .bpm * self .ticks_per_beat
@@ -45,9 +43,11 @@ def _tb_callback(self, state, nframes, pos, new_pos):
45
43
pos .bar = int (abs_beat / self .beats_per_bar )
46
44
pos .beat = int (abs_beat - (pos .bar * self .beats_per_bar ) + 1 )
47
45
pos .tick = int (abs_tick - (abs_beat * self .ticks_per_beat ))
48
- pos .bar_start_tick = pos .bar * self .beats_per_bar * self .ticks_per_beat
46
+ pos .bar_start_tick = (
47
+ pos .bar * self .beats_per_bar * self .ticks_per_beat )
49
48
pos .bar += 1 # adjust start to bar 1
50
49
else :
50
+ assert pos .valid & jack .POSITION_BBT
51
51
# Compute BBT info based on previous period.
52
52
pos .tick += int (nframes * pos .ticks_per_beat *
53
53
pos .beats_per_minute / (pos .frame_rate * 60 ))
@@ -62,70 +62,68 @@ def _tb_callback(self, state, nframes, pos, new_pos):
62
62
pos .bar_start_tick += pos .beats_per_bar * pos .ticks_per_beat
63
63
64
64
if self .debug :
65
- print (" Pos:" , jack .position2dict (pos ))
65
+ print (' Pos:' , jack .position2dict (pos ))
66
66
67
67
def become_timebase_master (self , conditional = None ):
68
- return self . set_timebase_callback ( self . _tb_callback , conditional
69
- if conditional is not None
70
- else self .conditional )
68
+ if conditional is None :
69
+ conditiona = self . conditional
70
+ return self .set_timebase_callback ( self . timebase_callback , conditional )
71
71
72
72
73
73
def main (args = None ):
74
74
ap = argparse .ArgumentParser (description = __doc__ )
75
75
ap .add_argument (
76
76
'-d' , '--debug' ,
77
77
action = 'store_true' ,
78
- help = "Enable debug messages" )
78
+ help = 'enable debug messages' )
79
79
ap .add_argument (
80
80
'-c' , '--conditional' ,
81
81
action = 'store_true' ,
82
- help = "Exit if another timebase master is already active" )
82
+ help = 'exit if another timebase master is already active' )
83
83
ap .add_argument (
84
84
'-n' , '--client-name' ,
85
85
metavar = 'NAME' ,
86
86
default = 'timebase' ,
87
- help = " JACK client name (default: %(default)s)" )
87
+ help = ' JACK client name (default: %(default)s)' )
88
88
ap .add_argument (
89
89
'-m' , '--meter' ,
90
90
default = '4/4' ,
91
- help = "Meter as <beats-per-bar>/<beat-type> (default: %(default)s)" )
91
+ help = 'meter as <beats-per-bar>/<beat-type> (default: %(default)s)' )
92
92
ap .add_argument (
93
93
'-t' , '--ticks-per-beat' ,
94
94
type = int ,
95
95
metavar = 'NUM' ,
96
96
default = 1920 ,
97
- help = "Ticks per beat (default: %(default)s)" )
97
+ help = 'ticks per beat (default: %(default)s)' )
98
98
ap .add_argument (
99
99
'tempo' ,
100
100
nargs = '?' ,
101
101
type = float ,
102
102
default = 120.0 ,
103
- help = "Tempo in beats per minute (0.1-300.0, default: %(default)s)" )
103
+ help = 'tempo in beats per minute (default: %(default)s)' )
104
104
105
105
args = ap .parse_args (args )
106
106
107
107
try :
108
- beats_per_bar , beat_type = (int (x ) for x in args .meter .split ('/' , 1 ))
109
- except (TypeError , ValueError ):
110
- print ("Error: invalid meter: {}\n " .format (args .meter ))
111
- ap .print_help ()
112
- return 2
108
+ beats_per_bar , beat_type = map (float , args .meter .split ('/' ))
109
+ except ValueError :
110
+ ap .error ('Invalid meter: ' + args .meter )
113
111
114
112
try :
115
113
tbmaster = TimebaseMasterClient (
116
114
args .client_name ,
117
- bpm = max ( 0.1 , min ( 300.0 , args .tempo )) ,
115
+ bpm = args .tempo ,
118
116
beats_per_bar = beats_per_bar ,
119
117
beat_type = beat_type ,
120
118
ticks_per_beat = args .ticks_per_beat ,
121
119
debug = args .debug )
122
120
except jack .JackError as exc :
123
- return " Could not create timebase master JACK client: {}" .format (exc )
121
+ ap . exit ( ' Could not create timebase master JACK client: {}' .format (exc ) )
124
122
125
123
with tbmaster :
126
124
if tbmaster .become_timebase_master (args .conditional ):
127
125
try :
128
- print (" Press Ctrl-C to quit..." )
126
+ print (' Press Ctrl-C to quit...' )
129
127
tbmaster .stop_event .wait ()
130
128
except KeyboardInterrupt :
131
129
print ('' )
@@ -136,8 +134,8 @@ def main(args=None):
136
134
# another JACK client might have grabbed timebase master
137
135
pass
138
136
else :
139
- return " Timebase master already present. Exiting..."
137
+ ap . exit ( ' Timebase master already present. Exiting...' )
140
138
141
139
142
140
if __name__ == '__main__' :
143
- sys . exit ( main () or 0 )
141
+ main ()
0 commit comments