/
ExampleGarden.java
139 lines (119 loc) · 5.42 KB
/
ExampleGarden.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
* Copyright 2009,2011 Reality Jockey, Ltd.
* info@rjdj.me
* http://rjdj.me/
*
* This file is part of ZenGarden.
*
* ZenGarden is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ZenGarden is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with ZenGarden. If not, see <http://www.gnu.org/licenses/>.
*
*/
package me.rjdj.zengarden;
import java.io.File;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;
/**
* <code>ExampleGarden</code> is a a simple test class used to quickly test <code>ZenGarden</code>.
* It contains a <code>main()</code> method and is not instantiable. Invoke the <code>main()</code>
* method with:<br>
* <pre>java -jar ZenGarden.jar path/to/zengarden/pd-patches/simple-osc.pd</pre>
* Some patches may require microphone input. Make sure that your computer has one attached.
*
* @author Martin Roth (mhroth@rjdj.me)
*/
public class ExampleGarden {
// A huge block size is selected because the the Java Sound API is very slow on most platforms.
private static final int BLOCK_SIZE = 2048;
// 22050 Hz is chosen (somewhat arbitrarily) because this is the sample rate that RjDj runs at.
private static final int SAMPLE_RATE = 44100;
private static final int NUM_INPUT_CHANNELS = 2;
private static final int NUM_OUTPUT_CHANNELS = 2;
private static volatile boolean shouldContinuePlaying = true;
private ExampleGarden() {
// no instances of ExampleGarden allowed
}
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("java ExampleGarden testpatch.pd");
} else {
// install a shutdown hook so that JVM shutdown (e.g., via CTRL-C) can be reacted to
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
shouldContinuePlaying = false;
}
});
// configure the audio input and output lines
AudioFormat inputAudioFormat = new AudioFormat(
SAMPLE_RATE, 16, NUM_OUTPUT_CHANNELS, true, true);
AudioFormat outputAudioFormat = new AudioFormat(
SAMPLE_RATE, 16, NUM_INPUT_CHANNELS, true, true);
DataLine.Info inputLineInfo = new DataLine.Info(TargetDataLine.class, inputAudioFormat);
DataLine.Info outputLineInfo = new DataLine.Info(SourceDataLine.class, outputAudioFormat);
short[] inputBuffer = new short[BLOCK_SIZE * NUM_INPUT_CHANNELS];
short[] outputBuffer = new short[BLOCK_SIZE * NUM_OUTPUT_CHANNELS];
byte[] bInputBuffer = new byte[inputBuffer.length * 2]; // 2 bytes per sample
byte[] bOutputBuffer = new byte[outputBuffer.length * 2];
TargetDataLine targetDataLine = null;
SourceDataLine sourceDataLine = null;
try {
// TODO(mhroth): ensure that stereo input and output lines are actually being returned
// by the system.
// open the audio input (line-in or microphone)
targetDataLine = (TargetDataLine) AudioSystem.getLine(inputLineInfo);
targetDataLine.open(inputAudioFormat, bInputBuffer.length);
targetDataLine.start();
// open the audio output
sourceDataLine = (SourceDataLine) AudioSystem.getLine(outputLineInfo);
sourceDataLine.open(outputAudioFormat, bOutputBuffer.length);
sourceDataLine.start();
} catch (LineUnavailableException lue) {
lue.printStackTrace(System.err);
System.exit(1);
}
// Create the context and graph based on the Pd patch
ZGContext zgContext = new ZGContext(NUM_INPUT_CHANNELS, NUM_OUTPUT_CHANNELS,
BLOCK_SIZE, (float) SAMPLE_RATE);
zgContext.addListener(new ZenGardenAdapter());
ZGGraph zgGraph = zgContext.newGraph(new File(args[0]));
zgGraph.attach();
while (shouldContinuePlaying) {
// run the patch in an infinite loop
targetDataLine.read(bInputBuffer, 0, bInputBuffer.length); // read from the input
// convert the byte buffer to a short buffer
for (int i = 0, j = 0; i < inputBuffer.length; i++) {
inputBuffer[i] = (short) (((int) bInputBuffer[j++]) << 8);
inputBuffer[i] |= ((short) bInputBuffer[j++]) & 0x00FF;
}
zgContext.process(inputBuffer, outputBuffer);
// convert short buffer to byte buffer
for (int i = 0, j = 0; i < outputBuffer.length; i++) {
bOutputBuffer[j++] = (byte) ((outputBuffer[i] & 0xFF00) >> 8);
bOutputBuffer[j++] = (byte) (outputBuffer[i] & 0x00FF);
}
// write to the output
sourceDataLine.write(bOutputBuffer, 0, bOutputBuffer.length);
}
// release all audio resources
targetDataLine.drain();
targetDataLine.close();
sourceDataLine.drain();
sourceDataLine.close();
}
}
}