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.IOException;
23 import java.nio.ByteBuffer;
24 import java.util.EnumSet;
25
26 import net.sf.jack4j.AbstractJackClient;
27 import net.sf.jack4j.JackException;
28 import net.sf.jack4j.JackLocalMidiPort;
29 import net.sf.jack4j.JackLocalPort;
30 import net.sf.jack4j.JackMidiEvent;
31 import net.sf.jack4j.JackMidiPortBuffer;
32 import net.sf.jack4j.JackPortFlag;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 public class MidiSine extends AbstractJackClient {
50
51
52
53
54 private static float[] midiNoteFreq;
55 static {
56 midiNoteFreq = new float[128];
57 for (int i = 0; i < 127; i++) {
58 midiNoteFreq[i] = (float) ((440.0f / 32) * Math.pow(2, (i - 9) / 12.0f));
59 }
60 }
61
62 private JackLocalMidiPort midiIn;
63 private JackLocalPort audioOut;
64
65 private float currentPhase;
66 private int currentNote;
67 private float currentVolume;
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87 @Override
88 public int process(int bufferSize) throws Exception {
89
90 ByteBuffer outBuffer = audioOut.getByteBuffer(bufferSize);
91
92 outBuffer.clear();
93
94
95 JackMidiPortBuffer inBuffer = midiIn.initializeMidiBuffer(bufferSize);
96
97
98
99
100
101
102 int nextEventIndex = 0;
103 JackMidiEvent nextEvent = (inBuffer.getEventCount() > 0) ? inBuffer.getEvent(0) : null;
104
105 int sampleRate = getSampleRate();
106
107
108
109 for (int i = 0; i < bufferSize; i++) {
110 while ((nextEvent != null) && (nextEvent.getTime() == i)) {
111
112
113
114 byte[] data = nextEvent.getData();
115
116
117 if (data[0] == (byte) 0x80) {
118
119
120 if (currentNote == data[1]) {
121 System.out.println("Switching note OFF");
122 currentNote = -1;
123 }
124 } else if (data[0] == (byte) 0x90) {
125
126
127 currentNote = data[1];
128 currentPhase = 0.0f;
129 currentVolume = ((float) data[2]) / 0x7f;
130 System.out.println("Playing note " + currentNote + " at volume " + currentVolume);
131 }
132
133
134 nextEventIndex++;
135 nextEvent = (nextEventIndex < inBuffer.getEventCount()) ? inBuffer.getEvent(nextEventIndex) : null;
136 }
137
138
139 float sample;
140
141 if (currentNote < 0) {
142
143 sample = 0.0f;
144 } else {
145
146 sample = (float) (currentVolume * Math.sin(2 * Math.PI * currentPhase));
147
148
149 currentPhase += midiNoteFreq[currentNote] / sampleRate;
150 if (currentPhase > 1.0) {
151 currentPhase -= 1.0;
152 }
153 }
154
155
156 outBuffer.putFloat(sample);
157 }
158
159 return 0;
160 }
161
162 public MidiSine(String clientName, boolean useExactName, boolean canStartServer, String serverName)
163 throws JackException {
164
165 super(clientName, useExactName, canStartServer, serverName);
166
167
168
169
170 setDefaultThreadInitCallback();
171
172
173 setDefaultProcessCallback();
174
175
176 midiIn = addMidiPort("in", EnumSet.of(JackPortFlag.IS_INPUT));
177
178
179 audioOut = addAudioPort("out", EnumSet.of(JackPortFlag.IS_OUTPUT));
180
181 currentNote = -1;
182 currentPhase = 0.0f;
183
184
185 }
186
187
188
189
190 public void run() throws JackException {
191
192 activate();
193
194 System.out.println("CONNECT BOTH PORTS created by the client");
195 System.out.println("You can connect the output audio port to the soundcard to hear the sound,");
196 System.out.println("and connect MIDI keyboard (vkeybd, for example) to the input MIDI port.");
197
198
199 System.out.println("Press any key to quit.");
200 try {
201 System.in.read();
202 } catch (IOException e) {
203 e.printStackTrace();
204 }
205
206
207 close();
208 }
209
210 public static void main(String[] args) throws Exception {
211 if (args.length != 1) {
212 throw new IllegalArgumentException("Expecting single parameter (client name)");
213 }
214 String clientName = args[0];
215
216 MidiSine midiSine = new MidiSine(clientName, false, true, null);
217
218 midiSine.run();
219 }
220 }