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.ArrayList;
26 import java.util.EnumSet;
27 import java.util.Iterator;
28 import java.util.List;
29
30 import net.sf.jack4j.AbstractJackClient;
31 import net.sf.jack4j.JackException;
32 import net.sf.jack4j.JackLocalMidiPort;
33 import net.sf.jack4j.JackMidiPortBuffer;
34 import net.sf.jack4j.JackPortFlag;
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 public class MidiSeq extends AbstractJackClient {
52
53
54
55
56 private static final class LoopNote {
57
58 public int noteNumber;
59 public int durationSamples;
60 public int pauseSamples;
61 }
62
63 private JackLocalMidiPort midiOut;
64 private List<LoopNote> loop;
65
66 private boolean running;
67 private Iterator<LoopNote> loopIterator;
68 private LoopNote currentLoopNote;
69 private boolean playingNote;
70 private int remainingSamples;
71
72
73
74
75
76
77
78
79
80
81
82
83 @Override
84 public int process(int bufferSize) throws Exception {
85 if (!running) {
86 return 0;
87 }
88
89
90 JackMidiPortBuffer outBuffer = midiOut.initializeMidiBuffer(bufferSize);
91
92 outBuffer.clearBuffer();
93
94 int sampleIdx = 0;
95 for (;;) {
96 if (remainingSamples < (bufferSize - sampleIdx)) {
97 sampleIdx += remainingSamples;
98
99
100
101 byte[] midiEventData;
102 if (playingNote) {
103
104 midiEventData = new byte[3];
105 midiEventData[0] = (byte) 0x80;
106 midiEventData[1] = (byte) currentLoopNote.noteNumber;
107 midiEventData[2] = 64;
108
109 playingNote = false;
110 remainingSamples = currentLoopNote.pauseSamples;
111 } else {
112
113 if (!loopIterator.hasNext()) {
114
115 loopIterator = loop.iterator();
116 }
117 currentLoopNote = loopIterator.next();
118
119 midiEventData = new byte[3];
120 midiEventData[0] = (byte) 0x90;
121 midiEventData[1] = (byte) currentLoopNote.noteNumber;
122 midiEventData[2] = 64;
123
124 playingNote = true;
125 remainingSamples = currentLoopNote.durationSamples;
126 }
127
128
129
130
131
132
133
134 System.out.println("Writing MIDI event, time " + sampleIdx + ", first byte is " + midiEventData[0]);
135 outBuffer.eventWrite(sampleIdx, midiEventData);
136 } else {
137
138 remainingSamples -= (bufferSize - sampleIdx);
139 break;
140 }
141 }
142
143
144 return 0;
145 }
146
147 public MidiSeq(String clientName, boolean useExactName, boolean canStartServer, String serverName)
148 throws JackException {
149
150 super(clientName, useExactName, canStartServer, serverName);
151
152
153
154
155 setDefaultThreadInitCallback();
156
157
158 setDefaultProcessCallback();
159
160
161 midiOut = addMidiPort("in", EnumSet.of(JackPortFlag.IS_OUTPUT));
162
163 loop = null;
164
165 running = false;
166
167
168 }
169
170
171
172
173 public void run() throws JackException, IOException {
174
175 activate();
176
177 BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
178
179 System.out.println("CONNECT THE OUTPUT PORT to some MIDI synthesizer.");
180 System.out.println("You can use MidiSeq example client from Jack4j package.");
181
182
183 loop = new ArrayList<LoopNote>();
184 for (;;) {
185 System.out.println("Enter MIDI note number (0-127) or enter to quit:");
186 String midiNoteStr = in.readLine();
187 if ("".equals(midiNoteStr)) {
188 break;
189 }
190 int midiNote;
191 try {
192 midiNote = Integer.parseInt(midiNoteStr);
193 } catch (NumberFormatException e) {
194 System.out.println(e.getMessage());
195 continue;
196 }
197 if ((midiNote < 0) || (midiNote > 127)) {
198 System.out.println("Invalid MIDI note");
199 continue;
200 }
201
202 System.out.println("Enter note duration in seconds (floating point number):");
203 String durationStr = in.readLine();
204 float duration;
205 try {
206 duration = Float.parseFloat(durationStr);
207 } catch (NumberFormatException e) {
208 System.out.println(e.getMessage());
209 continue;
210 }
211 if (duration <= 0) {
212 System.out.println("Duration must be positive number");
213 continue;
214 }
215
216 System.out.println("Enter pause duration in seconds (floating point number):");
217 String pauseDurationStr = in.readLine();
218 float pauseDuration;
219 try {
220 pauseDuration = Float.parseFloat(pauseDurationStr);
221 } catch (NumberFormatException e) {
222 System.out.println(e.getMessage());
223 continue;
224 }
225 if (pauseDuration < 0) {
226 System.out.println("Duration must be positive number");
227 continue;
228 }
229
230
231 LoopNote loopNote = new LoopNote();
232 loopNote.noteNumber = midiNote;
233 loopNote.durationSamples = (int) (duration * getSampleRate());
234 loopNote.pauseSamples = (int) (pauseDuration * getSampleRate());
235
236 loop.add(loopNote);
237 }
238
239 if (!loop.isEmpty()) {
240 loopIterator = loop.iterator();
241 currentLoopNote = null;
242 playingNote = false;
243 remainingSamples = 0;
244
245 running = true;
246
247
248 System.out.println("Playing; press ENTER to quit");
249 in.readLine();
250 }
251
252
253 close();
254 }
255
256 public static void main(String[] args) throws Exception {
257 if (args.length != 1) {
258 throw new IllegalArgumentException("Expecting single parameter (client name)");
259 }
260 String clientName = args[0];
261
262 MidiSeq midiSeq = new MidiSeq(clientName, false, true, null);
263
264 midiSeq.run();
265 }
266 }