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 }