1 package net.sf.jack4j.examples;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.BufferedReader;
23 import java.io.IOException;
24 import java.io.InputStreamReader;
25 import java.util.EnumSet;
26
27 import net.sf.jack4j.AbstractJackTransportClient;
28 import net.sf.jack4j.JackException;
29 import net.sf.jack4j.JackPositionBit;
30 import net.sf.jack4j.JackTransportState;
31 import net.sf.jack4j.TransportPosition;
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 public class TimebaseMaster extends AbstractJackTransportClient {
55
56 private boolean timeReset = true;
57 private double bpm = 120;
58 private float signatureNumerator = 4;
59 private float signatureDenominator = 4;
60 private double ticksPerBeat = 96;
61
62
63
64
65
66
67
68
69
70
71 @Override
72 public void timebaseCallback(JackTransportState state, int nframes, TransportPosition pos, boolean newPos) {
73 if (newPos || timeReset) {
74
75
76 pos.setBeatsPerBar(signatureNumerator);
77 pos.setBeatType(signatureDenominator);
78 pos.setBeatsPerMinute(bpm);
79 pos.setTicksPerBeat(ticksPerBeat);
80
81 timeReset = false;
82 }
83
84
85
86 double minute = pos.getFrame() / (pos.getFrameRate() * 60.0);
87 double ticksPerMinute = pos.getBeatsPerMinute() * pos.getTicksPerBeat();
88 double realAbsoluteTick = minute * ticksPerMinute;
89 int absoluteTick = (int) realAbsoluteTick;
90 int absoluteBeat = (int) (absoluteTick / pos.getTicksPerBeat());
91
92 int barNumber = (int) (absoluteBeat / pos.getBeatsPerBar());
93 int beatNumber = (int) (absoluteBeat - (barNumber * pos.getBeatsPerBar()));
94 int tickNumber = (int) (absoluteTick - (absoluteBeat * pos.getTicksPerBeat()));
95
96 pos.setBar(barNumber + 1);
97 pos.setBeat(beatNumber + 1);
98 pos.setTick(tickNumber);
99 pos.setBarStartTick(barNumber * pos.getBeatsPerBar() * pos.getTicksPerBeat());
100
101 double bbtOffsetTicks = realAbsoluteTick - absoluteTick;
102 double bbtOffsetSecond = (bbtOffsetTicks / ticksPerMinute) * 60.0;
103 pos.setBbtOffset((int) (bbtOffsetSecond * pos.getFrameRate()));
104
105
106
107 pos.setValidBitsSet(EnumSet.of(JackPositionBit.POSITION_BBT, JackPositionBit.BBT_FRAME_OFFSET));
108
109 }
110
111 public TimebaseMaster(String clientName, boolean useExactName, boolean canStartServer, String serverName)
112 throws JackException {
113
114 super(clientName, useExactName, canStartServer, serverName);
115
116
117
118
119 setDefaultThreadInitCallback();
120
121
122 }
123
124
125
126
127 public void run() throws JackException, IOException {
128
129 activate();
130
131 BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
132
133 System.out.println("Issue commands (\"h\" for help)");
134 boolean cont = true;
135 while (cont) {
136 System.out.print("cmd> ");
137
138 String cmd = in.readLine();
139 cmd = cmd.trim();
140 if (cmd.isEmpty()) {
141 continue;
142 }
143 char cmdChar = cmd.charAt(0);
144
145 try {
146 switch (cmdChar) {
147 case 'q':
148 cont = false;
149 break;
150 case 'h':
151 System.out.println("(h) print this help");
152 System.out.println("(q) quit");
153 System.out.println("(m) become timebase master");
154 System.out.println("(r) release timebase");
155 System.out.println("(p) start transport");
156 System.out.println("(s) stop transport");
157 System.out.println("(b) set time signature");
158 System.out.println("(t) set tempo");
159 break;
160 case 'm':
161 System.out.println("Registering as timebase master");
162 setDefaultTimebaseCallback(false);
163 break;
164 case 'r':
165 System.out.println("Releasing timebase");
166 releaseTimebase();
167 break;
168 case 'p':
169 System.out.println("Starting transport");
170 startTransport();
171 break;
172 case 's':
173 System.out.println("Stopping transport");
174 stopTransport();
175 break;
176 case 'b':
177 setTimeSignature(in);
178 break;
179 case 't':
180 setTempo(in);
181 break;
182 default:
183 System.err.println("Unknown command: " + cmd);
184 }
185 } catch (JackException e) {
186 e.printStackTrace();
187 }
188 }
189
190
191 close();
192 }
193
194 private void setTempo(BufferedReader in) throws IOException {
195 System.out.print("Enter new tempo value (positive floating number): ");
196
197 String line = in.readLine();
198 double newBpm;
199 try {
200 newBpm = Double.parseDouble(line);
201 } catch (NumberFormatException e) {
202 e.printStackTrace();
203 return;
204 }
205
206 if (newBpm > 0) {
207 bpm = newBpm;
208
209 timeReset = true;
210 } else {
211 System.err.println("Invalid tempo (must be positive)");
212 }
213 }
214
215 private void setTimeSignature(BufferedReader in) throws IOException {
216 System.out.print("Enter new signature numerator: ");
217 String line = in.readLine();
218 float newSignatureNumerator;
219 try {
220 newSignatureNumerator = Float.parseFloat(line);
221 } catch (NumberFormatException e) {
222 e.printStackTrace();
223 return;
224 }
225
226 System.out.print("Enter new signature denominator: ");
227 line = in.readLine();
228 float newSignatureDenominator;
229 try {
230 newSignatureDenominator = Float.parseFloat(line);
231 } catch (NumberFormatException e) {
232 e.printStackTrace();
233 return;
234 }
235
236 signatureNumerator = newSignatureNumerator;
237 signatureDenominator = newSignatureDenominator;
238
239 timeReset = true;
240 }
241
242 public static void main(String[] args) throws Exception {
243 if (args.length != 1) {
244 throw new IllegalArgumentException("Expecting single parameter (client name)");
245 }
246 String clientName = args[0];
247
248 TimebaseMaster timebaseMaster = new TimebaseMaster(clientName, false, true, null);
249
250 timebaseMaster.run();
251 }
252 }